쇼핑몰 상품 상세 페이지 크롤링을 해보았다.
한 번에 모든 코드를 작성해서 돌리면 100% 에러가 나기 때문에 요소 하나마다 테스트를 해줬다. 각 요소가 잘 추출되는 것을 확인하고 한꺼번에 크롤러를 돌렸다.
import csv
import time
from selenium import webdriver
from bs4 import BeautifulSoup
from selenium.webdriver.common.keys import Keys
csv_filename = 'products.csv'
csv_open = open(csv_filename, "w+", encoding = 'utf-8')
csv_writer = csv.writer(csv_open)
csv_writer.writerow(('product_number', 'product_name', 'like', 'product_image_url',
'thumbnail_image_url', 'discount_price', 'original_price', 'material', 'country'))
PATH = "/Users/NAON/myprojects/chromedriver"
driver = webdriver.Chrome(PATH)
driver.implicitly_wait(5)
driver.get("해당url")
htmlsrc = driver.page_source
bs = BeautifulSoup(htmlsrc, "html.parser")
rows = []
def productpage():
elements = driver.find_elements_by_css_selector('.ProductList > li > a')
links = []
for element in elements:
link = element.get_attribute('href')
links.append(link)
for link in links:
driver.get(link)
driver.implicitly_wait(5)
time.sleep(2)
htmlsrc = driver.page_source
bs = BeautifulSoup(htmlsrc, "html.parser")
# 상품명 긁어오기
product_name = bs.find('h1', {'class' : 'ProductDetail__title'}).text
# 좋아요 수 긁어오기
like = bs.find('span', {'class' : 'WishButtonPc__text WishButtonPc__text--middle'}).text
# 상품 상세 이미지 긁어오기
# 한 제품 이미지가 여러 장이지만 각 주소를 ,로 구분해서 한 셀 안에 넣는다.
# 그래야 한 제품 당 한 row만 차지하기 때문
# join은 리스트 요소를 문자열로 꺼내올 수 있는 함수다.
images = bs.find_all('img', {'class' : 'ProductDetailContainer__form__thumbnail__image'})
product_image_url = ",".join([image['data-src'] for image in images])
# 옵션 선택 썸네일 이미지 긁어오기
# 이미지가 style 속성의 값인 background-image로 설정되어있기 때문에
# url만 남기고 긁어올 수 있도록 나머지 텍스트는 split으로 발라내준다.
thumbs = bs.find_all('a', {'class' : 'ProductColor__item'})
thumbnail_image_url = ','.join([thumb['style'].split('background-image: url("')[1][:-3] for thumb in thumbs])
# 가격 긁어오기
# 할인 가격이 있는 상품이 있고 없는 상품이 있기 때문에
# try, except로 케이스를 분기해서 처리했다.
try:
discount_price = bs.find('strong', {'class' : 'ProductDetail__price--sale-price'}).text.replace(",", "")
original_price = bs.find('span', {'class' : 'ProductDetail__price--consumer-price'}).text.replace(",", "")
except:
discount_price = None
original_price = bs.find('strong', {'class' : 'ProductDetail__price'}).text.replace(",", "")
# 소재 긁어오기
# 소재는 상품 페이지 아래 부분에 있으면서
# 스크롤이 가까이 내려가야 비로소 로딩되기 때문에
# 아래 키(↓)를 눌러 스크롤을 내려준다.
body = driver.find_element_by_css_selector("body")
for i in range(200):
keys = body.send_keys(Keys.PAGE_DOWN)
time.sleep(3)
# 스크롤을 내려준 후에야 소스가 로딩되기 때문에 다시 긁어와서 html로 변환해준다.
htmlsrc = driver.page_source
bs = BeautifulSoup(htmlsrc, "html.parser")
material = bs.select_one('.ProductDetailContent__desc > ul:nth-of-type(1) > li:nth-of-type(3) > p').text
# 제조국 긁어오기
country = bs.select_one('.ProductDetailContent__desc > ul:nth-child(1) > li:nth-child(4) > p').text
# 상품번호 긁어오기
product_number_n = bs.select_one('.ProductDetailContent__desc--ul > li:nth-child(1)').text
product_number = " ".join(product_number_n.splitlines()).strip().split(':')[1].strip()
# csv 파일에 넣을 것. 컬럼명 순서와 같게 해준다.
csv_writer.writerow((product_number, product_name, like, product_image_url, thumbnail_image_url, discount_price, original_price, material, country))
productpage()
csv_open.close()
driver.quit()
어려웠던 점, 배운 점
이전에는 for문 안에서 긁어온 모든 소스를 row별로 리스트를 만들었다. 다시 그 리스트 요소를 꺼내서 csv 파일에 한 열씩 넣기 위해 for문 밖에 다른 for문을 썼다. 이번에는 굳이 그럴 필요가 없다는 걸 (다른 팀원 코드를 보고) 알게 되어서 for문 안에서 바로 한 열씩 들어가도록 바꿔줬다.