데이터 분석/웹 크롤링

[이론] sellenium을 활용한 크롤링

toraa 2025. 1. 20. 13:40

웹드라이버 구동

from selenium import webdriver

# driver = webdriver.Chrome('./chromedriver.exe') #115 버전이하

driver = webdriver.Chrome() #116 버전이상

selenium으로 크롬 구동

 

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

 

data-review-id 같이 원하는 정보있는 곳을 찾아서 가져옴


여러개 요소를 한번에 찾아오기

 

전체적인 요소를 모두 포함하는 곳 소스 확인해보면

반복되는 요소들이 있음(<div class='RHo1pe'>)  → 리뷰 목록에 해당

* 태그이름과 클래스이름이 같은것을 찾기

→ css selector가 같음

 

드래그로 리뷰가 노출됨→ 드래그를 최대한 많이 해서 노출시켜야 함

 

 

드래그가 있는 요소를 찾아야 함

보통 페이지 상부에 있지만, 내부에 있는 경우가 있음

보통 상부에 scroll이 있음
내부에 [scroll] 버튼이 있는 곳을 찾기

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 : 폴더가 이미 존재하면, 생성하지 않음