결과물
*본 게시물은 아래의 게시물에서 이어지는 게시물이다.
https://dong5854.tistory.com/16
AWS EC2 인스턴스에 있는 Django 프로젝트에 위의 게시물에서 작성한 크롤링 프로그램을 합치는 작업을 했다.
작업을 성공적으로 마친 후에는 다음과 같은 결과가 나타났다.
크롤링과 기존시스템을 통해 성공적으로 작동을 한 경우는 첫 번째 사진처럼 결과가 나타나고 선박은 존재하지만 marinetraffic API에서 선박에 대한 위치정보를 제공하지 않는 경우는 두번째 사진과 같이 경고창이 정보를 찾을 수 없다는 경고창이 나타나고, 선박 자체가 존재하지 않는 선박인 경우에는 해당하는 선박을 찾을 수 없다는 경고창이 나타난다.
준비과정
윈도우 환경에서 로컬로 돌리던 크롤링 파일을 돌리기 위해 AWS ec2 우분투 인스턴스에도 크롬을 설치하고 그 버전에 맞는 크롬드라이버를 설치해야 하는데 내가 설치를 하면서 본 블로그의 링크를 남긴다.
https://dvpzeekke.tistory.com/1
코드 살펴보기
프로젝트의 코드들 중에서 이번 작업과 관련이 있는 부분들을 살펴보자
//ship_position_page.html의 script 태그
$(document).on("click", "#search_button", function () { //html의 search_button아이디가 부여된 요소를 클릭시 실행
var search_key = $('#shipCallCode').val()
$.ajax({
type: 'GET',
url: "{% url 'single_Vessel_position' %}",
dataType : "json", //서버측에서 전송받은 데이터의 형식
data: { //서버측에 보내 줄 데이터
'search_key': search_key
},
error: function () {
// 선박명이 없는경우 response에 실패
alert("해당하는 선박을 찾을 수 없습니다.")
},
success: function (data) { //서버에서 성공적으로 데이터를 받아옴
$('#card_position').empty() //기존 card_position ID가 부여된 부분을 비운다.
data_json = data
let temp_mmsi = data_json['mmsi'] //서버에서 받아온 데이터를 넣어주는 부분
let temp_latitude = data_json['latitude']
let temp_longitude = data_json['longitude']
temp_html = ` <div class="card text-dark bg-light mb-3"> //추가할 html 백틱(`)을 사용함에 유의
<div class="card-header">${search_key}</div>
<div class="card-body">
<h5 class="card-title"></h5>
<p class="card-text">MMSI : ${temp_mmsi}<br>경도 : ${temp_latitude}<br>위도: ${temp_longitude}</p>
</div>
</div>
if (data_json['result'] == 'fail_data'){
alert("정보를 제공하지 않는 선박입니다.")
}
markerMaking() //지도 마커표시에 관여하는 함수(블로그에서는 보여주지 않는 함수)
$('#card_position').append(temp_html) //card_position ID를 가지고 있는 요소에 만들어둔 html을 append
}
})
})
ajax를 통해 검색창에 들어가 있는 값(선박명)을 서버측으로 전해준다. 서버에서 이 데이터(선박명)을 이용하여 IMO 번호를 알아낸 후 이를 다시 클라이언트 측으로 보내준 후 이를 이용해 html을 가공하는 방식이다.
#views.py
from django.shortcuts import render
from typing import Protocol
import requests
import json
from django.http import HttpResponse
from django.http import JsonResponse
import sys, os
sys.path.append(os.path.dirname(os.path.abspath(os.path.dirname(__file__)))) //해당 파일의 상위 디렉토리에 있는 모듈을 임포트 하기 위해 넣은 부분
from crawling import imo_crawling //상위 디렉토리의 crawling 폴더 안의 imo_crawling.py 임포트
def single_Vessel_position(request):
if request.method == 'GET':
api_key ='1f86e6bc78c4a862fc418932b8649f7d44469e32'
timespan = '20'
Protocol_type = 'jsono'
search_key = request.GET['search_key']
IMO = imo_crawling.get_IMO(search_key) //IMO번호를 받아오는 크롤링을 하는 함수에 선박명을 넣어준다.
pre_single_vessel = 'https://services.marinetraffic.com/api/exportvessel/v:5/'
queryParams = {'service_key': api_key, 'timespan': timespan, 'Protocol_type' : Protocol_type, 'IMO' : IMO}
api_url = pre_single_vessel + queryParams['service_key'] + '/timespan:' + queryParams['timespan'] + '/protocol:' + queryParams['Protocol_type'] + '/imo:' + queryParams['IMO']
print(api_url)
r = requests.get(api_url)
r = r.json()
#정보가 없는 예외처리
if not r:
return JsonResponse({'result' : 'fail_data'})
res_mmsi = r[0]['MMSI']
res_latitude = r[0]['LAT']
res_longitude = r[0]['LON']
res_speed = r[0]['SPEED']
res_heading = r[0]['HEADING']
res_course = r[0]['COURSE']
res_status = r[0]['STATUS']
res_timestamp = r[0]['TIMESTAMP']
res_dsrc = r[0]['DSRC']
context = {
'result' : 'success',
'mmsi' : res_mmsi,
'latitude' : res_latitude,
'longitude' : res_longitude,
'speed' : res_speed,
'heading' : res_heading,
'course' : res_course,
'status' : res_status,
'timestamp' : res_timestamp,
'dsrc' : res_dsrc
}
print(context)
print('mmsi:' + res_mmsi)
print('latutude:' + res_latitude)
print('longitude:' + res_longitude)
print('speed:' + res_speed)
print('heading:' + res_heading)
print('course:' + res_course)
print('status:' + res_status)
print('timestamp:' + res_timestamp)
print('dsrc:' + res_dsrc)
return JsonResponse(context)
else:
return HttpResponse("GET이아님")
html의 ajax를 통해서 보낸 데이터를 이용하여 imo 번호를 구하고 이 imo 번호를 다시 API에 활용해 선박의 위치정보를 받아와 리턴해서 다시 클라이언트 측으로 보내주는 함수이다. 이 코드를 작성하며 조금 시간을 잡아먹은 부분은
#정보가 없는 예외처리
if not r:
return JsonResponse({'result' : 'fail_data'})
이부분인데 정보가 없을 때 이 r이 빈 리스트로 값이 들어와 if r == []: 로 조건문을 주었는데 제대로 if문으로 들어가지 않아 구글링으로 방법을 찾아보니 빈 Sequence(String / Tuple / List)는 False 값을 가지기 때문에 is not r: 로 if문을 처리할 수 있다는 것을 알았다.
#imo_crawling.py
from bs4 import BeautifulSoup
from selenium import webdriver
from time import sleep
def get_IMO(search_key):
options = webdriver.ChromeOptions()
options.add_experimental_option("excludeSwitches", ["enable-logging"])
# 크롬 드라이버 경로 설정, 및 옵션 설정(옵션은 DevToolsActivePort를 찾을 수 없다는 에러 해결을 위해)
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument("--single-process")
chrome_options.add_argument("--disable-dev-shm-usage")
path = '/home/ubuntu/portwebsite/crawling/chromedriver'
driver = webdriver.Chrome(path, options=chrome_options)
#--------------------------------------------------------------------------------------------------------
url = "https://new.portmis.go.kr/portmis/websquare/popup.html?w2xPath=/portmis/w2/sample/popup/pop/UC_PM_CM_002_07_02.xml&menuCd=M0182&popupID=mf_tacMain_contents_M0182_body_popupSearchVsslInnb&idx=idx10_16280424157712233.0042600478396&w2xHome=/portmis/w2/main/&w2xDocumentRoot="
driver.get(url)
search_key = search_key.lower()
# search_box = driver.find_element_by_css_selector("input#mf_ipt1")
search_box = driver.find_element_by_xpath('//*[@id="mf_ipt1"]')
search_box.send_keys(search_key)
driver.implicitly_wait(1)
search_btn = driver.find_element_by_css_selector("div#mf_udcSearch_btnSearch")
search_btn.click()
# driver.implicitly_wait(10)
sleep(0.5)
req = driver.page_source
soup = BeautifulSoup(req, 'html.parser') # 가져온 정보를 beautifulsoup로 파싱
ship_name = None
IMO_num =None
for i in range(0, 9):
try:
ship_name_temp = soup.select_one(f"#mf_grdCallList_cell_{i}_0 > nobr").text
IMO_num_temp = soup.select_one(f"#mf_grdCallList_cell_{i}_3 > nobr").text
if search_key == ship_name_temp.lower():
ship_name = ship_name_temp
IMO_num = IMO_num_temp
except AttributeError:
break
print(ship_name)
driver.quit()
if ship_name is not None:
return IMO_num
else:
return False
IMO 번호를 구하기 위한 크롤링 파일인데 대부분은 전의 게시물에 올린 것과 같은 모습이고, 리턴값과 처음의 설정에서 서 차이가 있다. 또한 크롬 드라이브에서 DevToolsActivePort를 찾을 수 없다는 에러 해결을 위한 설정에 차이가 있다.
options = webdriver.ChromeOptions()
options.add_experimental_option("excludeSwitches", ["enable-logging"])
# 크롬 드라이버 경로 설정, 및 옵션 설정(옵션은 DevToolsActivePort를 찾을 수 없다는 에러 해결을 위해)
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument("--single-process")
chrome_options.add_argument("--disable-dev-shm-usage")
path = '/home/ubuntu/portwebsite/crawling/chromedriver'
driver = webdriver.Chrome(path, options=chrome_options)
'크롤링, 스크래핑' 카테고리의 다른 글
selenium과 bs4를 이용한 동적 웹사이트 크롤링 (0) | 2021.08.04 |
---|