today_is

[ spring 프로젝트 ] 파이썬 크롤링 본문

project

[ spring 프로젝트 ] 파이썬 크롤링

ye_rang 2024. 3. 8. 11:46

주제 :    편의점 웹 사이트 만들기 (spring MVC 프로젝트)


 

  [ 기획단계  -  UI프로토타이핑 담당 ]  

: draw.io 이용

 

 

대학교 과제에서도 PPT , 문서작성 등을 담당했었기 때문에, 디자인에 대한 부담감이 덜했다.

 

팀원들을 위해서 요소 배치까지 고려해서 기획했다.

 

여러 웹 사이트를 참고하면서 약 20페이지 정도 만들었다.

 

내가 만든 UI프로토타이핑 중 일부분을 가져왔다

 

 


 

  [ 데이터 수집 담당  -  데이터 크롤링 ]  

 

GS25 웹 사이트를 구현하기 위해서는

상품 데이터와 매장 데이터, 총 2개가 필요했다.

 

 

 

 

 1) 상품 데이터 추출 

:  파이썬 크롤링 

 

 

[ 웹 페이지 로드 ]

1 ) 목록 페이지(url)를 열기

2 ) 전체 상품을 먼저 펼쳐 놓기

목록 페이지에 "더보기" 버튼이 없어질때까지 더보기 버튼 클릭(총 5개있음) 

2번 과정이 없으면, 현재 페이지 내에 있는 요소들만 추출됨

 

[ 반복되는 부분 ]

3 ) 목록 페이지에 있는 각각을 클릭하여, 각 상품의 상세페이지에서 원하는 정보를 추출해서 append

4) 다시 목록 페이지로 돌아감

 

[ 파일로 저장 ]

4) 모든 목록을 다 훑었다면, append 했던 값들을 출력하여 확인해보기

5) 해당 결과값을 xlsx로 저장 

 

 

나는 파이썬을 사용한 workspace 경로를 알고 있어서 

별도로 파일저장 경로를 지정하지 않았지만,

 

파일 저장 경로도 지정 가능하다!

 

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from bs4 import BeautifulSoup
import requests
import time
from pprint import pprint
import json
from selenium.common.exceptions import NoSuchElementException

# Selenium WebDriver 설정 (Chrome을 사용하는 경우)
driver = webdriver.Chrome()

#  cu 상품 페이지 URL
url = 'https://cu.bgfretail.com/product/product.do?category=product&depth2=4&sf=N'

# 웹페이지 열기
driver.get(url)

# 페이지가 완전히 로드될 때까지 기다립니다. 필요에 따라 시간 조정
# time.sleep(30)
driver.implicitly_wait(360)  # seconds

# 더보기 추가하기
btnList = driver.find_elements(By.CSS_SELECTOR, 'div.prodListBtn-w > a')
idList = []

for i in range(4):
    # "더보기" 버튼이 있는지 확인하고, 있으면 클릭합니다.

    # 스크롤 다운하여 "더보기" 버튼이 화면에 보이게 합니다. (선택적)
    driver.execute_script("arguments[0].scrollIntoView();", btnList[0])

    # 버튼 클릭
    btnList[0].click()

    # 클릭 후 페이지가 로드될 때까지 잠시 대기
    time.sleep(1)  # 필요에 따라 대기 시간 조정

    btnList = driver.find_elements(By.CSS_SELECTOR, 'div.prodListBtn-w > a')


linkList = driver.find_elements(By.CSS_SELECTOR, 'div.prodListWrap > ul > li div.prod_img')
for link in linkList:
    t1 = link.get_attribute('onclick')
    t1 = t1.lstrip('view(')
    t1 = t1.rstrip(');')
    idList.append(t1)
print(idList)
detailUrl = 'https://cu.bgfretail.com/product/view.do?category=product&gdIdx='


products = []
print('start bs')
for i in range(len(idList)):
    print('\r' + str(i), end='')
    id = idList[i]
    response = requests.get(detailUrl + id)
    html = response.text
    soup = BeautifulSoup(html, 'html.parser')
    name = soup.select_one('div.prodDetail-e > p').text
    price = soup.select_one('div.prodDetail-e > .prodInfo dd.prodPrice > p > span').text
    img = 'https:' + soup.select_one('div.prodDetail-w > img')['src']
    detail = soup.select_one('div.prodDetail-e > .prodInfo  ul').text
    products.append({'name': name, 'price': price, 'img' : img, 'detail' : detail})



