클릭 한 번으로 쿠팡 상품 정보 끄집어내기! ChatGPT로 완성하는 크롤링 봇

ChatGPT와 함께 시작하는 쿠팡 크롤러 봇 개발! 검색 결과에 노출된 상품 정보를 가져오기 위한 프롬프트 작성과 디버깅 과정을 단계별로 설명합니다. 프롬프트 고려 사항부터 광고 상품 제외, URL 조작까지 자세한 코드 예시와 함께 알려드립니다.

0. 개요

요즘은 ChatGPT 덕에 정말 개발하기 쉬워진 세상입니다. 과연 크롤링 봇도 ChatGPT와 함께라면 쉽게 만들어 낼 수 있을까요? 쿠팡 크롤링 봇 개발하기(with ChatGPT), 시작해 보겠습니다.
 
 

1. 프롬프트 작성

1.1. 목표

검색 결과로 노출되는 상품 리스트에서 각 상품의 기본적인 정보를 가져오고자 합니다.
  • 상품 이름
  • 정가
  • 판매가
  • 별점
  • 리뷰 개수
  • 카드 할인 정보
  • 적립 정보
  • 배송 정보
 

1.2. 상품 리스트 HTML 찾기

상품 리스트를 담고 있는 HTML 요소를 찾아봅시다
 
notion image
id가 productListul이 상품 리스트이며, 그 안에 있는 각각의 li가 상품 요소인 것을 볼 수 있습니다.
그렇다면 위 ul HTML을 복사해서 ChatGPT에 물어볼까요?
 

1.3. HTML 크기 축소

 
notion image
ChatGPT는 토큰 수 제한이 있어 위의 ul과 같이 너무 큰 HTML은 처리할 수가 없습니다. HTML의 크기를 줄여야 하므로 ul의 첫 번째 li HTML을 복사해 다시 물어보겠습니다.
 

1.4. 프롬프트 고려 사항

프롬프트를 작성하기 전에, 어떤 것을 고려해야 할지 정리해 보겠습니다.
1️⃣ 상품 리스트 내 모든 li에 대하여 반복
//ul[@id="productList"] 안에 있는 모든 상품들을 수집하도록 합니다.
2️⃣ 광고 상품 제거
notion image
class에 search-product__ad-badge가 포함되어 있는 경우 광고 상품이므로 해당 상품까지 수집되지 않도록 합니다.
 

1.5. ChatGPT에 프롬프트 작성하기

GPT-4를 사용하였으며 프롬프트는 다음과 같이 입력했습니다.
<<li의 HTML>> 상품 정보를 담고 있는 위의 HTML에서 상품 이름, 가격, 정가, 별점, 리뷰 개수, 카드 할인, 적립 정보, 배송 정보를 가져오는 웹 크롤러를 셀레니움을 사용하여 만들어 줘. 이 li 요소는 "//ul[@id='productList']" 안에 있으므로, 상품 리스트를 가져와 그 안의 li 태그에 대해 수집 작업을 반복하도록 코드를 생성해 줘. class에 search-product__ad-badge가 포함되어 있는 경우 광고 상품이므로 해당 상품은 스킵해. 수집 내용을 콘솔에 출력할 때 각 상품마다 서로 구별될 수 있도록 구분선을 추가해 줘.
 

1.6. ChatGPT 코딩 결과물

## import 및 크롬 드라이버 설정 생략 ## # 웹페이지 열기 driver.get('<https://www.coupang.com/np/search?component=&q=%EC%95%84%EC%9D%B4%ED%8C%A8%EB%93%9C+%EC%97%90%EC%96%B4+5&channel=auto>') # 상품 리스트 가져오기 product_list = driver.find_element(By.XPATH, "//ul[@id='productList']") # BeautifulSoup으로 파싱 soup = BeautifulSoup(product_list.get_attribute('outerHTML'), 'html.parser') # 광고 상품 제외하고 각 상품 정보 가져오기 for product in soup.find_all('li', class_='search-product'): # 광고 상품인 경우 스킵 if product.find(class_='search-product__ad-badge'): continue product_name = product.find("div", class_="name").text.strip() original_price = product.find("del", class_="base-price").text.strip() sale_price = product.find("strong", class_="price-value").text.strip() rating = product.find("em", class_="rating").text.strip() review_count = product.find("span", class_="rating-total-count").text.strip() card_discount = product.find("span", class_="ccid-txt").text.strip() if product.find("span", class_="ccid-txt") else "N/A" reward_info = product.find("span", class_="reward-cash-txt").text.strip() if product.find("span", class_="reward-cash-txt") else "N/A" delivery_info = product.find("span", class_="arrival-info").text.strip() # 콘솔에 출력 print('-'*40) print("상품 이름:", product_name) print("정가:", original_price) print("판매 가격:", sale_price) print("별점:", rating) print("리뷰 개수:", review_count) print("카드 할인 정보:", card_discount) print("적립 정보:", reward_info) print("배송 정보:", delivery_info) driver.quit()
 
 

