본문 바로가기

크롤링, 스크래핑

selenium과 bs4를 이용한 동적 웹사이트 크롤링

사용목적

 

우리가 개발중인 웹 페이지에 선박명을 입력받으면 선박의 위치를 지도에 나타내는 기능을 넣으려고 한다. 하지만 선박의 이름을 입력받아 선박의 위치를 알려주는 API서비스는 찾지 못했다.

marinetraffic API Documentation

이에 따라 우리가 사용하기로 정한 marinetraffic의 API는 mmsi나 imo 혹은 marinetraffic에서 정의한 shipid만을 parameter로 받아 선박의 위치 정보를 제공해 주기 때문에 선박의 이름을 입력하면 그것을 mmsi 혹은 imo로 변환 시켜 줄 방법이 필요했다.

 

 

 

 

 

 

 

이에 대한 해결방법으로 생각한 것이 포트미스의 호출부로 조회 서비스를 이용하는 거였다.

호출부호를 조회하는 것으로 사용되는 이 서비스는 선박명을 검색하면  IMO번호 또한 조회가 가능하여, 이 웹페이지에서 IMO번호를 가져와 marinetraffic API에 사용하는 방식으로 설계를 했다.

 

사용 방법

selenium과  beautifulsoup4 두가지 라이브러리를 사용해 크롤링을 진행했는데 selenium은 주로 웹 동작을 수행하는데 사용을 하고 HTML, XML을 추출하여 수집할 때는 beautifulsoup4를 사용한다.

beautifulsoup4는 그냥 pip bs4로 설치를 하면 되지만 selenium은 그것만으로 부족하고 추가적으로 해줘야 하는 작업이 있다.

자신의 컴퓨터의 운영 체제와 크롬 버전에 맞는 크롬 드라이버를 설치해야 하는데 이 과정은 구글에 많은 정보가 있기 때문에 생략한다.

이 크롬 드라이버가 바로 크롬 브라우저를 실제로 제어하는 역할을 해준다.

 

사용 코드

 

from bs4 import BeautifulSoup
from selenium import webdriver
from time import sleep


def get_IMO(search_key):
    driver = webdriver.Chrome('./chromedriver') #크롬드라이버 사용 지정

    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) #크롤링을 진행할 url설정

    search_key = search_key.lower() #함수에 들어온 값을 소문자로 변환


    # search_box = driver.find_element_by_css_selector("input#mf_ipt1") #css_Selecter로 요소 지정
    search_box = driver.find_element_by_xpath('//*[@id="mf_ipt1"]') # xpath로 요소 지정
    search_box.send_keys(search_key) # search_box에 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(1) #explicitly wait 1초 블로그 글 참조!

    req = driver.page_source #지금 브라우저 화면의 페이지 소스를 가져온다.

    soup = BeautifulSoup(req, 'html.parser')  # 가져온 정보를 beautifulsoup로 파싱

    ship_name = None
    IMO_num =None
	
    #html에서 원하는 데이터를 뽑아내는 과정
    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

    # driver.quit()
    if ship_name is not None:
        return {'선박명': ship_name, 'IMO': IMO_num}
    else:
        return "존재하지 않는 선박입니다. 선박명을 다시 확인해주세요."


search_key = input() #입력을 받아준다.
print(get_IMO(search_key)) #받은 입력을 함수에 넣어 크롤링을 진행시킨다.

위의 코드가 전체 코드이다. 굉장히 짧고 간단한 코드이지만 한가지 짚고 넘어갈 부분이 있어 기록하였다. selenum과  bs4의 사용 방법은 구글에 검색하면 굉장히 쉽게 접할 수 있다.

    # driver.implicitly_wait(10)
    sleep(1)

    req = driver.page_source

내가 애를 먹은 부분의 이부분인데 코드를 처음 작성했을 때는 주석 처리된 부분과 같이 driver.implicitly_wait(10)을 사용했었다. 처음에 임플리시틀리 웨이트를 사용한 이유는 이 명령어가 다음 웹 페이지가 넘어올때까지 최대 괄호 안에 숫자만큼 기다리고 만약 그전에 웹페이지가 넘어오면 바로 다음 명령어를 실행하라는 뜻이라 이 명령어를 만능이라고 생각했었기 때문이다. 하지만 동적 웹사이트에 웹 페이지를 이미 받아오고 렌더링 하는 경우에 웹페이지는 이미 받아졌기 때문에 implicitly_wait명령어는 바로 다음 명령어로 넘어가고 나서야 렌더링이 진행되기 때문에 제대로 정보를 가져오지 못한다. 이러한 문제 때문에 sleep를 무조건 1초를 두어 렌더링이 될 시간을 준 후 웹페이지를 스크래핑해야한다.

 

이에 대한 설명이 잘 되어있는 블로그의 링크를 올리며 이 게시물을 마치고 다음 게시물에는 이 코드를 어떻게 개발중인 웹사이트에 활용했는지에 대한 글을 올릴 것이다.

https://pythondocs.net/selenium/%EC%85%80%EB%A0%88%EB%8B%88%EC%9B%80-wait-%EA%B0%9C%EB%85%90-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-implicitly-wait-vs-explicitly-wait/

 

셀레니움 wait 개념 이해하기 (implicitly wait VS explicitly wait) - 뻥뚫리는 파이썬 코드 모음

이 문서는 셀레니움 wait 에 관한 implicitly wait 와 explicitly wait 에 대해서 다루고 있습니다. 셀레니움 사용법 전반에 대해서 알아보시려면 셀레니움 크롤러 기본 사용법을 확인하시기 바랍니다. 목

pythondocs.net

 

이 코드를 개발중인 웹사이트에 활용한 내용은 다음 게시물에 적혀있다.

https://dong5854.tistory.com/17

 

웹사이트 크롤링 AWS EC2 Django 프로젝트에 적용

결과물 *본 게시물은 아래의 게시물에서 이어지는 게시물이다. https://dong5854.tistory.com/16 selenium과 bs4를 이용한 동적 웹사이트 크롤링 사용목적 우리가 개발중인 웹 페이지에 선박명을 입력받으면

dong5854.tistory.com