하고 있는 업무 때문에 시중 어플리케이션 몇 가지의 리뷰를 크롤링 하려 구글 플레이 스토어를 방문했습니다. 그런데 화면이 개편되어 있더군요. 모골이 송연해졌습니다. 그러면 클로링을 위해 짜둔 코드들, 그 코드를 구성하고 있는 HTML(CSS) 요소들이 전부 못 쓰게 되었을 가능성이 있거든요.
역시 이전의 프론트(화면)를 기준으로 작성된 크롤링 코드는 먹히질 않더군요. (그래도 참고삼아 링크 걸어봅니다)
그래서 부랴부랴 개편된 구글 플레이 스토어에 맞추어서 앱 리뷰 클로러를 새로 프로그래밍했습니다. 급하게 만들어서 코드에 비효율적인 부분이 좀 보이는데요. 지속적으로 업데이트 하도록 하겠습니다(이 포스팅에요).
작업환경
-
파이썬 셀레니움(Selenium) 라이브러리를 사용할 것이기 때문에 크롬드라이버 깔아두셔야 합니다. 크롬드라이버 설치법은 구글링 해보시면 금방 나올 거예요.
1) 파이썬3.7(32bit)
2) 파이썬IDE Anaconda JupyterNotebook
3) 본 크롤러는 크롬 확장프로그램의 도움으로 작동하기 때문에 사전에 크롬이 설치되어 있어야 합니다.
4) 또한 Selenium을 사용하기 때문에, 크롬드라이버(ChromeDriver)를 다운받아서 앞으로 작성할 파이썬 코드(.py)가 저장될 폴더에 함께 넣어두어야 합니다.
코드
-
말씀드린 것처럼 현재 코드는 초안이고 비효율적인 부분이 있습니다. 과업 중간에 사람이 개입해야 하는 부분이 있으니 양해 부탁드려요. 아래의 코드를 순차적으로 수행하시면 스크래핑 된 리뷰가 엑셀로 출력됩니다.
▼ 필요한 라이브러리 호출. 당연히 미리 install 해두어야 겠지요?
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
import time
from bs4 import BeautifulSoup
import re
import pandas as pd
▼ 아래 코드를 수행하면 url을 입력하는 필드가 출력됩니다. 이 필드에 스크래핑(=크롤링) 하고자 하는 앱의 상세 페이지 주소를 입력하고 엔터. 가령, https://play.google.com/store/apps/details?id=com.kakaogames.umamusume&hl=ko 이런 주소요(저 이 게임 관계자 아닙니다. 게임 안 좋아한다능).
# 크롤링하고자 하는 리뷰 페이지의 url을 적어준다.
url = input('스크래핑할 URL 주소를 입력 :')
▼ 이번 개편의 가장 큰 변화가 바로 '리뷰 전체 보기' 페이지를 별도의 url을 갖는 페이지가 아니라 원페이지 내 component로 구성했다는 것입니다. 때문에 셀레니움으로 브라우저를 열어서 머신이 버튼을 누르게끔 프로그래밍 해야 하는 거죠.
# '리뷰 전체 보기 버튼 클릭' 함수 정의
element = "button.VfPpkd-Bz112c-LgbsSe.yHy1rc.eT1oJ.QDwDD.DiOXab.VxpoF[aria-label='평가 및 리뷰 자세히 알아보기']" # 띄어쓰기는 '.'으로 교체
def click_element(driver):
click_target = driver.find_element(by=By.CSS_SELECTOR, value=element)
click_target.click()
time.sleep(3)
▼ 코드 본문 주석에 자세히 적어뒀습니다. 크롬드라이버로 브라우저 열어서, 앱 상세페이지 접속한 후, '리뷰 전체 보기' 버튼을 클릭하는 코드 스니펫입니다.
# options = Options()
# options.add_argument('--kiosk') # 화면을 전체화면으로 열어주기 위해서
driverPath = "chromedriver.exe" # 크롬드라이버 설치된 경로. 파이썬(.py) 저장 경로와 동일하면 그냥 파일명만
driver = webdriver.Chrome(driverPath) # Open Chrome
driver.get(url) # 최초 패러그래프에서 입력한 URL
time.sleep(3)
click_element(driver) # 브라우저 열어서 -> 평가 및 리뷰 전체 보기 버튼 클릭
▼ 이 단계에서 여러분의 손이 필요합니다. 위의 코드까지 실행하면 아래처럼 리뷰 목록이 화면에 떠있을 겁니다. 이 목록은 '전체' 리뷰를 담고 있는 것이 아니기 때문에 열나게 스크롤 다운해서 리뷰가 전부 소환되도록 손을 써주셔야 합니다. (이 과업도 당연히 코드로 자동화할 수 있는데 바빠서 못 했습니다. 업데이트 해둘게요)
▼ 열심히 스크롤 다운해서 리뷰를 다 불러왔다면 아래 코드를 실행해 줍니다. 몇 건의 리뷰가 있는지 보여줄 겁니다.
title = driver.find_element(by=By.CSS_SELECTOR, value="h5.xzVNx").text
html = driver.page_source
soup = BeautifulSoup(html, 'html.parser')
content = soup.findAll("div", {'class': 'RHo1pe'})
print(title)
print(len(content))
▼ 이제 본격적으로 리뷰를 수집합니다. 정확하게는 리뷰 내용과 별점, 그리고 해당 리뷰에 공감을 표한 사용자의 수. 이렇게 세 가지 정보를 수집합니다.
item_list = [
"iXRFPc" # 별점
, "h3YV2d" # 리뷰
, "AJTPZc" # 리뷰 동조자 수
]
# 수집 대상 요소를 컬럼으로 하는 빈 list 생성
tmp_list = []
print(len(content))
for i in range(len(content)):
try:
item1 = content[i].find("div", {'class': item_list[1]}).text
except:
item1 = ""
try:
item2 = content[i].find("div", {'class': item_list[0]})['aria-label']
except:
item2 = ""
try:
item3 = content[i].find("div", {'class': item_list[2]}).text
except:
item3 = str(0)
tmp_list.append([item1, item2, item3])
tmp_list
▼ List 형태의 데이터를 DataFrame으로 변환합니다. 내가 원하는 컬럼명도 지정해줍니다. 별점과 리뷰 동조자 수는 딱 숫자(정수)로만 표현하고 싶네요. 별도의 전처리가 필요하겠습니다. 바로 다음 코드 스니펫에서 이어갈게요.
# import pandas
import pandas as pd
from pandas import DataFrame
from datetime import datetime
# 위 과정의 결과물은 list이기 때문에 이것을 dataframe으로 변형
# DF로 변형하면서 컬럼명을 국문으로 지정
result_df = DataFrame(tmp_list, columns=[
'리뷰내용'
, '별점'
, '리뷰동조자수'
])
result_df.head()
▼ lambda를 사용해서 별점, 리뷰 동조자 수 컬럼의 값에서 숫자만 남기는 작업을 수행했습니다. 정규표현식에 대한 이해가 있어야 코드가 이해되실 겁니다. 매우매우 활용도가 높은 코딩 지식이니, '정규표현식'은 꼭 한 번 공부해보세요.
tmp = result_df.copy()
# 앞의 별표 5점은 생략. 정규 표현식을 사용해 숫자만 추출. "별표 5개"를 생략
tmp['별점'] = tmp['별점'].apply(lambda x: x[5:])
# re 라이브러리를 임포트 후 정규표현식 사용
import re
'''
정수형 숫자 1개만 있거나, 4.8 과 같이 소수점으로 계산된 점수를 추출
[0-9]는 0부터 9까지의 숫자 중 하나를 의미
"\"는 뒤에 오는 "."를 정규표현식이 아니라 있는 그대로의 문자열(".")로서 인식하도록 함
"."은 어떤 문자든 1개가 등장함을 의미
*은 앞의 문자(숫자, 특수문자 포함)가 0개 이상 등장함을 의미
'''
m = re.compile('[0-9][\.0-9]*')
tmp['별점'] = tmp['별점'].apply(lambda x : m.findall(x)[0])
tmp['리뷰동조자수'] = tmp['리뷰동조자수'].apply(lambda x : m.findall(x)[0])
tmp.head(3)
▼ 이제 다 되었습니다. 엑셀로 저장:D
# 엑셀 파일로 저장
date = datetime.today().strftime('%Y-%m-%d')
tmp.to_excel("("+date+")_닥터모아.xlsx", index=False)
긴 글 봐주셔서 감사해요. 혹시 크롤링 해보고 싶은 페이지가 있다면 제보해주세요.
'Programming > Code Archive' 카테고리의 다른 글
[파이썬] 인스타그램 해쉬태그(#) 검색결과 크롤링하기_최신ver. (19) | 2022.08.13 |
---|---|
[파이썬-오픈API] 한국은행 경제통계지표 추출 (0) | 2022.07.02 |
전체 상장기업 재무제표 조회방법(feat. 파이썬으로 DART API 호출) (12) | 2022.05.11 |
파이썬 streamlit으로 데이터 시각화 웹어플리케이션 배포하기 (0) | 2022.05.09 |
파이썬 Tesseract로 OCR(광학식 문자 판독기) 구현하기 (2) | 2022.05.08 |
댓글