2. 디버깅 🔨

ChatGPT가 만들어 준 코드를 실행시키면 높은 확률로 정상실행이 되지 않을 수 있습니다. 이번 예시 역시 곧바로 아래의 에러를 만나게 되었습니다:
Traceback (most recent call last): File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/pydevconsole.py", line 364, in runcode coro = func() File "<input>", line 13, in <module> AttributeError: 'NoneType' object has no attribute 'text'
이제부터 디버깅 과정을 보여드리겠습니다.
 

2.1. AttributeError: 'NoneType' object has no attribute 'text'

 
⚠️ 문제 상황
상품 이름을 가져오는 코드를 확인해봅니다.
product_name = product.find("div", class_="name").text.strip()
상품 이름을 가진 요소를 찾아 텍스트를 가져오는 코드입니다. 그런데 만약 class가 "name"div를 찾지 못한다면 product.find("div", class_="name")는 'NoneType' object가 됩니다. None에서는 텍스트를 가져올 수 없기 때문에 AttributeError가 발생하게 되는 것입니다.
 
✅ 에러 해결
케이스를 나눠서 해결해 봅시다.
notion image
notion image
위 사진에서 볼 수 있듯, 상품마다 정보량 차이가 나게 됩니다. 때문에 우리는 정보의 종류를 두 가지로 나누려고 합니다.
 

☝️ 꼭 있어야 하는 정보

상품명, 가격과 같이 필수로 존재해야 하는 정보가 None이어서는 안되겠죠. 이러한 것이 None이라면 에러를 발생시켜야 합니다.
try: # 요소를 찾음 product_name = product.find("div", class_="name").text.strip() except AttributeError: # 에러 발생 시 raise raise AttributeError("상품 이름을 가져오는 중 에러가 발생했습니다.")

✌️ 없어도 되는 정보

리뷰 개수와 같이 없어도 되는 정보에는 다른 처리가 필요합니다. 정보를 담고 있는 요소를 찾지 못했을 경우 변수에 None을 할당하도록 하겠습니다.
# 요소를 찾음 review_count_span = product.find("span", class_="rating-total-count") # 요소가 None이라면 변수에 None 할당 review_count = review_count_span.text.strip() if review_count_span else None
이렇게 한 줄로 쓸 수도 있겠죠:
review_count_span = product.find("span", class_="rating-total-count").text.strip() if product.find("span", class_="rating-total-count") else None
 

2.2. 광고 상품 건너뛰기

프롬프트에 이러한 내용이 있었습니다.
class에 search-product__ad-badge가 포함되어 있는 경우 광고 상품이므로 해당 상품은 스킵해.
이것을 보고 ChatGPT는 다음과 같은 코드를 생성했습니다:
for product in soup.find_all('li', class_='search-product'): # 광고 상품인 경우 스킵 if product.find(class_='search-product__ad-badge'): continue # ...후략
⚠️ 문제 상황
위 코드에서는 class가 'search-product__ad-badge'인 요소를 찾게되면 스킵하도록 되어 있습니다. 해당 클래스를 product의 클래스에서 찾아야 하는데, 문제는 이것을 product 내부 요소에서 찾는다는 것입니다. 아마 이것은 제가 프롬프트를 정확하게 작성하지 않아 발생한 문제인 것 같지만, 해결해보도록 하겠습니다.
✅ 해결
if 'search-product__ad-badge' in product['class']: continue
반복문에서 스킵하는 조건을 위와 같이 바꾸어 product의 클래스에 'search-product__ad-badge'가 포함되어 있도록 수정했습니다.
 
 

2.3. 불완전한 URL 조정

크롤링 봇을 실행한 결과, 아래와 같이 상품 URL을 가져왔습니다.
상품 URL: /vp/products/6396408893?itemId=13659935611&vendorItemId=80912364532&pickType=COU_PICK&q=%EC%95%84%EC%9D%B4%ED%8C%A8%EB%93%9C+%EC%97%90%EC%96%B4+5&itemsCount=36&searchId=c12ac6801b8042dbbe20f91a2e875bc0&rank=1
⚠️ 문제
BeautifulSoup로 HTML을 파싱하여 수집하는 경우, href 속성에 URL이 전체 다 표시되어있지 않을 수 있습니다. 위의 URL과 실제 URL을 비교해 보면 앞 부분에 https://www.coupang.com/가 빠져 있음을 확인할 수 있습니다.
 
