독학

[파이썬] 경쟁력있는 상품 소싱방법 : 리뷰 분석 프로그램 (형태소 분석)

JC story 2021. 11. 10. 22:49
반응형

안녕하세요 종백이 입니다~!

오늘은 제가 만든 상품 리뷰 분석 프로그램에 대해 리뷰해 보려고 합니다👏👏👏

 

우선 상품 리뷰 분석 프로그램이란 네이버 쇼핑에 특정 키워드를 치면 상품들이 쭉~ 나오죠?

그 상품들 중에서 1페이지 리뷰를 분석해서 평점이 높은 리뷰 낮은 리뷰 가장 많이 나온 명사, 형용사를 추출해서 보여주는 프로그램입니다. 현재 온라인 쇼핑몰 판매를 하고 있고 리뷰를 분석해서 경쟁력 높은 상품을 소싱하기 위해 만들어 봤습니다 ㅎㅎ (직접 다 보면 오래 걸리니깐요!)

 

주로 사용된 파이썬 모듈은 konlpy와 wordcloud입니다.

konlpy는 한글 문장에서 형태소 단위로 파악하여 명사와 형용사 등등 분석해 주는 모듈입니다. 그리고 wordcloud는 말 그대로 단어들 모음을 언급 순으로 크기로 나누어 보여주는 모듈입니다.

 

우선 konlpy를 사용하기 위한 전 단계가 있는데 과정이 매우 복잡하여 여기 참고 부탁드립니다! 저는 윈도이므로 자바 JDK 설치 후 JAVA_HOME을 설정한 뒤 나온 대로 따라 하니 잘 실행되었습니다. 중간에 * 뭐 에러가 있어서 머리 터질 뻔했지만 아주 간단하게 해결했습니다 ㅎㅎ 혹시 저와 같이 자바 * 뭐 에러 나는 분 있으시면 댓글 남겨주세요 ㅎㅎ 뭐 다른 글에서는 파이썬 버전이 안 맞아서 그렇다는데 저는 다 똑같은데도 안되더라고요 ㅎㅎ 겨우 찾아서 해결했습니다! wordcloud는 pip install을 통해 다운!

 

과정을 설명하면

  • selenium을 통해 키워드 검색 후 나온 상위 마켓 상품 리뷰를 쭉 분석합니다. 여기서 광고, 브랜드 카탈로그 그리고 가격 비교 란은 건너뛰었습니다.
  • 그 후 굿 리뷰와 베드 리뷰의 형태소를 분석하여 명사와 형용사만 추출해서 list에 저장했습니다.
  • 추출한 list를 wordcloud를 통해 분석하였습니다.

아주 간단하죠?

 

테스트로 네이버에 서랍을 검색하면 아래와 같이 결과가 나옵니다!

왼쪽은 리뷰 점수 5점, 4점 모음이고 오른쪽은 리뷰 점수 3점, 2점, 1점 모음입니다.

 

goodreviewbadreview
리뷰 분석 후 wordcloud 결과

우선 배송에 웃고 배송에 우는 거... 이거는 모든 키워드 전부 그런 것 같아요 ㅎㅎ 배송만 빠르게 잘해도 일단 반 이상은 한다는 소리겠죠?

 

이 프로그램은 브레인스토밍을 필요로 합니다 ㅎㅎ 저 단어로 더 창의적인 결과가 나올 수도 있을 거 같아요 ㅎㅎ 분석을 해보자면 구매자들은 서랍을 산 후에 튼튼한 품질과  깔끔한 디자인과 원하는 크기 이 정도로 볼 수 있겠네요! 즉 디자인은 본인이 원하는 거를 살 것이고... 무조건 튼튼하고 크기에 대한 공시를 잘해줘야 좋은 원하는 사람만 구매하고 좋은 리뷰를 받을 것 같습니다. 반면에 안 좋은 리뷰를 보면 플라스틱 제품이고 중국산 같은 품질로 불만이 많은 것을 볼 수 있습니다.

 

즉 서랍은 디자인 제품으로 안정감 있고 높은 품질 제품을 빠르게 배송만 잘해주면 된다는 결론이 나오네요!

이런 식으로 사용할 수 있습니다 ㅎㅎ

 

전체 코드는 아래 참고해주세요!

from selenium.webdriver.common.by import By
from selenium.webdriver import ActionChains
from wordcloud import WordCloud
from collections import Counter
from selenium import webdriver
from konlpy.tag import Okt
import time

def scrollControl(driver):
    last_height = driver.execute_script("return document.body.scrollHeight")

    while True:
        # 끝까지 스크롤 다운
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")

        # 1초 대기
        time.sleep(1)

        # 스크롤 다운 후 스크롤 높이 다시 가져옴
        new_height = driver.execute_script("return document.body.scrollHeight")
        if new_height == last_height:
            break
        last_height = new_height


