실전 프로젝트 10 - 크롤링
많은 데이터를 DB에 저장하기 위해 컴퓨터 관련 제품들을 웹사이트에서 크롤링해야할 필요가 있었다. 해당 데이터드를 '다나와'웹사이트에서 크롤링하기로 결정한 후, 간결하고 가독성이 좋은 Python을 이용해 데이터를 끌어오고자 했다. Python에 익숙하지 않고 크롤링을 시도해본 적이 없어서 생각보다 어려운 과정이 되고 있다.
import random
import time
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.options import Options
from openpyxl import Workbook
필요한 라이브러리 import
wb = Workbook()
ws = wb.active
ws.append(['ID', 'URL', 'name', 'images', 'main_category', 'sub_category', 'content', 'price'])
끌어온 데이터를 엑셀파일에 저장하기 위해 엑셀파일을 생성하고 열 제목을 추가해준다.
ser = Service('./chromedriver/chromedriver.exe')
chrome_options = Options()
chrome_options.add_argument("--headless")
browser = webdriver.Chrome(chrome_options, ser)
listUrl = 'https://prod.danawa.com/list/?cate=112758&15main_11_02'
browser.get(listUrl)
웹 드라이버를 설정하고 Chrome 웹 브라우저에 접속한다. headless 모드를 이용하여 코드 실행 후 크롬 웹페이지를 띄우지 않고 접속할 수 있다. 해당 접속 방식은 Python의 Selenium 라이브러리를 이용하였다.
Selenium
Selenium은 웹 애플리케이션을 테스트하고 자동화하는 도구다. 웹 브라우저를 자동으로 조작하여 웹 페이지의 동작을 시뮬레이션하거나 테스트할 수 있다. 셀레늄은 다양한 프로그래밍 언어에서 사용할 수 있으며, 웹 애플리케이션의 동작을 확인하고 검증하는 등의 목적으로 활용된다.
셀레늄의 주요 기능과 특징
1. 웹 브라우저 자동 조작
셀레늄을 사용하면 코드를 통해 웹 브라우저를 자동으로 제어할 수 있다. 클릭, 텍스트 입력, 폼 제출 등과 같은 다양한 동작을 자동으로 수행할 수 있다.
2. 웹 페이지 테스트: 웹 애플리케이션의 기능을 테스트하거나 버그를 찾는 등의 목적으로 셀레늄을 사용할 수 있다. 자동화된 테스트 스크립트를 작성하여 여러 상황에서 애플리케이션이 예상대로 동작하는지 확인할 수 있다.
3. 페이지 로딩 및 요소 대기: 셀레늄은 웹 페이지가 로딩되거나 특정 요소가 나타날 때까지 대기할 수 있는 기능을 제공한다. 이를 통해 웹 페이지의 상태 변화에 따라 동작을 제어하거나 데이터를 추출할 수 있다.
element = browser.find_element(By.CLASS_NAME,"tab_done")
element.click()
time.sleep(3)
해당 페이지 요소는 노트북 페이지의 단종 상품 탭에 접속하는 코드. 이 부분을 해결하기가 상당히 어려웠는데 단종 상품 탭이 Url로 구분된 것이 아니고 페이지 내 탭의 차이로 데이터가 분리되어 있는 상태였다. 방대한 데이터를 위해서 개발자 도구를 이용하여 해당 탭의 이름을 확인하고 이를 클릭하여 단종 상품의 데이터까지 끌어오려고 했는데 어떻게 접근해야할지 고민이 되었고 여러 번의 시행착오 끝에 성공한 이 방법을 선택하게 되었다. 사실 제일 최선의 방법인지는 모르겠지만, 당장 데이터를 긁어오는 게 중요한 것 같다고 생각했다.
current_page = 2
excel_index = 1
try:
while True:
print('===================================================')
print(current_page - 1)
print('===================================================')
html = browser.page_source
soup = BeautifulSoup(html, 'html.parser')
number_wrap = soup.select('.number_wrap')
### 목록의 상세조회 url
aTag = soup.select('div.main_prodlist > ul.product_list > li.prod_item > div > div.prod_info > p > a')
maxCount = len(aTag) - 1
i = 1
for a in aTag:
url = a['href']
images = []
main_category = ''
sub_category = ''
modelName = ''
content_url = ''
browser.get(url)
print(" ========================== ")
html = browser.page_source
soup = BeautifulSoup(html, 'html.parser')
imageArea = soup.select('.thumb_slide > li')
for imageLi in imageArea:
image = imageLi.find('img')
image_url = 'https://'+image.get('src')
images.append(image_url)
image_url_str = ','.join(images)
titleArea = soup.select('#blog_content > div.summary_info > div.top_summary > h3 > span')
title = titleArea[0].text
random_price = random.randint(100000, 1200000)
price = random_price # Format the price with commas
sub_category = title.split()[0]
main_category = (soup.select_one('#blog_content > div.summary_info > div.top_summary > div > div.sub_dsc > div > dl > dd > div > div > a:nth-child(1) > u')
.text)
ws.append([excel_index, url, title, image_url_str, main_category, sub_category, 'N/A', price])
excel_index = excel_index + 1
if i == maxCount:
break
i = i+1
# print(driver.find_element(By.CLASS_NAME, 'number_wrap'))
# a_element = driver.find_element(By.CLASS_NAME, 'number_wrap')
url = 'https://prod.danawa.com/list/?cate=112758&15main_11_02'
browser.get(listUrl)
element = browser.find_element(By.CLASS_NAME,"tab_done")
element.click()
time.sleep(3)
print('======================================')
if(current_page % 10 != 1):
browser.execute_script('javascript:movePage(\'' + str(current_page) + '\')')
time.sleep(3)
else:
a_element = WebDriverWait(browser, 20).until(EC.presence_of_element_located((By.CLASS_NAME, 'edge_nav')))
browser.execute_script('javascript:movePage(\'' + str(current_page) + '\')')
time.sleep(3)
current_page = current_page + 1
except:
wb.save('test.xlsx')
이후 반복문을 통해서 데이터들을 가져오고, 10페이지를 넘어갈 때와 그렇지 않을 때를 구분하여 페이지 버튼을 누르는 동작을 Script로 구분하여 실행시켰다.
페이지가 렌더링 되는 시간을 계산해서 time.sleep을 걸어주는 게 유효했던 크롤링이었다. 사실 크롤링을 직접 해 볼 일이 없어서 어떤 식으로 코드를 작성해야하는지 많이 난항을 겪었다. 그나마 크롤링에 관련해서는 팀원 한 분이 많은 공헌을 하셨기에 이 정도 진행되지 않았나 싶다. 이번 과제에서 나는 기본적으로 작성한 코드 뼈대에 단종상품 탭을 확인하고 가격을 무작위로 배정하는 작업만 진행해서 다 된 밥상에 숟가락만 얹은 기분이다. 뭔가 많이 도움이 못 된 거 같아 스스로 많이 낙담하기도 했다. 그래도 아무것도 안 하고 손을 놓기보다는 어떻게든 같이 공부하고 코드를 작성해보는 태도가 중요하다고 생각하였기에 모르는 것이 있다면 물어보고 스스로 과제를 찾아서 해결해보려고 노력했던 것 같다. 비록 효율은 안 좋았지만..
앞으로의 과제는 끌어온 데이터들을 어떻게 DB에 넣을지에 대한 고민이 필요하다. 지금 방식으로 데이터를 집어넣기엔 소요시간도 너무 많고 데이터들이 제대로 들어가는지 확인할 수도 없다. 쓰레드를 이용한 병렬처리가 필요할 거 같은데 이 부분에 대해서는 추가적인 코드 작성이 필요할 듯 하다.