이번 글에서는 Jupyter lab을 이용하여 selenium 크롤링 실습을 해보고자 한다.
0. 준비하기
- jupyter alb, selenium, webdriver-manager, pandas, requests를 pip install 해주었다.
- 또한, 다음과 같이 ipnyb 파일, driver 폴더를 생성하여 기본 세팅을 해주었다.
1. 셀레니움 작동 확인하기(url 열기)
- chrome driver path = 바로 위에 이미지에서 보이는 경로(크롬드라이버 실행파일이 있는 곳)
- 이하는 드라이버 설정인데, 그냥 기본 설정이라고 생각하자.
- driver.get 메서드로 드라이버를 통해 원하는 url로 이동
★ 기타 오류를 방지하기 위해 드라이버 사용 후 꼭 " driver.quit()" 메서드를 통해 드라이버를 종료해주자 ★
2. 이미지 검색 부터 다운로드까지
2-1 이미지 검색 검색창에 원하는 검색어 입력하기 (사람의 행동을 COPY)
- 먼저 검색어를 입력하는 곳의 태그는 다음과 같다.
- 이 경로를 참고해서 다음과 같이 코드를 작성한다. * keys.RETURN이 Enter 의 기능이라고 보면 된다.
elem = driver.find_element(By.CSS_SELECTOR, "body > div.L3eUgb > div.o3j99.ikrT4e.om7nvf >
form > div:nth-child(1) > div.A8SBwf > div.RNNXgb > div > div.a4bIc > textarea.gLFyf")
elem.send_keys("보라카이")
elem.send_keys(Keys.RETURN)
2-2 최대한 많은 이미지 로드를 위해 스크롤
- 보통 이미지를 검색하면 초기화면보다 스크롤을 더 내렸을 때, 이미지가 더 나오곤 한다. 그렇기 때문에 크롤링에서도 이미지 로드를 위한 스크롤 작업이 필요한 것이다. 다음과 같이 selenium에서 스크롤 기능을 구현할 수 있다.
elem = driver.find_element(By. TAG_NAME, "body")
for i in range(60):
elem.send_keys(Keys.PAGE_DOWN)
time.sleep(0.1)
- elem = driver.find_element(By. TAG_NAME, "body") 은 selenium 을 사용하여 웹페이지에서 <body> 태그를 찾아 해당 요소(element)를 가져오는 코드다. 말 그대로 by tag name으로 body라는 태그를 찾아 해당 요소를 elem 변수에 할당하는 코드이다. 이렇게 변수를 할당하는 이유는 이후 해당 변수를 통해 send_keys메서드와 PAGE_DOWN 메서드를 사용하기 위해서이다.
try:
driver.find_element(By.CSS_SELECTOR, "#islmp > div > div > div > div > div.C5Hr4 > div.K414Oe > div.FAGjZe > input").click()
for i in range(80):
elem.send_keys(Keys.PAGE_DOWN)
time.sleep(0.1)
except:
pass
- 해당 구문은 스크롤을 내리다보면 마주치는 "결과 더보기" 탭을 클릭하기 위한 것이다. 그래서 .click() 메서드가 있는 것이고 이후로 또 80번의 스크롤을 반복하는 코드이다. 예외처리 = pass
links=[]
images = driver.find_elements(By.CSS_SELECTOR, "#islrg > div.islrc > div > a.wXeWr.islib.nfEiy > div.bRMDJf.islir > img")
for image in images:
if image.get_attribute('src') is not None:
links.append(image.get_attribute('src'))
- 값을 반환해줄 빈 리스트를 만들고, css 선택자로 이미지의 경로를 복사해서 붙여넣어주고, img src값을 포함한 elements값을 가져와 images 변수에 할당한다.
- 이후 for 와 if 문을 이용해서 elements에서 src 값이 not None 인 값들만 다시 links 리스트에 추가해준다.
links=[]
images = driver.find_elements(By.CSS_SELECTOR, "#islrg > div.islrc > div > a.wXeWr.islib.nfEiy > div.bRMDJf.islir > img")
images2 = driver.find_elements(By.CSS_SELECTOR, "#islrg > div.islrc > div > div > a.wXeWr.islib.nfEiy > div.bRMDJf.islir > img")
# 결과더보기 이후는 경로가 약간 다름 div와 a태그 사이에 또다른 div 존재
for image in images:
if image.get_attribute('src') is not None:
links.append(image.get_attribute('src'))
for image in images2:
if image.get_attribute('src') is not None:
links.append(image.get_attribute('src'))
print(' 찾은 이미지 개수:',len(links))
- 여기서 심화로 들어가보자면, 결과더보기 클릭 후 스크롤 이후에도 값은 계속 48개인 것을 확인할 수 있었을 것이다. 결과 더보기 이후로(혹은 특정 구간 이후) images와 images2 처럼 selector 경로가 달라졌었기 때문이다. 해당 문제를 위와 같이 두 가지 경우로 리스트를 추가해주면 되겠지만, 구글 이미지 검색 특성상 "관련컨텐츠"에 있는 이미지도 같이 크롤링 되므로 생각을 해볼 문제이다.
- 심화 내용을 제외하면 다음과 같이 전체 코드가 작성된다.
from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager
driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager().install()))
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
import time
URL = 'https://www.google.co.kr/imghp'
driver.get(url=URL)
elem = driver.find_element(By.CSS_SELECTOR, "body > div.L3eUgb > div.o3j99.ikrT4e.om7nvf > form > div:nth-child(1) > div.A8SBwf > div.RNNXgb > div > div.a4bIc > textarea.gLFyf")
elem.send_keys("보라카이")
elem.send_keys(Keys.RETURN)
elem = driver.find_element(By. TAG_NAME, "body")
for i in range(70):
elem.send_keys(Keys.PAGE_DOWN)
time.sleep(0.1)
try:
driver.find_element(By.CSS_SELECTOR, "#islmp > div > div > div > div > div.C5Hr4 > div.K414Oe > div.FAGjZe > input").click()
for i in range(80):
elem.send_keys(Keys.PAGE_DOWN)
time.sleep(0.1)
except:
pass
links=[]
images = driver.find_elements(By.CSS_SELECTOR, "#islrg > div.islrc > div > a.wXeWr.islib.nfEiy > div.bRMDJf.islir > img")
# images2 = driver.find_elements(By.CSS_SELECTOR, "#islrg > div.islrc > div > div > a.wXeWr.islib.nfEiy > div.bRMDJf.islir > img")
# 결과더보기 이후는 경로가 약간 다름 div와 a태그 사이에 또다른 div 존재
for image in images:
if image.get_attribute('src') is not None:
links.append(image.get_attribute('src'))
# for image in images2:
# if image.get_attribute('src') is not None:
# links.append(image.get_attribute('src'))
print(' 찾은 이미지 개수:',len(links))
2-3 크롤링된 사진 다운로드하기
import urllib.request
for k, i in enumerate(links):
url = i
urllib.request.urlretrieve(url, ".\\사진다운로드\\"+str(k)+".jpg")
print('다운로드 완료하였습니다.')
driver.quit()
- urllib.request.urlretrieve() 는 웹에서 파일을 다운로드하는 데 사용되는 urllib.request 모듈의 함수이다. 이 함수를 사용하면 지정한 URL에서 파일을 다운로드하여 로컬 시스템에 저장할 수 있다. 어려운 문법이 필요한 내용은 아니다.
- links 리스트의 url을 반복하면서 만들어놓은 "사진다운로드(변경 가능)" 폴더에 저장한다.
* str(k) : 정수 k를 문자열로 변환하는 Python의 내장 함수.
3. 실시간 검색어 가져오기
- 이제는 역사속으로 사라진 실시간 검색어를 시그널사이트에서 가져와 보도록 하겠다.
- 먼저 사이트를 상세 분석해보면 다음과 같다.
- 공통부분을 찾고 이외에는 합치는 느낌
1 위 값 : #app > div > main > div > section > div > section > section:nth-child(2) > div:nth-child(2) > div > div:nth-child(1) > div:nth-child(1) > a > span.rank-text
10위값 : #app > div > main > div > section > div > section > section:nth-child(2) > div:nth-child(2) > div > div:nth-child(2) > div:nth-child(5) > a > span.rank-text
- 최종 요청할 selector
"#app > div > main > div > section > div > section > section:nth-child(2) > div:nth-child(2) > div > div > div > a > span.rank-text")
from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
import time
driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager().install()))
URL = 'https://signal.bz/news'
driver.get(url=URL)
results = driver.find_elements(By.CSS_SELECTOR, "#app > div > main > div > section > div > section > section:nth-child(2) > div:nth-child(2) > div > div > div > a > span.rank-text")
results
keyword_list = []
for keyword in results:
print(keyword.text)
- 코드 실행 결과
4. 실시간 검색어 가져오기 2 (새로고침되는 리스트 가져오기)
- 이번에는 nate의 실시간 이슈 키워드를 가져와보려고 하는데, 이전과 다르게 1-5위 6-10위가 N 초마다 번갈아가면서 나오는 상황이다.
- 한편, 실시간 이슈 키워드에서 불러와야할 값은 안타깝게도(?) 두 가지이다. 실시간 이슈 키워드 순위와 실시간 이슈 키워드를 말한다. 사이트 소스코드를 확인해보면 다음과 같이 구성되어있다.
- 이러한 부분들을 고려하여 다음과 같이 코드를 작성하였다.
from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
import time
driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager().install()))
nate_list_1st = []
nate_list_2nd = []
for i in range(2):
URL='https://www.nate.com'
driver.get(url=URL)
rank_results = driver.find_elements(By.CSS_SELECTOR,'#olLiveIssueKeyword > li > span.num_rank')
nate_results = driver.find_elements(By.CSS_SELECTOR,'#olLiveIssueKeyword > li > a > span.txt_rank')
for rank, keyword in zip(rank_results, nate_results):
if i == 0: # 첫번째 화면
nate_list_1st.append(f'{rank.text}_{keyword.text}')
elif i == 1: # 두번째 화면
nate_list_2nd.append(f'{rank.text}_{keyword.text}')
time.sleep(8)
driver.refresh() # driver 재시동
result = nate_list_1st + nate_list_2nd
print(result)
driver.quit()
- 특별한 것 위주로 코드 해설
- for rank, keyword in zip(rank_results, nate_results) : 두 개의 리스트에서 동시에 요소를 하나씩 가져와서 반복 작업을 수행하는 구문. "병렬(iterable) 언패킹(parallel unpacking)"이라고도 한다. 여기서 rank_results와 nate_results는 두 리스트의 각 요소들이 각각 rank와 keyword 변수에 할당됨. 반복문을 통해 두 리스트의 각 요소를 순차적으로 가져와서 두 변수에 할당하고, 각 요소에 대한 작업을 수행할 수 있게 된다.
- 코드 실행 결과
* enumerate() 와 zip()의 차이
5. 쇼핑 리스트 가져오기
- 다음으로는 뽐뿌의 게시판 제목과 URL들을 가져오는 실습을 해보려고 한다.
- 첫번째 게시글의 title selector 값은
#revolution_main_table > tbody > tr:nth-child(9) > td:nth-child(3) > table > tbody > tr > td:nth-child(2) > div > a > font
두번째 게시글의 title selector 값은
#revolution_main_table > tbody > tr:nth-child(11) > td:nth-child(3) > table > tbody > tr > td:nth-child(2) > div > a > font
- 첫번째 게시글의 href selector 값은
#revolution_main_table > tbody > tr:nth-child(9) > td:nth-child(3) > table > tbody > tr > td:nth-child(2) > div > a
두번째 게시글의 href selector 값은
#revolution_main_table > tbody > tr:nth-child(13) > td:nth-child(3) > table > tbody > tr > td:nth-child(2) > div > a
- 위 내용을 조합하여 다음과 같이 코드를 작성해보자.
from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
import time
driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager().install()))
URL= 'https://www.ppomppu.co.kr/zboard/zboard.php?id=ppomppu'
driver.get(url=URL)
titles = driver.find_elements(By.CSS_SELECTOR, '#revolution_main_table > tbody > tr > td:nth-child(3) > table > tbody > tr > td:nth-child(2) > div > a > font')
urls = driver.find_elements(By.CSS_SELECTOR, '#revolution_main_table > tbody > tr > td:nth-child(3) > table > tbody > tr > td:nth-child(2) > div > a')
title_lists = []
url_lists = []
for i in range(len(titles)):
print(titles[i].text) #0번째 게시글 타이틀 출력
title_lists.append(titles[i].text) #0번째 게시글 타이틀 리스트로 추가
print(urls[i].get_attribute('href')) #0번째 게시글 URL 출력
url_lists.append(urls[i].get_attribute('href')) #0번째 게시글 URL 리스트에 추가
- 아마 지금까지 글을 쭉 따라온 사람이라면, 이해가 안되는 내용을 없을 것이다. for 반복문도 기본적인 내용이다.
* 막간 상식(.text 하기 전의 모습)
print(titles[0])
<selenium.webdriver.remote.webelement.WebElement (session="b231329af1f08bd9b72a62554308b2c6", element="3C9CC48DF124368DD5237BB4B46F1EA5_element_27")>
print(type(titles[0]))
<class 'selenium.webdriver.remote.webelement.WebElement'>
'IT & 개발공부 > 파이썬(Python)' 카테고리의 다른 글
scrapy를 이용한 웹 크롤링 실습 (0) | 2023.08.10 |
---|---|
Pandas 기초 개념 공부 (0) | 2023.08.09 |
파이썬(beautifulsoup) 크롤링 기초 공부 및 예제를 통해 실습하기 -2 (0) | 2023.08.07 |
파이썬(beautifulsoup) 크롤링 기초 공부 및 예제를 통해 실습하기 -1 (0) | 2023.08.06 |
네이버 API 사용 신청(2023년 08월 최신) (0) | 2023.08.03 |