본문 바로가기
내마음대로만들어보자/참고자료

[참고자료] 크롤링

by 소농민! 2021. 6. 11.
728x90

파이썬으로 웹 크롤링할때 쓸만한 테크닉을 정리했습니다.

1. Beautiful Soup

기본 구조는 아래와 같습니다.

requests 는 웹 페이지를 가져오기 위한 라이브러리
bs4(BeautifulSoup) 는 웹 페이지 파싱을 위한 라이브러리입니다.
soup 에 HTML을 파싱한 정보가 들어갑니다.

import requests from bs4 import BeautifulSoup res = requests.get('가져올 웹 페이지 주소') soup = BeautifulSoup(res.content,'html.parser')

☑️ 테스트 코드 작성하는 방법

실제 웹페이지에서 가져오기 전 테스트 코드를 작성하고 싶을때 아래와 같이 사용 합니다.

from bs4 import BeautifulSoup # html 구조 만들기 html=""" <p>요소1</p><p>요소2</p><p>요소3</p> """ # html 파싱 soup=BeautifulSoup(html,'html.parser') # 메소드로 원하는 부분 추출 p = soup.find('p') print(p) print(p.next_sibling) print(p.next_sibling.next_sibling) 실행결과 <p>요소1</p> <p>요소2</p> <p>요소3</p>

1.1 find

태그로 요소를 선택합니다.

<h1>제목입니다</h1> data = soup.find('h1')

아이디로 요소를 선택합니다.

<div id="title">제목입니다</div> data = soup.find(id='title')

태그와 클래스 이름을 지정해 선택합니다.

<p class="title">제목입니다</p> data = soup.find('p', class_='title')

태그 안 속성으로 선택합니다.

<p data = soup.find('p', attrs = {'align': 'center'}) 📌 속성 추출attr

태그 안에 있는 속성들을 전부 key : value 형태의 딕셔너리로 추출합니다.

p class="tag" href="http://example.com" style="text-align:center">요소1</p> data = soup.find('p').attrs 실행결과 {'class': ['tag'], 'href': 'http://example.com', 'style': 'text-align:center'} href

태그에서 링크를 추출할때 사용합니다.
이미지에서 링크 추출할 때 많이 씁니다.

<p href="http://example.com">요소</p> data = soup.find('p')['href'] 실행결과 http://example.com 속성 검증

try except 를 사용하는 대신 아래 속성 검증을 통해
오류를 방지할 수 있습니다.

if 'href' in a.attrs : ... 추출하는 코드 ...

1.2 css select

select_one 은 요소를 반환합니다.
select 는 요소의 리스트를 반환합니다.

soup.select_one('선택자') soup.select('선택자') 선택자

태그명으로 가져옵니다.

'p'

아이디로 가져옵니다.

'#main'

자식들 중 해당 태그를 가져옵니다.

`ul.items li`

바로 아래에 있는 자식 태그를 가져옵니다

`ul.items > li`

href의 값이 특정 문자열로 시작하는 태그를 가져옵니다.

`a[href^='http://www.testsite.co.kr/']`

href의 값이 특정 문자열로 끝나는 태그를 가져옵니다. 입니다.

'[href$='contact/']'

href의 값이 특정 문자열을 포함하는 태그를 가져옵니다.

a[href*='news']

class의 값이 특정 문자(열)와 같지 않은 태그를 가져옵니다

`a[class!='문자열']`📌 속성 추출

문서 전체에서 a 태그 중 속성 값 href를 갖은 태그를 찾습니다.

soup.select('a[href]')

id가 bodyContent인 태그의 자손 태그 중에서 a 태그 속성 값 href를 갖은 태그를 찾습니다.

soup.select('#bodyContent a[href]')

1.3 텍스트 추출

아래와 같이 태그 내부의 글자를 추출할 수 있습니다.

<div>글자</div> data = soup.find('div') # 글자 추출 data.get_text() data.string

2. Selenium

셀레니움을 이용하면 브라우저를 제어해서 크롤리을 할 수 있습니다.

스크롤, 클릭, 키보드입력 등이 가능하기 때문에 인스타그램, 페이스북 등 로그인이 필요한 사이트들의 소스를 가져올 수 있습니다.

pip install selenium

기본 코드 구조입니다.

