Selenium으로 웹크롤러 만들기
목표
selenium을 이용하여 크롤링을 통해 각 상품별 세부 사항을 엑셀에 저장한다.
(Beautifulsoup을 이용하여 시도해보았으나 여러가지 문제점에 의해 Selenium을 선택했다. 동적 페이지 크롤링의 어려움, 웹페이지를 조작하기 어려움 등)
목표 세부설명
https://www.sigmaaldrich.com URL로 이동해 상품을 검색한다.
검색 결과로 나타난 상품을 클릭해 세부사항을 확인한다.
아래 그림의 빨간 네모칸에 해당하는 사항들을 크롤링한다.
- 크롤링의 결과로 출력된 데이터를 정리하여 Excel에 저장한다.
과정
1. URL 파악하기
https://www.sigmaaldrich.com/ 로 접속한 뒤 C:/test/product_list.txt
파일안의 상품들을 검색하고 그결과들을 통해 url 패턴을 파악한다. 패턴분석 결과 공통된 부분
+ 상품 카테고리(빨간부분)
+ 상품코드(초록색부분)
로 url이 이루어지는것을 찾아내었다.
문제점
각 상품별 url이 불규칙적이다. 불규칙 적인 부분은 상품코드 앞의 상품종류이다. 파악된 상품 카테고리는 총 4가지이다. sial, sigma, sigald, aldrich
해결책 1
첫번째로 각 상품코드 + 카테고리별 URL로 크롤링을 시도한다.
4가지 밖에 되지 않으니 시도해볼만 하다. 실제로 시도하여 성공했으나 성능, 유지보수
문제로 다른 방법으로 해결해야할 것 같다. 성능
의 경우 최대 4개의 URL로 요청 응답을 받아야 하므로 성능이 저하될 수 밖에 없다. 유지보수
의 경우 상품의 카테고리가 변경되거나 늘어나면 코드의 변경역시 필요하다.
해결책 2
상품을 클릭하기 이전의 URL에는 상품의 카테고리가 포함되지 않으며 충분히 규칙적인 것으로 파악되었다 https://www.sigmaaldrich.com/catalog/search?term='상품코드'
2. URL 완성시키기
상품을 클릭하여 세부사항을 확인하기 이전의 URL은 https://www.sigmaaldrich.com/catalog/search?term='상품코드' 로 규칙적임을 앞서 파악했다. 이로써 공통된 URL
+ 상품코드
로 URL을 완성시킬 수 있다.
* C:/test/product_list.txt
파일
먼저 C:/test/product_list.txt
파일을 로딩하여 상품 목록을 불러온다
x
# Product_list 파일 위치
product_file_path = 'C:/test/'
# Product_list 파일 이름
product_file_name = 'product.txt'
def make_request():
product_file = open(product_file_path + product_file_name, 'r') #읽기전용 파일 로딩
로딩된 파일을 이용해 한줄 한줄 뽑아내어 공통 URL
과 더해 URL을 완성한다
def make_request():
product_file = open(product_file_path + product_file_name, 'r')
for product_code in product_file.readlines():
url = 'https://www.sigmaaldrich.com/catalog/search?term=' + product_code
문제점
세부 상품을 확인해야 최종 목표인 크롤링을 진행할 수 있다. 따라서 코드로 웹페이지를 클릭하는 등의 효과를 줄수 있는 방법이 필요하다.
해결책
Selenium의 click 메소드
를 이용하여 웹페이지를 클릭하는 등의 효과를 줄 수 있다.
3. Chrome Driver 설치
우선 selenium을 이용하기 위해서는 웹브라우저의 Driver가 필요하다. http://chromedriver.chromium.org/downloads로 이동하여 적절한 버전을 다운로드 받고. 적절한 위치에 드라이버를 위치 시킨다. 아래 그림과 같이 간단히 chrome.exe
파일만을 넣어 두면 된다.
driver = webdriver.Chrome(chrome_driver_path)
코드를 통해 driver를 로딩한다. 그 후 webdriver의 get()
함수를 이용하여 앞서 얻어낸 url로 get요청을 진행한다.
xxxxxxxxxx
from selenium import webdriver
def make_request():
product_file = open(product_file_path + product_file_name, 'r')
for product_code in product_file.readlines():
try:
url = 'https://www.sigmaaldrich.com/catalog/search?term=' + product_code
driver = webdriver.Chrome(chrome_driver_path)
driver.get(url)
4. Selenium으로 웹페이지 클릭하기
우선 클릭해야 하는 웹페이지의 link
의 xpath
를 찾아야 한다. 상품을 검색한 뒤 키보드의 f12
를 눌러 Chrome 개발자 도구를 연뒤 마우스커서 버튼
을 클릭한다. 그 다음 XPath를 찾으려는 대상을 클릭하면 해당 오브젝트의 HTML 태그를 확인할 수 있다.
* XPath - XPath는 W3C의 표준으로 확장 생성 언어 문서의 구조를 통해 경로 위에 지정한 구문을 사용하여 항목을 배치하고 처리하는 방법을 기술하는 언어이다.
get요청을 통해 얻어낸 Webdriver 객체가 담긴 driver
의 find_element_by_xpath
함수를 이용하여 앞서 얻어낸 xpath를 이용해 클릭해야하는 오브젝트를 얻어낸다. 그 후 얻어낸 객체의 click()
함수를이용해 해당 페이지를 클릭한다.
xxxxxxxxxx
def make_request():
product_file = open(product_file_path + product_file_name, 'r')
for product_code in product_file.readlines():
try:
url = 'https://www.sigmaaldrich.com/catalog/search?term=' + product_code
driver = webdriver.Chrome('/Users/galid/chrome_driver/chromedriver')
driver.get(url)
search_result = driver.find_element_by_xpath( #xpath로 클릭할 오브젝트를 얻어냄
"//*[@id='searchResultContainer-inner']/div[7]/div/div[2]/div[2]/div[1]/ul/li[2]/a") # copy한 xpath
search_result.click() #페이지 클릭
5. 웹페이지 크롤링
이제 원하는 페이지에 접근까지 했으니 크롤링만 하면 된다. 원하는 페이지 까지 이동된 driver 객체에서 데이터를 크롤링하기 위해 crawling(driver)
함수의 인자로 넘겨준다.
xxxxxxxxxx
def make_request():
product_file = open(product_file_path + product_file_name, 'r')
for product_code in product_file.readlines():
try:
url = 'https://www.sigmaaldrich.com/catalog/search?term=' + product_code
driver = webdriver.Chrome(chrome_driver_path)
driver.get(url)
search_result = driver.find_element_by_xpath(
"//*[@id='searchResultContainer-inner']/div[7]/div/div[2]/div[2]/div[1]/ul/li[2]/a")
search_result.click()
crawling(driver) #driver를 crawling() 함수의 인자로 넘겨줌
crawling(driver)
함수에서는 웹페이지에서 원하는 정보들을 크롤링 한다. 역시 크롬 개발자 도구(f12)
를 이용해 필요한 데이터를 가진 태그들을 찾아낸 후 그것을 이용해 크롤링을 한다. 그 후 크롤링한 데이터들을 리스트에 담은 뒤 엑셀에 저장하는 함수인 insert_data_to_excel()
함수의 인자로 넘겨준다.
xxxxxxxxxx
def crawling(driver):
crawling_results = []
#H1, H2 데이터 불러오기
crawling_results.append(driver.find_elements_by_tag_name('h1')[0].text) #title
crawling_results.append(driver.find_elements_by_tag_name('h2')[1].text) #description
#Table안의 데이터 불러오기
table = driver.find_element_by_tag_name('table')
tbody = table.find_element_by_tag_name('tbody')
trs = tbody.find_elements_by_tag_name('tr')
try : #td 내용 존재하는 경우(sku, shipping, price)
tds = trs[1].find_elements_by_tag_name('td')
crawling_results.append(tds[0].text) #sku
crawling_results.append(tds[1].text) #shipping
crawling_results.append(tds[3].text) #price
except : #내용 존재하지 않는 경우(비고)
note = 'To order products, please contact your local dealer.'
#TODO 비고에 덧 붙히는 코드추가
driver.close()
insert_data_to_excel(crawling_results) # 크롤링한 데이터가 담긴 배열을 엑셀에 저장하는 함수의 인자로 넘겨준다.
6. Excel에 저장
엑셀에 데이터를 저장하기 이전에 우선 excel파일을 만들도록 하자. openpyxl
을 이용할것이다. 그 후 insert_data_to_excel
을 이용하여 만든 excel 파일에 데이터를 저장할 것이다. excel 파일의 이름은 자동화를 위해 날짜로 지정했다.
아래 코드는 excel 파일을 만드는 코드이다. 우선 openpyxl 모듈을 로딩한 뒤 excel 파일을 만들기 위한 변수들을 선언했다. 그후 make_excel()함수를 통해 excel 파일을 만들었다. 마지막으로 excel의 헤더들을 미리 입력해두기 위해 sheet의 cell.value를 통해 sheet의 원하는 cell에 헤더를 입력했다.
xxxxxxxxxx
from openpyxl import Workbook,load_workbook
# 날짜
now = datetime.datetime.now()
date = now.strftime('%Y.%m.%d')
# Excel 파일 저장 위치
excel_file_path = 'C:/test/'
# Excel 파일 이름
excel_file_name = excel_file_path + date + '.xlsx'
# Excel sheet 이름
excel_sheet_title = 'confirm'
# Excel 행
excel_row = 2
## Excel파일 생성 메소드
def make_excel():
work_book = Workbook()
sheet1 = work_book.active
sheet1.title = excel_sheet_title
#헤더 입력
sheet1.cell(row=1, column=1).value = '상품ID'
sheet1.cell(row=1, column=2).value = '규격'
sheet1.cell(row=1, column=3).value = 'Title'
sheet1.cell(row=1, column=4).value = 'Description'
sheet1.cell(row=1, column=5).value = 'CAS Number'
sheet1.cell(row=1, column=6).value = '확인가능'
sheet1.cell(row=1, column=7).value = '가격'
sheet1.cell(row=1, column=8).value = 'Storage Temp'
sheet1.cell(row=1, column=9).value = '비고'
work_book.save(filename=excel_file_name) # 변경 후에는 꼭 save를 해주어야 한다.
work_book.close()
우선 위에서 생성한 excel파일을 load_workbook('경로가 포함된 파일이름')
함수를 이용해 파일을 로딩한다. 그 후 sheet.cell(row, column).value를 이용해 크롤링 데이터 리스트에서 한 항목씩 꺼내어 excel에 저장을 한다.
xxxxxxxxxx
## 엑셀에 크롤링된 데이터 저장하는 메소드
def insert_data_to_excel(crawling_results):
excel_file = load_workbook(excel_file_name)
sheet1 = excel_file[excel_sheet_title]
excel_column = 3
for data in crawling_results:
sheet1.cell(row=excel_row, column=excel_column).value = data
excel_column += 1
excel_file.save(excel_file_name)
excel_file.close()
최종 코드
xxxxxxxxxx
from urllib.error import HTTPError
from selenium import webdriver
from openpyxl import Workbook,load_workbook
import datetime
#TODO 파일 열고 닫는 위치를 조정해야할 듯
#TODO 성능향상 위해 multiprocessing 추가해야 할 듯
# 날짜
now = datetime.datetime.now()
date = now.strftime('%Y.%m.%d')
# Product_list 파일 위치
product_file_path = 'C:/test/'
# Product_list 파일 이름
product_file_name = 'product.txt'
# Chrome Driver 위치
chrome_driver_path = '/Users/galid/chrome_driver/chromedriver'
# Excel 파일 저장 위치
excel_file_path = 'C:/test/'
# Excel 파일 이름
excel_file_name = excel_file_path + date + '.xlsx'
# Excel sheet 이름
excel_sheet_title = 'confirm'
# Excel 행
excel_row = 2
## Excel파일 생성 메소드
def make_excel():
work_book = Workbook()
sheet1 = work_book.active
sheet1.title = excel_sheet_title
#헤더 입력
sheet1.cell(row=1, column=1).value = '상품ID'
sheet1.cell(row=1, column=2).value = '규격'
sheet1.cell(row=1, column=3).value = 'Title'
sheet1.cell(row=1, column=4).value = 'Description'
sheet1.cell(row=1, column=5).value = 'CAS Number'
sheet1.cell(row=1, column=6).value = '확인가능'
sheet1.cell(row=1, column=7).value = '가격'
sheet1.cell(row=1, column=8).value = 'Storage Temp'
sheet1.cell(row=1, column=9).value = '비고'
work_book.save(filename=excel_file_name)
work_book.close()
## 크롤링을 위한 메소드
def make_request():
product_file = open(product_file_path + product_file_name, 'r')
for product_code in product_file.readlines():
try:
url = 'https://www.sigmaaldrich.com/catalog/search?term=' + product_code
driver = webdriver.Chrome(chrome_driver_path)
driver.get(url)
search_result = driver.find_element_by_xpath(
"//*[@id='searchResultContainer-inner']/div[7]/div/div[2]/div[2]/div[1]/ul/li[2]/a")
search_result.click()
crawling(driver)
except HTTPError as e:
#TODO Log 파일 만드는 것 좋을듯.
pass
global excel_row
excel_row += 1
product_file.close()
## 크롤링한 데이터로부터 원하는 정보를 파싱하는 메소드
def crawling(driver):
crawling_results = []
#H1, H2 데이터 불러오기
crawling_results.append(driver.find_elements_by_tag_name('h1')[0].text) #title
crawling_results.append(driver.find_elements_by_tag_name('h2')[1].text) #description
#Table안의 데이터 불러오기
table = driver.find_element_by_tag_name('table')
tbody = table.find_element_by_tag_name('tbody')
trs = tbody.find_elements_by_tag_name('tr')
try : #td 내용 존재하는 경우(sku, shipping, price)
tds = trs[1].find_elements_by_tag_name('td')
crawling_results.append(tds[0].text) #sku
crawling_results.append(tds[1].text) #shipping
crawling_results.append(tds[3].text) #price
except : #내용 존재하지 않는 경우(비고)
note = 'To order products, please contact your local dealer.'
#TODO 비고에 덧 붙히는 코드추가
driver.close()
insert_data_to_excel(crawling_results)
## 엑셀에 크롤링된 데이터 저장하는 메소드
def insert_data_to_excel(crawling_results):
excel_file = load_workbook(excel_file_name)
sheet1 = excel_file[excel_sheet_title]
excel_column = 3
for data in crawling_results:
sheet1.cell(row=excel_row, column=excel_column).value = data
excel_column += 1
excel_file.save(excel_file_name)
excel_file.close()
make_excel()
make_request()
참고
https://medium.com/@peteryun/python-selenium%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-%ED%81%AC%EB%A1%A4%EB%9F%AC-%EB%A7%8C%EB%93%A4%EA%B8%B0-b055cefd1195
'Language > Python' 카테고리의 다른 글
Python - Json 사용법 (0) | 2019.01.13 |
---|---|
Python - 문자열내에 특수문자 존재 확인 (any 메소드) (0) | 2018.12.27 |
Python - 전역변수와 지역변수 , Global (2) | 2018.12.17 |
Python - 클래스(Class)란? (0) | 2018.12.17 |
Python - 모듈, 패키지 (0) | 2018.12.17 |