데이터 핸들링/데이터 수집

Selenium을 활용한 크롤링

jeongpil 2021. 10. 20. 00:05

공부를 한 후에도 계속해서 사용하기 편하도록 동적 크롤링에 해당하는 Selenium의 함수들을 코드와 함께 기록해 놓으려 합니다!

 

 

•  Selenium 라이브러리 로드 및 드라이버 생성

 

# 기본 selenium 라이브러리 로드
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
# driver 생성
chromedriver = 'chromedriver' #chromedriver의 위치
driver = webdriver.Chrome(chromedriver)

 

 

※ 크롤링 동작이 화면에 보이지 않고 크롤링이 되는 Headless Chrome

 

위의 driver 생성 부분 대신 사용하게 되면 크롤링이 돌아가는 화면이 보이지 않고 크롤링됩니다.

 

#Headless Chrome (돌아가는 화면 없음)
chromedriver = 'chromedriver'
headless_options=webdriver.ChromeOptions()
headless_options.add_argument('headless')
driver=webdriver.Chrome(chromedriver,options=headless_options)

 

 

 

 데이터 가져오기

 

데이터를 가져오는 방법은 크게 5가지가 있습니다.

 

class_name, name, tag_name, id, css_selector을 통해 데이터를 가져올 수 있습니다.

 

find_element를 이용하면 최초의 것만 가져오고, find_elements로 복수를 이용하면 전체를 가져옵니다.

 

아래 코드에서 tag_name을 통해 가져오는 부분은 find_elements_by_tag_name을 사용해서 해당 태그를 가진 데이터 전체를 가져와서 titles에 저장하는 것입니다.

 

#크롤링할 사이트 호출
driver.get('크롤링할 url')

#검색 결과(데이터) 가져오기

#class_name을 통해 가져오기 (최초)
title=driver.find_element_by_class_name('class_name 입력')
print(title.text)

#name을 통해 가져오기
title=driver.find_element_by_name('name 입력')

#tag_name을 통해 가져오기 (전체)
titles=driver.find_elements_by_tag_name('tag 입력 (ex: h3)')
for title in titles:
    print(title.text)

#id를 통해 가져오기
writings=driver.find_element_by_id('id 입력')
print(writings.text)

#css seletor를 통해 가져오기 (div 태그의 role 속성이 navigation인 것 가져오기)
title=driver.find_element_by_css_selector("div[role='navigation']")
print(title.text)

#드라이버 끝내기
driver.quit()

 

 

 

 사이트의 검색창에 검색어 입력하기

 

#name이 q인 최초 데이터 가져오기 (검색칸의 name)
element=driver.find_element_by_name("q")

#검색창의 input 텍스트 초기화
element.clear()

#키(검색어) 이벤트 전송
element.send_keys("검색어") #원하는 검색어 입력

#엔터 입력
element.send_keys(Keys.RETURN)

 

 

 

  웹페이지의 동적인 부분 크롤링

 

위의 코드까지는 정적인 부분을 크롤링하는 거였다면, 이번에는 댓글 부분처럼 동적인 부분을 크롤링하는 방법을 코드를 통해 알아보겠습니다.

 

아래의 코드는 Daum의 기사인 https://news.v.daum.net/v/20211020104453002의 댓글을 크롤링한 코드입니다.

 

#라이브러리 로드
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
import time

#드라이버 생성
chromedriver='chromedriver'
driver = webdriver.Chrome(chromedriver)
driver.get('https://news.v.daum.net/v/20211020104453002')

loop, count = True, 0

#추천 댓글이 아닌 전체 댓글 확인을 위해 최신순 버튼 클릭
driver.find_element_by_css_selector('#alex-area > div > div > div > div.cmt_box > ul.list_category > li:nth-child(3) > button').click()
time.sleep(1)

#모든 댓글 크롤링을 위해 safe봇 설정 제거
# safe_button 클릭
driver.find_element_by_css_selector('#alex-area > div > div > div > div.cmt_box > div.cmt_keeper_setting > button').click()
time.sleep(1)
# on_off_button 클릭
driver.find_element_by_css_selector('#alex-area > div > div > div:nth-child(2) > div.cmtarray_layer > div > div.layer_body > dl > dd > button').click()
time.sleep(1)
# x_button 클릭
driver.find_element_by_css_selector('#alex-area > div > div > div:nth-child(2) > div.cmtarray_layer > div > a').click()