class naverReviewAnal:
    def __init__(self):
        options = webdriver.ChromeOptions()
        options.add_experimental_option('excludeSwitches', ['enable-logging'])
        self.driver = webdriver.Chrome(options = options)
    def getUrlList(self, keyword):
        url = f"https://search.shopping.naver.com/search/all?query={keyword}"
        self.driver.get(url)
        urlList = []
        scrollControl(self.driver)
        itemList = self.driver.find_elements(By.CLASS_NAME, "basicList_inner__eY_mq")
        i = 0
        for itemData in itemList:
            if itemData.text.find("광고") != -1 or itemData.text.find("쇼핑몰별 최저가") != -1 or itemData.text.find("브랜드 카탈로그") != -1:
                continue
            itemUrl = itemData.find_element(By.CLASS_NAME, "thumbnail_thumb__3Agq6").get_attribute("href")
            urlList.append(itemUrl)
            i += 1
            if i == 5:
                break
        return urlList

    def getReview(self, urlList, keyword):
        nlpy = Okt()
        for url in urlList:
            self.driver.get(url)
            scrollControl(self.driver)
            reviewOptionList  = self.driver.find_elements(By.CLASS_NAME, "_1K9yWX-Lpq")
            action = ActionChains(self.driver)
            goodReviewList = []
            badReviewList = []
            for a in reviewOptionList:
                if  a.text =="평점 높은순" or a.text =="평점 낮은순" :
                    action.move_to_element(a).perform()
                    a.click()
                else:
                    continue
                reviewbox = self.driver.find_element(By.CLASS_NAME, "_3YOQJDR2-0")
                reviewPages = reviewbox.find_elements(By.CLASS_NAME, "UWN4IvaQza")
                # 페이지 있는 화면에 멈춰야함
                for reviewPage in reviewPages:
                    print(reviewPage.text, " 페이지 ")
                    time.sleep(1.5)
                    review = self.driver.find_elements(By.CLASS_NAME,"_1YShY6EQ56")
                    for data in review:
                        reviewRate = data.find_element(By.CLASS_NAME, "_15NU42F3kT").text
                        reviewData = data.find_element(By.CLASS_NAME, "_19SE1Dnqkf").text
                        reveiwAnal = nlpy.pos(reviewData)
                        for a in reveiwAnal:
                            if len(a[0]) == 1: #한글자 제거
                                continue
                            if a[1] == "Noun":
                                if int(reviewRate) > 3:
                                    goodReviewList.append(a[0])
                                else:
                                    badReviewList.append(a[0])
                            elif a[1] == "Adjective" and len(a[0]) >2: # 3글자 이상 형용사
                                if int(reviewRate) > 3:
                                    goodReviewList.append(a[0])
                                elif int(reviewRate) < 3 :
                                    badReviewList.append(a[0])

                    if reviewPage.text == "1":
                        continue
                    try: reviewPage.click()
                    except: pass

        GBRException = ["생각","좀","달","상품","사용", "아주","일단","반품", "구매","정말","차라리", "암만", "생각","같아요","진짜", "그냥","좋아요","좋네요"]
        
        for delItem in goodReviewList:
            if delItem in GBRException or delItem == keyword:
                goodReviewList.remove(delItem)

        for delItem in badReviewList:
            if delItem in GBRException or delItem == keyword:
                badReviewList.remove(delItem)

        return [goodReviewList,badReviewList]
    
    def getWordCloud(self, goodReviewList, badReviewList):
        goodCounts = Counter(goodReviewList).most_common(40) # 상위 40개 추출
        badCounts = Counter(badReviewList).most_common(40)
        if len(goodCounts) == 0:
            goodCounts = [("없음",1)]
        elif len(badCounts) == 0:
            badCounts = [("없음",1)]
        wordcloud = WordCloud(font_path='C:\windows/fonts/malgun.ttf',
                              background_color = 'white',
                              width = 1000, height = 800).generate_from_frequencies(dict(goodCounts))
        wordcloud1 = WordCloud(font_path='C:\windows/fonts/malgun.ttf',
                              background_color = 'white',
                              width = 1000, height = 800).generate_from_frequencies(dict(badCounts))
        wordcloud.to_file('./reviewBigData/goodReview.jpg')
        wordcloud1.to_file('./reviewBigData/badReivew.jpg')



if __name__ == "__main__":
    nr = naverReviewAnal()
    keyword = input("Put keyword for review analysis : ")
    urlList = nr.getUrlList(keyword)
    reviewList = nr.getReview(urlList, keyword)
    nr.getWordCloud(reviewList[0],reviewList[1])

 

이상으로 리뷰 마치겠습니다.

글 내용이 도움되셨다면 구독 또는 좋아요 부탁드립니다~

더 많은 정보와 리뷰를 원하시면 여기로!

모바일은 여기로!

반응형