✅ 해결
URL의 앞부분을 추가해주도록 합니다.
product_url = "https://www.coupang.com" + product.find('a', class_='search-product-link')['href']
 
 

3. 수집 결과 확인

수정된 크롤링 봇으로 수집된 데이터를 확인해 봅시다.
---------------------------------------- 상품 URL: <https://www.coupang.com//vp/products/6396408893?itemId=13659935634&vendorItemId=80912364549&q=%EC%95%84%EC%9D%B4%ED%8C%A8%EB%93%9C+%EC%97%90%EC%96%B4+5&itemsCount=36&searchId=e5fbb32250614118b4a65b814ec1b9ae&rank=10> 상품 이름: Apple 2022 아이패드 에어 5세대, 스타라이트, 256GB, Wi-Fi 정가: 1,169,000 판매 가격: 1,098,860 별점: 5.0 리뷰 개수: (17833) 카드 할인 정보: 최대 1% 카드 즉시할인 적립 정보: 최대 50,000원 적립 배송 정보: 내일(목) 도착 보장 ---------------------------------------- 상품 URL: <https://www.coupang.com//vp/products/1947960654?itemId=5913484902&vendorItemId=83384415356&sourceType=SDW_TOP_SELLING_WIDGET_V2&searchId=e5fbb32250614118b4a65b814ec1b9ae&q=아이패드> 에어 5 상품 이름: 케이안 애플펜슬 수납 마그네틱 태블릿PC 커버 매트 폴리오 케이스, 차콜그레이 정가: 33,150 판매 가격: 30,960 별점: 5.0 리뷰 개수: (6498) 카드 할인 정보: 최대 4% 카드 즉시할인 적립 정보: 최대 1,548원 적립 배송 정보: 내일(목) 도착 보장 ---------------------------------------- 상품 URL: <https://www.coupang.com//vp/products/5540742883?itemId=8726176862&vendorItemId=79466342181&sourceType=SDW_TOP_SELLING_WIDGET_V2&searchId=e5fbb32250614118b4a65b814ec1b9ae&q=아이패드> 에어 5 상품 이름: 신지모루 애플펜슬 수납 아이패드 클리어 케이스 + 강화유리 2P, 웜그레이 정가: 26,900 판매 가격: 25,900 별점: 5.0 리뷰 개수: (1313) 카드 할인 정보: 최대 2% 카드 즉시할인 적립 정보: 최대 1,295원 적립 배송 정보: 내일(목) 도착 보장
검색 결과에 노출되는 상품만 수집하는 간단한 구조의 크롤링 봇이지만, 꽤 많은 정보를 얻을 수 있음을 확인할 수 있습니다!
 
 

4. 결론

지금까지 우리는 ChatGPT를 이용하여 쿠팡의 검색 결과를 크롤링하는 과정을 살펴보았습니다. ChatGPT는 굉장히 유용한 도구이지만, 약간의 디버깅과 수정이 필요했습니다. 그래도 우리는 꽤 사용할 만한 결과를 얻을 수 있었습니다.
그러나 쿠팡 검색 결과를 원활하게 수집하기 위해서는 여러 가지를 고려해야 합니다. 쿠팡은 봇 여부를 빠르게 감지하고 차단하기 때문에 이를 효율적으로 우회할 방안이 필요하며, 로그인 여부에 따라 다르게 보이는 정보를 수집하려면 추가적인 작업이 필요합니다. 이러한 제약 사항들은 크롤링 작업을 복잡하게 만들고, 따라서 정확하고 빠른 정보 수집에 어려움을 줄 수 있습니다.
이런 문제들을 효과적으로 해결하려면 전문적인 도구와 서비스가 필요합니다. 해시스크래퍼는 이런 복잡한 문제들을 해결할 수 있는 전문 웹 크롤링 서비스를 제공합니다. 막힘 없이, 그리고 빠르게 쿠팡의 다양한 정보를 수집할 수 있습니다.
이번 포스트를 통해 ChatGPT를 통해 웹 크롤러를 개발하는 방법을 알아보았습니다. 다양한 도구와 방법이 있지만, 가장 효율적이고 정확한 정보 수집을 원한다면 전문적인 서비스를 이용하는 것이 추천드립니다.
감사합니다.