자습
T-아카데미 강의 : Python을 활용한 웹 크롤러 만들기( 하나투어 크롤링)
빵으니
2020. 7. 1. 16:47
selenium¶
- 인터파크 투어 사이트에서 여행지 입력 후 검색 -> 잠시 후 -> 결과
- 로그인 시 pc 웹 사이트에서 처리가 어려울 경우 -> 모바일 로그인 진입
- 모듈 가져오기
In [114]:
%%html
<!-- 에디터 폰트를 조정합니다. -->
<style type='text/css'>
.CodeMirror{
font-size: 14px;
font-family: consolas;
</style>
In [115]:
# pip install selenium
모듈 가져오기¶
In [116]:
from selenium import webdriver as wd
사전에 필요한 정보 로드¶
- 디비혹스 쉘, 배치 파일에서 인자로 받아서 세팅
In [117]:
main_url = 'http://tour.interpark.com/'
keyword = '로마'
드라이버 로드¶
- 차후에 옵션을 부여하여 (프록시, 에이전트 조작, 이미지 배제)
- 크롤링을 오래 돌리면 임시파일들이 쌓임 - 템포 파일 삭제
In [118]:
# 드라이버 로드
driver = wd.Chrome(executable_path='chromedriver.exe')
사이트 접속 (get 방식)¶
In [119]:
driver.get(main_url)
검색창을 찾아서 검색어 입력¶
In [120]:
# id : SearchGNBText
driver.find_element_by_id('SearchGNBText').send_keys(keyword) # send_keys 웹 페이지에 입력해라
# send_keys에서 수정할 경우, 뒤에 내용이 붙어버림 -> .clear()로 선 조치 후 send_keys('내용')다시 입력
검색버튼 클릭¶
In [121]:
driver.find_element_by_css_selector('.search-btn').click()
In [122]:
# 검색 후, 더보기를 눌러야 전체 해외여행 패키지 상품에 진입 가능
# 검색 결과 리스트가 페이징되어 나타남
잠시 대기¶
- 페이지가 로드되고 나서 즉각적으로 데이터를 획득하는 행위는 자제
- 검색 결과가 다 불러져 화면 구성할 때까지 대기해줘야 함
- 명시적 대기 : 특정 요소가 로케이트(발견될때까지) 대기
- 암묵적 대기 : DOM이 다 로드 될때까지 대기하고 먼저 로드되면 바로 진행
- 절대적 대기 : time.sleep(10) / 클라우드 페어(디도스 방어 솔루션)
In [123]:
from selenium.webdriver.common.by import By
# 명시적 대기를 위해
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
In [124]:
# 명시적 대기
try:
element = WebDriverWait(driver, 10).until(
# 지정한 한개 요소가 올라오면 웨이트 종료
EC.presence_of_element_located( (By.CLASS_NAME, 'oTravelBox') )
) # 첫번째 인자 driver, 10초 대기
except Exception as e:
print(' 오류 발생 ', e)
In [125]:
# 암묵적 대기
# 요소를 찾을 특정 시간 동안 DOM 풀링을 지시, 예를 들어 10초 이내라고 발견되면 진행
driver.implicitly_wait( 10 )
검색 후, '더보기' 눌러서 게시판 진입¶
In [126]:
driver.find_element_by_css_selector('.oTravelBox>.boxList>.moreBtnWrap>.moreBtn').click()
In [127]:
# 게시판에서 데이터를 가져올 때, 데이터가 많은면 세션(혹시 로그인을 해서 접근할 수 있는 사이트일 경우) 관리
# 특정 단위별로 로그아웃 로그인 계속 시도
# 특정 게시물이 사라질 경우, 팝업 발생(없는 ~ 라고 나옴) ->so, 팝업 처리 방법 검토 필요
# 게시판을 스캔 할 때, 임계점을 모른다는 문제가 있다.
# 게시판을 스캔 -> 메타 정보 획득 -> loop 돌려서 일괄적으로 방문 접근 처리
상품 정보를 담아주는 클래스 생성¶
In [128]:
class TourInfo:
# 각각의 정보 멤버변수에 정의 (실제 컬럼보다는 적게 셋팅함)
title = ''
price = ''
area = ''
link = ''
img = ''
# 생성자 (생성자에서는 멤버변수 초기화해주기)
def __init__(self, title, price, area, link, img):
self.title = title
self.price = price
self.area = area
self.link = link
self.img = img
# 주피터에서 class 쓰고 싶으면 쓰고 싶은 부분 위에서 정의 후 사용
In [129]:
# 페이징되어있는 게시판 (자바스크립트 구동)
# searchModule.SetCategoryList(1, '') 스크립트 실행
# 상품 정보 담는 리스트 생성(TourInfo 리스트)
tour_list = []
for page in range(1, 2): #17): #1-16 / 17은 임시값, 게시물을 넘어갔을 때 현상 확인용
try:
# 자바스크립트 구동하기
driver.execute_script("searchModule.SetCategoryList(%s, '')" % page)
time.sleep(2) # 2초마다 페이지 넘어가게
####################
# 여러 사이트에서 정보를 수집할 경우, 공통 정보 정의 단계 필요
# 상품명, 코멘트, 기간1, 기간2, 가격, 평점, 썸네일, 링크(상품상세정보)
boxItems = driver.find_elements_by_css_selector('.oTravelBox>.boxList>li') # 하위 하위의 li들만
# 상품 하나하나 접근
for li in boxItems:
print( '상품명', li.find_element_by_css_selector('h5.proTit').text)
print( '코멘트', li.find_element_by_css_selector('p.proSub').text)
print( '기간1', li.find_element_by_css_selector('p.proInfo').text)
print( '기간2', li.find_element_by_css_selector('p.proInfo:nth-child(2)').text)
print( '가격', li.find_element_by_css_selector('strong.proPrice').text)
print( '평점', li.find_element_by_css_selector('.info-row div:nth-child(2) p.proInfo').text)
# for info in li.find_elements_by_css_selector('.info-row .proInfo'):
# print( info.text )
# find (요소).get_attribute(속성명)사용해서 요소의 속성 값 가져올 수 있다
print( '썸네일', li.find_element_by_css_selector('img').get_attribute('src'))
print( '링크', li.find_element_by_css_selector('a').get_attribute('onclick'))
# 이미지를 링크값을 사용할 것인가
# or 직접 다운로드 해서 우리 서버에 업로드(ftp)할 것인가 고민
print('----------------------------------------------------------------')
# 데이터 모음
# 데이터가 부족하거나 없을수도 있으므로 직접 인덱스로 표현하는 것은 위험성이 있음
obj = TourInfo(
li.find_element_by_css_selector('h5.proTit').text,
li.find_element_by_css_selector('strong.proPrice').text,
li.find_element_by_css_selector('.info-row div:nth-child(2) p.proInfo').text,
li.find_element_by_css_selector('a').get_attribute('onclick'),
li.find_element_by_css_selector('img').get_attribute('src'),
)
tour_list.append( obj )
except Exception as e1:
print('오류', e1)
print(tour_list, len(tour_list))
수집한 정보 개수를 루프 -> 페이지 방문 -> 콘텐츠 획득(상품상세정보) -> DB¶
In [130]:
for tour in tour_list:
# tour -> TourInfo
# print( type(tour) )
# 링크 데이터에서 실데이터 확득
# 분해
arr = tour.link.split(',')
# print(arr)
if arr:
# 대체
link = arr[0].replace('searchModule.OnClickDetail()','')
print(link)
# 슬라이싱 -> 앞에 내용과 ', 뒤에 ' 제거
detail_url = link[28:-1]
print(detail_url)
# 상세 페이지 이동 : URL 값이 완성된 형태인지 확인(http~)
driver.get( detail_url )
time.sleep(2)
Beautiful Soup¶
In [131]:
from bs4 import BeautifulSoup as bs
In [135]:
# 현재 페이지를 beautiful soup 의 dom 으로 구성
soup = bs(driver.page_source, 'html.parser')
# 현재 상세 정보 페이지에서 스케줄 정보 획득
data = soup.select('.schedule-all')
print(type(data))
종료¶
In [ ]:
# 종료
driver.close()
driver.quit()
# import sys
# sys.exit()