스크래핑
스크래핑 시작 전 스크래핑의 진행의 구상은 속도면에서 리뷰까지 한번에 가져오는것 보다 나눠서 가져오는것이 빠를것이라는 생각이 들었다.
따라서 category와 product에 들어갈 데이터만 먼저 가져오고(main.py)
이후 review를 가져올 코드를 다시 작성한다.(review.py)
모듈들은 expactor안에 fcmm.py와 mysql.py로 나누어 작성하였다.
스크래핑은 selenium을 활용해 진행하였다.
main.py는
category -> segment -> subsegment를 클릭하도록 만들고 이후 product들을 가져오기 위해 더보기를 클릭하였다.
중간중간 time.sleep을 통해 쉬게 해줬다.
데이터는 dictionary형태로 변환하여 가져오고 이를 mysql에 바로 저장시켰다.
review.py는
fcmm 홈페이지에서 review가 있는 해당 페이지를 찾고 mysql에 저장한 제품코드를 불러와 직접 사이트에 접속했다.
전체 소스코드
https://github.com/RyuMinSu/fcmm-Analysis
GitHub - RyuMinSu/fcmm-Analysis: Scraping and Analysis
Scraping and Analysis. Contribute to RyuMinSu/fcmm-Analysis development by creating an account on GitHub.
github.com
<main.py>
from expactor.fcmm import *
from expactor.mysql import insert_category_tbl
#접속
url = "https://www.fcmm.kr/index.html"
driver = create_driver()
driver.get(url)
driver.maximize_window()
driver.implicitly_wait(5)
CATEGORY_CSS_CLASS = "xans-element-.xans-layout.xans-layout-category.top_category"
navbar = get_object_class(driver, CATEGORY_CSS_CLASS)
navbar_btns = get_objects_tag(navbar, "li") #네비게이션 버튼 가져오기
for index in range(6, len(navbar_btns)-1): #순회하며 정보 가져오기
# for index in range(4,5):
navbar = get_object_class(driver, CATEGORY_CSS_CLASS)
navbar_btn = get_objects_tag(navbar, "li")[index]
#category
cat_btn = navbar_btn.find_element(By.TAG_NAME, 'a')
SEGMENT_CSS_CLASS = "xans-element-.xans-product.xans-product-displaycategory.xans-record-"
if cat_btn.text == "럭키데이":
get_segments(driver, cat_btn, SEGMENT_CSS_CLASS, 1)
elif cat_btn.text == '베스트':
cat_dict = {'category':cat_btn.text, 'segment': '', 'subsegment': ''}
product_dict = {'category':cat_btn.text, 'segment': '', 'subsegment': ''}
print(cat_dict)
# insert_category_tbl(cat_dict) #db저장
move_page(cat_btn) #카테고리로 이동
time.sleep(1)
click_more_btn(driver)
back_page(driver)
soup = create_soup(driver)
get_product_info(soup, product_dict, 0, end_num=-1)
back_page(driver)
elif cat_btn.text == '신상품':
cat_dict = {'category': cat_btn.text, 'segment': '', 'subsegment': ''}
product_dict = {'category': cat_btn.text, 'segment': '', 'subsegment': ''}
print(cat_dict)
# insert_category_tbl(cat_dict) # db저장
move_page(cat_btn) # 카테고리로 이동
time.sleep(1)
click_more_btn(driver) #더보기
back_page(driver)
soup = create_soup(driver)
get_product_info(soup, product_dict, 1)
back_page(driver)
elif cat_btn.text == '프로모션':
cat_dict = {'category': cat_btn.text, 'segment': '', 'subsegment': ''}
product_dict = {'category': cat_btn.text, 'segment': '', 'subsegment': ''}
print(cat_dict)
# insert_category_tbl(cat_dict) # db저장
move_page(cat_btn) # 카테고리로 이동
time.sleep(1)
soup = create_soup(driver)
get_product_info(soup, product_dict, 1)
back_page(driver)
elif cat_btn.text == '라이프스타일 웨어':
get_sub_segments(driver, cat_btn, SEGMENT_CSS_CLASS)
back_page(driver)
elif cat_btn.text == '스포츠 웨어':
get_sub_segments_sports(driver, cat_btn, SEGMENT_CSS_CLASS)
back_page(driver)
else:
get_segments(driver, cat_btn, SEGMENT_CSS_CLASS, 0)
<review.py>
import traceback
from expactor.mysql import read_url, insert_review_tbl
from expactor.fcmm import *
import pandas as pd
import time
import re
#----------------------데이터조회
sql = """
SELECT *
FROM (
SELECT category, segment, subsegment, PRODUCT_CODE, REVIEW_NUM, SUBSTRING_INDEX(URL, '/', 4) AS new_url, ROW_NUMBER() OVER(PARTITION BY PRODUCT_CODE ORDER BY category) AS RNUM
FROM PRODUCT
) S
WHERE S.RNUM=1 AND REVIEW_NUM>=1;
"""
result = read_url(sql) # 튜플
#----------------------시작
driver = create_driver()
df = pd.DataFrame(result, columns=['category', 'segment', 'subsegment', 'product_code', 'review_num', 'new_url', 'rnum'])
print(df.shape)
product_codes = df['product_code'].tolist()
for idx, code in enumerate(product_codes):
review_dict = {'code': code} #데이터저장할 dict
#접속
url = f"https://review5.cre.ma/fcmm.kr/products/reviews?product_code={code}"
print(idx, url)
driver.get(url)
driver.maximize_window()
driver.implicitly_wait(5)
while True:
try:
soup = create_soup(driver) # 박스정보 가져오기
total_rate = soup.select_one('span.products_reviews_summary_v2__score_text').get_text().replace('.0','')
reviews = soup.select("li.js-review-container") #해당 페이지 리뷰들
for review in reviews:
#작성자 평점
writer_rate = review.select_one("div.review_list_v2__score_star span").get_text().split(": ")[1].replace("점","")
#제목
title = review.select_one("div.review_list_v2__score_text").get_text().strip()
#내용(정제)
content = review.select_one(
"div.review_list_v2__message.js-collapsed-review-content.js-translate-text").get_text().strip()
content = re.sub('[^가-힣a-zA-Z0-9 ]','', content)
#작성자
writer = review.select_one(
"div.review_list_v2__user_name_message"
).get_text().strip().split("님의 리뷰입니다.")[0]
if review.select_one("div.review_list_v2__options_section"):
writer_option = review.select_one(
"div.review_list_v2__options_section"
).get_text().strip().replace("\n", ":").replace(":::::", ":::")
else:
writer_option = ""
#데이터 저장
review_dict['total_rate'] = total_rate
review_dict["writer"] = writer
review_dict["writer_rate"] = writer_rate
review_dict["title"] = title
review_dict['content'] = content
review_dict['writer_option'] = writer_option
print(review_dict)
# print(idx, code, total_rate, writer, writer_rate, content,writer_option)
#MySQL데이터 저장
insert_review_tbl(review_dict)
next_btn = get_object_class(driver, "pagination__button.pagination__button--next")
next_btn.click()
time.sleep(.5)
except Exception as ex:
break
최종적으로 category: 35건 / product: 1255건 / review: 11821건의 데이터가 수집되었다.
데이터를 수집하고 보니 db를 수정해야 할것 같다. db수정은 아래의 링크에서 진행하였다.
https://hiphan-mansoorrr.tistory.com/12
fcmm테이블 모델링
hiphan-mansoorrr.tistory.com