Python/Crawling

쿠팡 _ 특정 상품 가격 크롤링(BeautifulSoup)

hihih 2021. 9. 10. 22:04
728x90

selenium 대신 BeautifulSoup를 사용하게 된 이유

 

[Python/Crawling] - 쿠팡 _ 특정 상품 가격 크롤링(selenium)

 


BeautifulSoup

 

튜터님에게 쿠팡 상품 크롤링 예시를 받고 이를 참고하여 작성했다.

엑셀은 필요없어서 주석처리 했다.

 

import urllib.request as req
from bs4 import BeautifulSoup
import os
import openpyxl
import requests

# if not os.path.exists("./쿠팡_크롤링.xlsx"):
#     openpyxl.Workbook().save("./쿠팡_크롤링.xlsx")
#
# book = openpyxl.load_workbook("./쿠팡_크롤링.xlsx")
# sheet = book.active

page_num = 1
while True:
    code = requests.get("https://www.coupang.com/np/categories/118923?listSize=120&brand=&offerCondition=&filterType=&isPriceRange=false&minPrice=&maxPrice=&page={}".format(page_num),headers={"User-Agent":"Mozilla/5.0"})
    soup = BeautifulSoup(code.text, "html.parser")
    title = soup.select("div.name")
    price = soup.select("strong.price-value")
    if len(title) == 0: # 끝 페이지까지 크롤링 모두 완료했다면?
        break
    row_num = 1
    for i in range(len(title)):
        print(title[i].string.strip(), price[i].text.strip())
        # sheet.cell(row=row_num, column=1).value = title[i].string.strip()
        # sheet.cell(row=row_num, column=2).value = price[i].text.strip()

        #book.save("./쿠팡_크롤링.xlsx")
        row_num += 1
    page_num += 1

 

 

 

 

코드 살펴보기

code = requests.get("https://www.coupang.com/np/categories/118923?listSize=120&brand=&offerCondition=&filterType=&isPriceRange=false&minPrice=&maxPrice=&page={}".format(page_num),headers={"User-Agent":"Mozilla/5.0"})

 

서버에서 봇으로 인지하고 원하는 정보를 주지 않고 차단한 경우

headers 정보에 User-Agent를 넣어주면 된다.

 

req.urlopen() 함수 말고, requests.get()을 사용해보면 될 것이라는 조언이 있어서

위 예시 코드처럼 사용했더니 Access Denied 화면은 나오지 않았다.

 

 

 

 

code가 아닌 code.text를 한 이유?

더보기
더보기

 

soup = BeautifulSoup(code.text, "html.parser")

 

req.urlopen() 함수와 requests.get() 함수는 그 기능은 거의 같지만, 반환값이 살짝 다르다.

req.urlopen() 함수의 경우 그 반환값을 변수 code로 받았을 때,

그걸 그냥 그대로 BeautifulSoup()에 넣어줘도 BeautifulSoup()가 그 변수 code에 있는 'HTML 코드'를 꺼내가지만

 

requests.get() 함수는 그 반환값을 변수 code로 받았을 때, 그걸 그냥 그대로 BeautifulSoup()에 넣어주면 

BeautifulSoup()가 그 변수 code에 있는 'HTML 코드'를 꺼내가지 못한다.

code.text 까지 적어줘야지만, 'HTML 코드'를 꺼내가게 된다.

 

이유라고 한다면 req.urlopen() 를 만든 개발자와 requests.get() 을 만든 개발자가 그렇게 개발을 했기 때문?

requests.get() 함수의 경우, 변수 code 뒤에 .text ← 얘를 붙여줘야하는 이유에 대해선 그 개발자만 알 것이다.

 

그냥 이렇게 기억하면 된다.

"req.urlopen()  requests.get() 은 같은 기능을 하지만, 차이점은 BeautifulSoup()에 변수 code를 넣어줄 때,

.text붙이냐 안붙이냐의 차이가 있다."

 

 

 

 

이번처럼 req.urlopen()를 사용했을 때 에러가 뜨는 경우만 requests.get()을 사용하는가?

더보기
더보기

 

→ 무조건 requests.get() 을 써도 완전 무방하다.

어떤 것을 쓰던 상관은 없다. 그저 각자 개인 취향이다.

requests.get() 이 조금 더 성능이 좋다? 라고 적혀있는게 있어

지금처럼 urlopen() 으로 안되는 경우에 requests.get() 으로 해결이 되는 경우가 아주 가끔 있다.

