[SIG 프로젝트] 2. Selenium과 BS4를 사용해보자(데이터 수집)

조우성·2021년 7월 13일
0

데이터 수집을 위해 웹 크롤링이 필요했다.
그래서 파이썬 패키지인 Selenium과 Beautiful Soup 4를 사용해서 데이터를 수집해보았다.
이 글에서는 Selenium과 BS4의 기초적인 사용방법에 대해 알아보도록 한다.

설치 방법

1. pip 설치

pip install Selenium
pip install bs4

import는 아래와 같이 하면 된다.

import selenium
from selenium import webdriver
from selenium.webdriver import ActionChains
 
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
 
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import Select
from selenium.webdriver.support.ui import WebDriverWait
from bs4 import BeautifulSoup

2. 브라우저 엔진 다운로드

구글 크롬 기준으로 설명하겠다.
먼저 크롬 설치가 필요하다. 크롬 설치 후에 버전을 확인하자(중요).
설치를 완료 했다면 링크에 들어가서 설치한 버전과 맞는 엔진을 다운로드 한다.

다운로드 했다면 Selenium을 import한 파일이 존재하는 디렉토리에 엔진을 이동한다.

  • [python-code-directory]
    ┣ use-selenium.py
    └ chrome.exe

와 같은 구조로 저장되어 있으면 된다.

Selenium과 Beautiful soup 4를 동시에 사용하는 이유

Selenium의 경우 동적인 페이지를 불러올 때 유용하다. bs4의 경우 자바스크립트가 실행되어서 페이지를 채우는 경우나, 서버가 jsp로 작성된 경우에 jsp안에 jsp를 include하는 경우를 보통 동적 페이지라고 한다.
bs4는 동적 페이지를 읽어오지 못하고 정적 페이지만 받아올 수 있다. 한마디로 동적 페이지가 존재할 경우 온전한 html를 받아오지 못한다는 것이다.
하지만 Selenium의 경우 실제 브라우저 엔진을 활용하기 때문에 동적으로 실행되는 모든 페이지를 받아올 수 있다. 어떻게? 페이지가 모두 로딩될 때 까지 기다렸다가 html을 받아오면 된다!

Selenium은 실제로 브라우저를 메모리에 올려 실행시키고 브라우저를 통해 데이터를 받아온다는 점 때문에 bs4에 비해 느리다. 그에 반해 bs4는 빠르다는 장점을 가진다.
그래서 Selenium으로는 전체 HTML만 받아오게끔 하고 실제 파싱은 bs4가 맡게하면 속도향상이 있다고 한다.

Selenium이 페이지가 모두 로딩될 때 까지 기다리게 하는 방법 3가지

앞서 설명한 대로 Selenium은 동적인 페이지가 모두 로딩될 때 까지 기다릴 수 있다는 장점이 존재한다.
그렇다면 사용자가 지정하지 않아도 알아서 자동으로 기다릴까?
필자는 그럴 줄 알고 사용했다가 그렇지 않다는 것을 깨닫고 방법을 찾아내었다.

1. 단순 시간 대기

from time import sleep
 
URL = 'http://encykorea.aks.ac.kr/Contents/CategoryNavi?category=contenttype&keyword=%EC%9D%B8%EB%AC%BC&ridx=0&tot=18507' # 원하는 페이지 지정
 
driver = webdriver.Chrome('chromedriver.exe') # 원하는 브라우저 엔진 선택(파일 경로)
driver.get(URL) # 해당 URL로 브라우저 창을 실행
sleep(3)

브라우저 엔진이 URL을 접속하고 단순히 3초를 대기한다.
이 방법은 모든 페이지마다 항상 3초를 대기하므로 비효율적이다.

2. 암묵적 대기(Implicitly Wait)

URL = 'http://encykorea.aks.ac.kr/Contents/CategoryNavi?category=contenttype&keyword=%EC%9D%B8%EB%AC%BC&ridx=0&tot=18507' # 원하는 페이지 지정
 
driver = webdriver.Chrome('chromedriver.exe') # 원하는 브라우저 엔진 선택(파일 경로)
driver.get(URL) # 해당 URL로 브라우저 창을 실행
driver.implicitly_wait(time_to_wait=1000) # 암시적으로 시간을 지정

단위는 second이다. 브라우저 엔진이 접속한 URL의 페이지가 모두 로딩될 때 까지 지정한 시간만큼 대기한다.
만약 지정한 시간 전에 모두 로딩이 끝나면 바로 다음 코드를 진행하게 된다.

3. 명시적 대기(Explicitly Wait)

from time import sleep
 
URL = 'http://encykorea.aks.ac.kr/Contents/CategoryNavi?category=contenttype&keyword=%EC%9D%B8%EB%AC%BC&ridx=0&tot=18507' # 원하는 페이지 지정
 
driver = webdriver.Chrome('chromedriver.exe') # 원하는 브라우저 엔진 선택(파일 경로)
driver.get(URL) # 해당 URL로 브라우저 창을 실행
WebDriverWait(driver, 1000).until(EC.presence_of_element_located((By.CSS_SELECTOR, "#index"))) #index라는 id를 가진 요소가 로딩될 때 까지 기다림

지정한 요소가 로딩이 완료 될 때 까지 지정한 시간만큼 대기한다. 이 코드 또한 지정한 시간 이전에 요소가 로딩이 완료되면 다음 코드를 진행한다.

로딩이 완료된 후 전체 html 가져오기

앞선 코드로 모든 로딩이 끝나게 되면 html을 가져올 수 있는 상태가 된다.

html = driver.page_source #URL에 해당하는 페이지의 HTML를 가져옴

이 코드를 활용해 html을 텍스트 형태로 변수에 담는다. 이후 이 html는 bs4를 통해 파싱하게 된다.

BS4를 이용한 파싱

soup = BeautifulSoup(html, 'html.parser')

앞서 Selenium에서 받아온 HTML을 BS에다 넣어준다.

파싱하는 함수의 경우 대표적으로 두 가지의 방법이 있다.

1. find(), find_all() 함수 사용

html요소의 태그네임, 속성값을 이용해 파싱한다.

body = soup.find('body')  # body 태그 요소
      
spans = soup.find_all('span')  # 모든 span 요소를 리스트[]형태로 반환
 
test_ids = soup.find_all(id = "test_id")  #id 속성이 "test_id"인 모든 요소를 리스트형태로 반환

2. select(), select_all()

select함수의 경우 css selector에 대한 이해가 있어야 한다.

title = soup.select('#ko_title') # id가 ko_title인 모든 요소를 리스트형태로 반환
 
cats = soup.select_one('#cm_meta > div > dl > dt') # "#cm_meta > div > dl > dt" 인 요소를 하나만 반환

파싱한 요소에서 텍스트만 추출하고 싶을 때

get_text()함수를 사용하게 되면 텍스트만 추출해주게 된다.

cats = soup.select_one('#cm_meta > div > dl > dt').get_text()

0개의 댓글