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요청을 진행한다.
xxxxxxxxxxfrom selenium import webdriverdef 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() 함수를이용해 해당 페이지를 클릭한다.
xxxxxxxxxxdef 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) 함수의 인자로 넘겨준다.
xxxxxxxxxxdef 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() 함수의 인자로 넘겨준다.
xxxxxxxxxxdef 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에 헤더를 입력했다.
xxxxxxxxxxfrom 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()
최종 코드
xxxxxxxxxxfrom urllib.error import HTTPErrorfrom selenium import webdriverfrom openpyxl import Workbook,load_workbookimport 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 |