계속 req.urlopen() 만을 써도 되고 requests.get() 만을 써도 상관없다.

 

 

 

 

 

 

코드 작성하기 

selenium 에서 사용한 .send_keys()를 사용했더니 에러가 또 떠서 가격 범위를 지정하는 대신

크롤링 후에 가격의 글자수가 자릿 수 구분 쉼표(,) 포함 5글자가 되는 경우를 크롤링 하였고

(4,000 ~ 5,000원 대)

 

if len(price[a].text) == 5 :

 

 

 

여기서 내가 원하는 가격대는 4 ~ 5000원대 이므로 가격의 앞자리가 4와 5인 경우만 출력되게 하였다.

            if (price[a].text)[0:1] == "4":

            if (price[a].text)[0:1] == "5":

 

 

4000원대 상품이 나오게 되면 해당 사이트로 바로 들어갈 수 있도록 사이트 주소를 출력시켰고

list index out of range 에러가 자주 출력되서 일단 실행시키는 try ~ except 구문을 활용해

에러가 뜨지 않게 했다.

 

 

 

BeautifulSoup 전체 코드

더보기
더보기

 

  • 해당 코드는 line api를 이용한 봇을 만들기 위해 메모장을 활용해 실행했다.
  • 주석 처리 된 부분을 참고하면 메모장을 활용하지 않고 실행할 수 있다.

 

import urllib.request as req
from bs4 import BeautifulSoup
import urllib.parse as par  # 한글을 특수한 문자로 변환시켜 주기 위해서
import requests


keyword = "오그래놀라팝"
encoded = par.quote(keyword) # 한글 -> 특수한 문자


code = requests.get("https://www.coupang.com/np/search?rocketAll=false&q={}".format(encoded), headers={"User-Agent" : "Mozilla/5.0"})
# 신원의 정보 입력

soup = BeautifulSoup(code.text, "html.parser") 
print("쿠팡에서 오그래놀라팝을 검색 중입니다. ")
#print(code.text)



f = open("4000.txt", "w")
ff = open("5000.txt", "w")


product = soup.select("div.descriptions-inner > div.name")
price = soup.select("em.sale > .price-value")


print()
print("4 ~ 5000원대 제품 크롤링 완료")
print()

a = 0

for a in range(len(product)) :
    try:
        if len(price[a].text) == 5 :
  
            if (price[a].text)[0:1] == "4": # 4,000원대의 상품 가격이 나오면 링크가 같이 출력되게함
                f.write(product[a].string + "\n" + price[a].text + "\n"+"\n")
                print(
                    "해당 사이트로 접속하기 >> https://www.coupang.com/np/search?q=%EC%98%A4%EA%B7%B8%EB%9E%98%EB%86%80%EB%9D%BC%ED%8C%9D&channel=recent")
                # print(product[a].string)
                # print(price[a].text)
                # print()

            if (price[a].text)[0:1] == "5":
                ff.write(product[a].string + "\n" + price[a].text + "\n"+"\n")
                # print(product[a].string)
                # print(price[a].text)
                # print()
        else :
            pass
        a += 1

    except :
        pass


f = open("4000.txt", "r", encoding='cp949')
rd = f.read()
print("4000원대 상품 : ", rd , "\n")

ff = open("5000.txt", "r", encoding='cp949')
rrd = ff.read()
print("5000원대 상품 : \n" + rrd)

 

 

크롤링 예시

 

 

 

 

 

 

 

 


error


encoding='utf-8'

유니코드나 한글 파일을 읽으려고 할 때 아래와 같은 방식을 사용하는데

f = open("4000.txt", "r", encoding='utf-8')
ff = open("5000.txt", "r", encoding='cp949')

 

encoding='utf-8'를 사용해 실행했더니 아래와 같은 오류가 떴다.

'utf-8' codec can't decode byte 0xbf in position 0: invalid start byte

 

 

구글링을 통해 아래로 변경했더니 오류 없이 실행되었다.

f = open("4000.txt", "r", encoding='cp949') 
ff = open("5000.txt", "r", encoding='cp949')

 

 

Q&A .

encoding='utf-8' 대신 encoding='cp949'으로 우선적으로 써도 상관없을까?

 

인코딩 관련 에러가 났을 때엔,

"utf-8" / "cp949" / "euc-kr" 중에 하나 넣으면 해결이 보통 90%이상 된다는 것만 알고 있어도,

코딩하는데엔 별 문제가 없다.

 

 

 

728x90