[이론] sellenium을 활용한 크롤링
웹드라이버 구동
from selenium import webdriver
# driver = webdriver.Chrome('./chromedriver.exe') #115 버전이하
driver = webdriver.Chrome() #116 버전이상
selenium모듈에서 webdriver 클래스로 원하는 브라우저에서 열 수 있음
chrome이 아닌 브라우저 사용시 webdriver설치 필요
get() : url로 원하는 주소 접속
url = 'https://play.google.com/store/apps/details?id=com.towneers.www'
driver.get(url)
원하는 요소를 찾기 : 2가지 선택자
1. CSS_SELECTOR
from selenium.webdriver.common.by import By
## CSS_SELECTOR로 가져오기
cs = 'i.google-material-icons.notranslate.VfPpkd-kBDsod.W7A5Qb'
rb = driver.find_element(By.CSS_SELECTOR, cs)
rb.click()
- 태그이름.클래스이름(띄어쓰기를 .으로 치환) 형태로 줄임 표현 가능
- 동일한 css 클래스를 가지고 있는 요소가 중복될 수 있음
(디자인 요소이기 때문)
2. XPATH
원하는 요소 우클릭 : 검사 - 해당 요소 copy - copy Xpath
from selenium.webdriver.common.by import By
xp = '//*[@id="ow14"]/section/header/div/div[2]/button/i'
rb = driver.find_element(By.XPATH, xp)
rb.click()
경로를 문자열로 만들어서 변수에 저장
click() : 해당 요소 클릭
get_attribute :
# get_attribute 활용
xp = '//*[@id="yDmH0d"]/div[5]/div[2]/div/div/div/div/div[2]/div/div[2]/div[4]/div[1]'
text = driver.find_element(By.XPATH, xp).get_attribute('innerText')
text
여러개 요소를 한번에 찾아오기
전체적인 요소를 모두 포함하는 곳 소스 확인해보면
반복되는 요소들이 있음(<div class='RHo1pe'>) → 리뷰 목록에 해당
* 태그이름과 클래스이름이 같은것을 찾기
→ css selector가 같음
드래그로 리뷰가 노출됨→ 드래그를 최대한 많이 해서 노출시켜야 함
드래그가 있는 요소를 찾아야 함
보통 페이지 상부에 있지만, 내부에 있는 경우가 있음
from time import sleep
import random
xp = '//*[@id="yDmH0d"]/div[5]/div[2]/div/div/div/div/div[2]'
review_box = driver.find_element(By.XPATH, xp) # 드래그가 있는 요소 가져오기
for i in range(10):
driver.execute_script('arguments[0].scrollTop = arguments[0].scrollHeight', review_box )
sleep(random.randint(2, 3)) # 2~3초 쉬었다가 반복
★ scroll할때 공식처럼 암기해도 좋음
execute_script() 스크립트를 직접 작성- selenium이 직접 동작하도록
scrollHeight : 스크롤의 끝이 높이만큼 스크롤바를 내리게끔
데이터 노출시킬때 delay를 걸어주어야 함
timemodule안에 sleep이라는 함수 사용
(for time import sleep으로 처음에 불러와주기)
random모듈 사용해서 랜덤으로 2또는 3의 값이 나오게
: 2~3초 사이로 쉬었다가 반복하게끔
(랜덤 안넣고 그냥 3 넣어도 됨)
1. CSS_SELECTOR 활용 : 중복되는 동일한 스타일 사용시 유용
# CSS_SELECTOR활용
texts = driver.find_elements(By.CSS_SELECTOR, 'div.h3YV2d')
texts
find_elements: 한번에 요소들을 찾음. 중복요소들을 리스트로 반환
for text in texts:
print(text.get_attribute('innerText'))
요소들의 목록을 반복문으로 반환
모든리뷰에 대한 텍스트 내용을 출력할 수 있음
2. XPATH 활용 (css _selector가 더 간편하긴 함)
# '//*[@id="yDmH0d"]/div[5]/div[2]/div/div/div/div/div[2]/div/div[2]/div[1]/div[1]'
# '//*[@id="yDmH0d"]/div[5]/div[2]/div/div/div/div/div[2]/div/div[2]/div[2]/div[1]'
# '//*[@id="yDmH0d"]/div[5]/div[2]/div/div/div/div/div[2]/div/div[2]/div[3]/div[1]'
for i in range(1,10):
xp = f'//*[@id="yDmH0d"]/div[5]/div[2]/div/div/div/div/div[2]/div/div[2]/div[{i}]/div[1]'
text = driver.find_element(By.XPATH, xp).get_attribute('innerText')
print(text)
여러개의 xpath를 가져와서 패턴 관찰하면 거의 유사하지만
숫자가 변하는 곳이 있을것임 → 리뷰 순번임을 알 수 있음
문자열 포맷팅을 이용하여 반복 설정해줌
계층구조에서 데이터도 계층적으로 가져와야 함
# CSS_SELECTOR
reviews = driver.find_elements(By.CSS_SELECTOR, 'div.RHo1pe')
print(len(reviews))
css selector로 모든 리뷰들 가져오기
# XPATH
# '//*[@id="yDmH0d"]/div[5]/div[2]/div/div/div/div/div[2]/div/div[2]/div[i]
xp = f'//*[@id="yDmH0d"]/div[5]/div[2]/div/div/div/div/div[2]/div/div[2]/div'
reviews = driver.find_elements(By.XPATH, xp)
print(len(reviews))
가장 마지막 번호가 바뀔때,
대괄호를 지우고 find_elements에 xpath를 넣으면 뒤에 붙을 모든 순번에 대한xpath를 가져올 수 있음
→ 맨마지막 번호만 바뀔때만 가능. 중간에 바뀔때는 반복문 포맷팅
# 반복문을 사용하여 하위요소인 택스트 내용 추출
texts = []
for review in reviews:
text = review.find_element(By.CSS_SELECTOR, 'div.h3YV2d').get_attribute('innerText')
texts.append(text)
texts
리뷰박스를 가져와서 한번 더 find elements로 내부요소 가져오기
review박스안에 있는 css_selector로 가져오기
→ 더 안정적인 크롤링 가능
모든 요소의 데이터 추출 : 함수, 반복문
def get_content(review):
condic = {}
condic['text'] = review.find_element(By.CSS_SELECTOR, 'div.h3YV2d').get_attribute('innerText').strip()
condic['id'] = review.find_element(By.CSS_SELECTOR, 'header.c1bOId').get_attribute('data-review-id')
condic['rat'] = len(review.find_elements(By.CSS_SELECTOR, 'span.Z1Dz7b'))
condic['date'] = review.find_element(By.CSS_SELECTOR, 'span.bp9Aid').get_attribute('innerText').strip()
return condic
[text] text가져오고 딕셔너리안에 만들어서 text키에 할당
[id] id를 리뷰안에서 해당요소 가져와서 id키에 할당
[] 날짜도 해당 리뷰안에서 가져와서 할당
[] 별이 채워져있는 요소의 개수로 별점 할당
...
import pandas as pd
result = []
for content in driver.find_elements(By.CSS_SELECTOR, 'div.RHo1pe'):
try:
dic = get_content(content)
result.append(dic)
except:
print('error')
pass
df = pd.DataFrame(result)
df
for문 : 리뷰boxs
content에 box들이 들어감
dictionary로 리턴(dic)
모든 dic이 result안에 들어감
최종적으로 csv파일로 저장
# 데이터 폴더 생성 후
import os
os.makedirs('data', exist_ok=True) # 디렉토리 생성 (이미 존재하면 무시
df.to_csv('./data/reviews.csv', index=False)
index는 false로 설정해서 순서 인덱스를 빼고 저장하게끔 설정
폴더가 없을때 폴더를 만들기 위해
os모듈에서 os.makedirs 사용하여 폴더 생성
exist_ok=true : 폴더가 이미 존재하면, 생성하지 않음