#더보기 누르기
while loop and count <10:
    try:
        element=WebDriverWait(driver, 5).until( # 더보기 버튼이 존재한다면 실행
            EC.presence_of_element_located((By.CSS_SELECTOR,'#alex-area > div > div > div > div.cmt_box > div.alex_more > button'))
        )
        more_button=driver.find_element_by_css_selector('#alex-area > div > div > div > div.cmt_box > div.alex_more > button').click()
        count+=1
        time.sleep(1)
    except TimeoutException: # 더보기 버튼이 없다면 while문 종료
        loop=False

#크롤링 시작
comment_box=driver.find_element_by_css_selector('#alex-area > div > div > div > div.cmt_box > ul.list_comment')
comment_list=comment_box.find_elements_by_tag_name('li')
comments=[]
for comment_item in comment_list:
    try:
    	# 리스트에 댓글 저장
        comments.append(comment_item.find_element_by_css_selector('div  p').text) 
    except:
        continue
    
print(comments)
driver.quit()

 

By 라이브러리는 특정 태그 존재 여부를 확인하는 기능을 합니다.

 

예를 들면, (By.CSS_SELECTOR, 'css_selector')는 해당 css_selector가 존재하는지 확인하는 기능을 합니다.

 

By.id, By.CLASS_NAME, By.NAME 등 다른 태그들에도 사용 가능합니다. 

 

주의할 점은 EC.presence_of_element_located() 안에 튜플 형태로 입력해줘야 한다는 점이 있습니다.

 

 

버튼을 누를 때 click() 메소드를 사용하는데 click()외에도 마우스와 키보드를 동작하는 메소드들은 다음과 같이 존재합니다.

 

 

•   element 클릭: element.click()

 

•   element 더블 클릭: element.double_click()

 

•   element 키보드 입력 전송: element.send_keys()

 

•   element로 마우스 이동: element.move_to_element()

 

 

여러 마우스와 키보드 동작을 한 번에 묶어서 실행하려면 ActionChains() 메소드를 이용하면 됩니다.

 

ActionChains() 메소드를 통해 행동 여러 개를 체인으로 묶어서 저장하고 원하는 만큼 실행합니다.

 

perform() 메소드 실행 시에는 전체 행동을 실행합니다.

 

예를 들면, actions=ActionChains(driver).click().send_keys().click() 처럼 ActionChains를 통해 여러 행동을 묶고

 

actions.perform() 을 실행하면 묶은 행동 전체를 실행할 수 있습니다.

 

 

※ 댓글에 글이 없고 이모티콘만 있을 경우, comment_item 하위에 div p 태그가 존재하지 않아 에러 발생

 

try except 구문을 활용해서 해결.

comments 리스트에 댓글을 추가하려 try하고 div p 태그가 없어 에러가 날 경우에는 continue를 시켜 반복문 계속 진행.

 

 

 

  XPATH 문법

 

XPATH란 XML문서의 특정 요소나 속성에 접근하기 위한 경로를 지정하는 언어입니다.

 

XPATH는 BeautifulSoup에서는 지원하지 않고 Selenium에서만 사용할 수 있는 방식입니다.

 

최근에는 JSON 파일을 많이 사용하기 때문에 XPATH를 많이 사용하지 않지만 참고로 알아두면 좋을 것 같습니다.

 

XPATH의 문법은 다음과 같습니다.

 

 

•   / : 절대경로를 나타냄


•   // : 문서내에서 검색

 

•   //@href : href 속성이 있는 모든 태그 선택


•   //a[@href='http://daum.net'] : a 태그의 href 속성에
http://daum.net 속성값을 가진 모든 태그 선택 


•   (//a)[3] : 문서의 세 번째 링크 선택


•   (//table)[last()] : 문서의 마지막 테이블 선택


•   (//a)[position() < 3] : 문서의 처음 두 링크 선택


•   //table/tr/* 모든 테이블에서 모든 자식 tr 태그 선택


•   //div[@*] 속성이 하나라도 있는 div 태그 선택

 

 

 

Selenium을 활용해서 여러 방식으로 데이터를 수집하는 방식에 대해 알아보고 이를 직접 코드를 통해 실행해봤습니다.

 

크롤링은 항상 공부하고 싶었던 부분이었는데 공부하고 직접 크롤링을 해보니 너무 재밌네요 : )

 

BeautifulSoup이나 Scrapy를 활용해서 크롤링하는 방법에 대해 공부하는 등 크롤링에 대해서 더욱 자세히 공부하려 합니다.