from selenium import webdriver from selenium.webdriver.common.keys import Keys import time chromedriver = '설치한 chromedriver 경로' driver = webdriver.Chrome(chromedriver) driver.get('가져올 웹 페이지 주소') # 검증 코드입니다. assert '웹 페이지 타이틀' in driver.title ... driver.quit()

2.1 페이지 로드 대기

브라우저에서 해당 웹 페이지의 요소들을 로드하는 데 시간이 걸리기 때문에 대기가 필요합니다.

driver.implicitly_wait(time_to_wait=3)

찾으려는 element가 로드될 때까지 지정한 시간만큼 대기할 수 있도록 설정합니다.
이는 한 webdriver에 영구적으로 작용합니다.

time.sleep(3)

time.sleep(secs) 함수를 사용하여 무조건 몇 초간 대기하는 방법도 있습니다.

from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC driver = webdriver.Chrome('chromedriver') driver.get(url='https://www.google.com/') try: element = WebDriverWait(driver, 5).until( EC.presence_of_element_located((By.CLASS_NAME , 'gLFyf')) ) finally: driver.quit()

위의 코드는 웹페이지에서 class가 gLFyf인 어떤 element를 찾을 수 있는지를 최대 5초 동안 매 0.5초마다 시도합니다.
expected_conditions(EC)는 element를 찾았다면 True를, 아니라면 False를 반환합니다.

2.2 키보드 입력

input 태그를 선택하고 키보드 입력을 명령으로 텍스트를 입력할 수 있습니다.

clear() : 기존에 입력값이 있었다면 지웁니다.
send_keys('입력할 값') : 키보드 입력값을 input 태그에 전달합니다.
Keys.RETURN : 엔터키를 누릅니다.
dir(Keys) 로 키에 대응되는 이름을 찾습니다.

elem = driver.find_element_by_name('username') elem.clear() elem.send_keys('Ywoosang') elem.send_keys(Keys.RETURN)

2.3 클릭하기(click)

find_element 함수로 요소를 선택한 다음 click() 함수를 호출합니다.

tag = driver.find_element_by_tag_name('button') tag.click()

2.4 스크롤

scrollTo 이용driver.execute_script("window.scrollTo(0, Y)")

페이지 끝까지 가려면 Y 값을 아래와 같이 설정합니다. (Y  height 을 의미합니다.)

document.body.scrollHeight

동적 웹페이지에서 스크롤 다운하면서 데이터를 조회할 경우입니다.

SCROLL_PAUSE_SEC = 1 # 스크롤 높이 가져옴 last_height = driver.execute_script("return document.body.scrollHeight") while True: # 끝까지 스크롤 다운 driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") # 1초 대기 time.sleep(SCROLL_PAUSE_SEC) # 스크롤 다운 후 스크롤 높이 다시 가져옴 new_height = driver.execute_script("return document.body.scrollHeight") if new_height == last_height: break #time.sleep(1) #if new_height == last_height: # break last_height = new_height

간혹 로딩이 걸려 중간에 코드가 멈춘다면 SCROLL_PAUSE_SEC 를 증가시켜봅니다.

ActionChains 의 move_to_element 이용

ActionChains 에 대해 아래에서 자세히 다룹니다.
특정한 element 를 알고 있을 때 그 위치까지 스크롤 할 수 있습니다.

# ActionChains 를 사용하기 위해서. from selenium.webdriver import ActionChains # id가 something 인 element 를 찾음 some_tag = driver.find_element_by_id('something') # somthing element 까지 스크롤 action = ActionChains(driver) action.move_to_element(some_tag).perform()특정 시간동안 계속해서 scroll down 하기

아래와 같이 datetime 을 이용해서 정해진 초 동안 1초에 한번씩 스크롤 다운합니다.
동적 웹페이지의 데이터를 다 가져올 수 없을 때 시간을 정하고 그 시간동안 크롤링을 실행합니다.

import datetime def doScrollDown(whileSeconds): start = datetime.datetime.now() end = start + datetime.timedelta(seconds=whileSeconds) while True: driver.execute_script('window.scrollTo(0, document.body.scrollHeight);') time.sleep(1) if datetime.datetime.now() > end: break

2.5 JavaScript 코드 실행

driver.execute_script() 함수로 자바스크립트 코드를 실행할 수 있습니다.

