본문 바로가기
카테고리 없음

[동적 웹페이지 크롤링, selenium] Kream에서 브랜드 별 인기상품 크롤링

by 호메로스 2024. 7. 9.

 

웹 크롤링을 공부하면서 네이버 쇼핑의 상품들을 크롤링하는 것을 보고,

필자는 Kream이라는 사이트에서 크롤링을 적용해보면 좋을거 같아 내용을 정리해본다.

 

Kream은 여러 브랜드 상품들을 리셀 거래하는 사이트이다.

Kream에서 먼저 '나이키'를 검색하고 '인기순'으로 정렬하면 다음과 같은 모습을 볼 수 있다.

 

 

이 때 웹사이트 주소는 "https://kream.co.kr/search?keyword={브랜드 이름}&tab=products&sort=popular_score"의 형태를 띈다.

만약 'adidas'를 검색한다면 브랜드 이름 란에 adidas가 들어가는 것을 확인할 수 있을 것이다.

또한 'sort=popular_score'는 인기순으로 분류해서 나타난다고 짐작할 수 있다.


이번에는 웹페이지가 어떻게 구성되어 있는지 확인해보겠다.

 

크롬에서 마우스 우클릭하고, '검사'를 누르면 해당 웹페이지가 어떻게 짜여있는지 볼 수 있다.

 

그럼 이런식으로 화면이 보일텐데, 표시해놓은 것을 누르고 웹페이지를 이리저리 드래그 해보면 어떤 코드가 어떤 요소를 나타내는지 알 수 있다.

 

 

그렇다면 class='search_result_list'안에 class="search_result_item product"로 표시된 것들 하나가 한 상품을 표시한다는 것을 알 수 있다. 그리고 웹페이지의 스크롤을 내리다보면 상품들이 늘어난다는 것을 볼 수 있다. 이는 곧 상품들을 크롤링하려면 스크롤을 내려야하므로 동적 크롤링을 해야한다는 것을 의미한다.


필자는 '제품명', '가격', '거래량', '위시량', '리뷰수' 를 크롤링 하기로 했다. 그래서 각 요소들은 어떤 class에 담겨있는지 확인해봤다. 위 사진에서 나열된 상품들을 하나씩 클릭하면서 살펴봐도 좋고, ctrl+shift+C를 통해 아까처럼 코드를 찾게해도 된다.

 

각 코드들을 살펴본 결과 각 속성별 class는 다음과 같았다.

'제품명'의 class = 'translated_name'

 


'가격'의 class = 'amount'


'거래량'의 class = 'satus_value'


'위시량'의 class = 'text'


'리뷰수'의 class = 'text'

 

문제가 생겼다. '위시량'의 class와 '리뷰수'의 class가 'text'로 같은 것을 볼 수 있다. 이 요소들을 컴퓨터가 문제없이 찾아내게 하기 위해 필자는 '위시량'의 class는 class = 'wish_figure' 안에서 'text' 클래스를 찾고, '리뷰수'의 class는 class = 'review_figure' 안에서 찾았다.


필요한 정보들은 모두 조사했으니, 이번에는 코드를 어떻게 구성되었는지 보자. 동적 크롤링을 진행하기 위해서 'selenium'을 이용했다.

 

# 라이브러리 정의
import requests
import bs4
import pandas as pd
from selenium import webdriver
import time
from tqdm.notebook import tqdm
import warnings
warnings.simplefilter('ignore')
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException

 

먼저 필요한 라이브러리들을 정의했다.

 

query = 'nike' # 다른 브랜드들에도 적용할 수 있도록

titles = [] # 상품명을 넣을 리스트
prices = [] # 가격을 넣을 리스트
transactions = [] # 거래량을 넣을 리스트
wishFigures = [] # 위시량를 넣을 리스트
reviewCounts = [] # 리뷰수를 넣을 리스트

driver = webdriver.Chrome()

page_url = f"https://kream.co.kr/brands/{query}?sort=popular_score" 
driver.get(page_url) # webdriver로 해당 페이지에 들어감
time.sleep(1) # 페이지가 로딩중인데 다음 코드가 실행되는 것을 방지

for scroll_down in range(10):
	# 가상 페이지에서 스크롤을 동작하는 코드. 여기서는 10번의 스크롤을 동작한다.
    driver.execute_script('window.scrollTo(0, document.body.scrollHeight);')
    time.sleep(0.5)

list_basis = driver.find_element(By.CLASS_NAME, "search_result_list") 
# 상품들을 모아놓은 리스트를 list_baiss로 선언
product_list = list_basis.find_elements(By.CLASS_NAME, "product_card")
# list_basis에서 각 상품에 해당하는 객체들을 product_list 안에 넣어둠.

for item in tqdm(product_list): # 각 상품에 해당하는 객체들에 대해서 크롤링

    
    transaction = item.find_element(By.CLASS_NAME, "status_value").text #'거래량' 찾기
    # 숫자만 뽑기 위해서 숫자가 아닌 이외의 것들 다 수정
    transaction = transaction.replace("거래 ", "")
    transaction = transaction.replace(',', '')
    transaction = transaction.replace(".", "")
    transaction = transaction.replace("만", "0000") 
    # 만으로 표시된 것들을 천의 자리까지 무시하고 10000을 곱함
    transactions.append(transaction) # 거래량 리스트에 추가

    title_element = item.find_element(By.CLASS_NAME, "translated_name") # '상품명' 찾기
    titles.append(title_element.text) # 상품명 리스트에 추가

    price_element = item.find_element(By.CLASS_NAME, "amount").text[:-1].replace(',', '') # '가격' 찾기
    prices.append(price_element) # 가격 리스트에 추가
	
    # 'item' 중에서 'wish_figure'에 해당하는 객체 찾기
    wish = item.find_element(By.CLASS_NAME, 'wish_figure')
    
    # 'wish_figure'에서 'text' 클래스, 즉 '위시량' 찾기
    wishFigure = wish.find_element(By.CLASS_NAME, 'text').text
    wishFigure = wishFigure.replace(',', '')
    wishFigure = wishFigure.replace(".", "")
    wishFigure = wishFigure.replace("만", "0000")
    wishFigures.append(wishFigure)
	
    
   	# 마찬가지로 'item' 중에서 'review_figure'에 해당하는 객체를 찾고 거기서 'text' 클래스를 찾아 '리뷰수'를 찾음
    try: # '리뷰수'가 0인 경우 오류가 떠서 try-except로 예외를 처리함.
        review = item.find_element(By.CLASS_NAME, 'review_figure')
        reviewCount = review.find_element(By.CLASS_NAME, 'text').text
        reviewCount = reviewCount.replace(',', '')
        reviewCount = reviewCount.replace(".", "")
        reviewCount = reviewCount.replace("만", "0000")
        reviewCounts.append(reviewCount)
    except NoSuchElementException:
        reviewCounts.append('0') # '리뷰수' 정보가 없으면 '0'으로 저장.
    
driver.quit() # 'webdriver'종료

 

필자가 코드를 실행했을 때는 각 속성에 해당하는 리스트 크기들이 '550'개씩 들어가있다고 나왔다.


이번에는 정리한 리스트들을 data frame으로 정리해보자.

result = pd.DataFrame({"제품명" : titles,
                      "가격" : prices,
                      "거래량" : transactions,
                      "위시량" : wishFigures,
                      "리뷰수" : reviewCounts})
result

 

실행해보면 다음과 같은 결과가 나타난다.

 

다음 코드로 엑셀로 저장할 수도 있다.

result.to_excel(f'cream_shopping({query}).xlsx', index=False)