# 추출된 상품 목록 출력
for product in products:
    print(f"상품 이름: {product['name']}, 가격: {product['price']}, 이미지 : {product['img']}, 상품 설명 : {product['detail']}")

# input('Press ENTER')
driver.quit()


# 데이터프레임 생성
df = pd.DataFrame(products)

# Excel 파일로 저장
excel_filename = 'GS25_Products.xlsx'
df.to_excel(excel_filename, index=False, engine='openpyxl')

print(f'{excel_filename}로 저장되었습니다.')

 

 

 어려웠던점 + 느낀 점 

1 ) NoneType 에러

 

로직은 이해가 됐었는데, 정확히 어떤 데이터만 불러와야하는지가 헷갈려서 오래걸렸다.

 

개발자도구를 봐도, list안에 ul에 ol에... 마구잡이로 나열되어 있어서 더 찾기 어려웠던 것 같다.

 

css 선택자에 대해서 어느정도 익숙해졌다 생각했었는데, 자꾸 NoneType 에러가 떴다.

 

 

->    콘솔로 출력해보면서, 원하는 데이터인지 먼저 파악한 후에 웹사이트로 실행했더니, 좀 더 수월하게 파악할 수 있었다.

 

 

 

 

 

2 ) 코드의 비효율적인 부분 개선

: 개선하여, 한번의 실행으로 400개의 데이터 추출 가능 ( 효율성 10배 상승 )

 

개선 전 개선 후
40개 400개

 

 

 

이 코드를 추가하여서 효율성을 높일 수 있었다.

 

"더보기" 버튼에 숨어있는 li 들을 펼쳐서 크롤링하는 것이다. 

for i in range(4):
    # "더보기" 버튼이 있는지 확인하고, 있으면 클릭합니다.

    # 스크롤 다운하여 "더보기" 버튼이 화면에 보이게 합니다. (선택적)
    driver.execute_script("arguments[0].scrollIntoView();", btnList[0])

    # 버튼 클릭
    btnList[0].click()

    # 클릭 후 페이지가 로드될 때까지 잠시 대기
    time.sleep(1)  # 필요에 따라 대기 시간 조정

    btnList = driver.find_elements(By.CSS_SELECTOR, 'div.prodListBtn-w > a')

 


 

2 ) 매장 데이터 추출 

 :  Excel 데이터 필터링

 

 

 

 

공공데이터 포털을 이용했다.

 

우리 팀은 DB에 데이터를 담아두고 사용할 것이기 때문에

DB 테이블에서 사용할 데이터만 남겨두고 다 지웠다. 

 

또한 데이터 개수도 너무 많기 때문에

5개의 구/군 편의점 데이터 중에서 10개씩만 추출했다

 

 

부산시 내에 있는 전체 편의점 목록

 

 

 

 결과물 

pnum (매장전화번호) 컬럼은 기존 데이터에 존재하지 않아서

 

ChatGPT 를 이용하여

051-0000-0000 형식으로 랜덤한 숫자를 만들어서 추가했다.

 

매장명 / 도로명 주소 / 전화번호 / 위도 / 경도

 

 


 

 

역할 분배

 

우리가 구현할 페이지를 토대로, 기능을 총 5가지로 나누었다. (5명)

 

불만이 생기지않도록 우리는 운에 맡기기로 했다. 

 

랜덤으로 사다리를 탔는데

 

나는 비교적 쉬운 회원테이블 파트를 담당하게 됐다.

 

실제로 역할 분담한 증거 ㅎㅎ

 

 

 

 내가 구현해야하는 주요기능  

1) 회원가입 : 아이디 중복 불가 기능 

2) 로그인 : 이메일 인증 기능 / Hash를 이용한 비밀번호 재설정 기능

3) 마이페이지 : 내가 쓴 후기 

 

 

 

다짐 

모든지 적극적으로 임하자.

짐이 되는 팀원이 되지말자