아래는 Name이 search_box인 요소의 값을 query의 값으로 변경하는 코드입니다.

driver.execute_script("document.getElementsByName('id')[0].value=\'"+query+"\'")

2.6 태그 가져오기

element 는 최초로 발견한 태그만 가져옵니다.
elements 는 조건에 해당하는 모든 태그를 리스트로 가져옵니다.

아래와 같이 다양한 검색 조건이 존재합니다.
필요에 맞는 것을 가져다 쓰면 됩니다.

find_element_by_tag_name(), find_element_by_id() find_element_by_css_selector() driver.find_element_by_xpath() ind_element_by_name()

아래처럼 사용합니다.

diver.find_element_by_css_selector('div > a[href="http://example.com"]') driver.find_element_by_css_selector('html head title') driver.find_element_by_css_selector('h3.tit_view') driver.find_element_by_css_selector("div#harmonyContainer")

2.7 ActionChains 사용방법

마우스 클릭, Drag & Drop, 키보드 입력 등을 연속적으로 수행할 수 있습니다.

메소드수행 동작

click(on_element=None) 인자로 주어진 요소를 왼쪽 클릭한다.
double_click(on_element=None) 인자로 주어진 요소를 왼쪽 더블클릭한다.
key_down(value, element=None) value 로 주어진 키를 누르고 떼지 않는다.
key_up(value, element=None) value로 주어진 키를 뗀다.
send_keys(*keys_to_send) 키보드 입력을 현재 focused된 요소에 보낸다.
send_keys_to_element(element, *keys_to_send) 키보드 입력을 주어진 요소에 보낸다.
perform() 이미 쌓여 있는(stored) 모든 행동을 수행한다(chaining).

예를들어 Ctrl + C를 누르고 싶다면 아래와 같이 입력합니다.

ActionChains(driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()

참고
on_element인자를 받는 함수는, 해당 인자가 주어지지 않으면 현재 마우스 위치를 기준으로 합니다.
element 인자를 받는 함수는, 해당 인자가 주어지지 않으면 현재 선택이 되어 있는 요소를 기준으로 합니다.
key_down, key_up 함수는 Ctrl 등의 키를 누를 때 사용합니다.

링크를 참조합니다.

2.8 뒤로가기, 앞으로 가기

뒤로가기(back)와 앞으로 가기(forward) 입니다.

driver.forward() driver.back()

2.9 텍스트 추출

태그에 .text 를 붙여 문자열로 가져옵니다.

data.text

2.10 브라우저 크기 설정

브라우저 크기를 원하는대로 조정합니다.

driver.set_window_size(1920, 1080)

브라우저 크기를 최대, 최소로 조정할 수 있습니다.

driver.minimize_window() driver.maximize_window()

2.11 XPath

XPath 문법입니다.

nodename : nodename을 name으로 갖는 모든 요소 선택
/ : root 요소에서 선택
// :현재 요소의 자손 요소를 선택
. :현재 요소를 선택
.. :현재 요소의 부모 요소를 선택
@ :속성(attibutes)를 선택
* : 모든 요소에 매치됨
@* : 모든 속성 요소에 매치됨
node() : 모든 종류의 모든 요소에 매치됨
| : OR 조건의 기능

아래는 예시입니다.

/div/p[0] : root > div > p 요소 중 첫 번째 p 요소를 선택합니다.

/bookstore/book[price>35.00] : root > bookstore > book 요소 중 price 속성이 35.00 초과인 요소들을 선택합니다.

/div/p[position()<3] : root > div > p 요소 중 첫 두 p 요소를 선택합니다.

//*[@id="tsf"]/div[2]/ : id가 tsf인 모든 요소의 자식 div 요소 중 3번째 요소를 선택합니다.

2.12 Headless Chrome

셀레니움을 이용하면 크롬창이 열립니다.
이를 보고싶지 않다면 Headless Chrome 을 이용합니다.

from selenium import webdriver options = webdriver.ChromeOptions() options.add_argument('headless') options.add_argument('window-size=1920x1080') options.add_argument("disable-gpu") agent="User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131" options.add_argument(agent) options.add_argument("lang=ko_KR") chromedriver = '설치한 chromedriver 경로' driver = webdriver.Chrome(chromedriver, chrome_options=options) driver.get('가져올 웹 페이지 주소') ... driver.quit()