프로젝트

개인 프로젝트 - 의류 검색기

원띵재 2023. 7. 10. 12:00

프로젝트 기간 :

23.04.06 ~ 23.04.12

 

사용 언어 : python

사용 라이브러리 : pandas, numpy, sklearn, flask, selenium, tqdm, BeatifulSoup

 

프로젝트 멤버 :

원명재(본인)

 

GITHUB LINK : https://github.com/Thingjae9/PROJECT4

 

GitHub - Thingjae9/PROJECT4

Contribute to Thingjae9/PROJECT4 development by creating an account on GitHub.

github.com

 

진행한 업무 : 

데이터 크롤링, 데이터 EDA, 모델 구현, 모델 WEB 서빙

 

사용한 데이터 :

https://www.musinsa.com/app/

 

무신사 스토어

온라인 패션 스토어. 우리가 사랑한 패션의 모든 것, 다 무신사랑 해.

www.musinsa.com

def musinsa_rank(category_num,page_num):
    url = f"https://www.musinsa.com/ranking/best?period=now&age=ALL&mainCategory=00{category_num}&subCategory=&leafCategory=&price=&golf=false&kids=false&newProduct=false&exclusive=false&discount=false&soldOut=false&page={page_num}&viewType=small&priceMin=&priceMax="
    response = requests.get(url)
    html = bs(response.text, 'lxml')
    musinsa_rank_df = rbnl(html)
    
    return musinsa_rank_df

def rbnl(html):
    musinsa_rank_df = pd.DataFrame()
    
    #순위 뽑기
    rank_html = html.select('#goodsRankList > li > p')
    rank_no_list = []
    
    for i in rank_html:
        rank_no_list.append(i.string.strip())
        
    musinsa_rank_df['순위'] = rank_no_list
    
    #브랜드 이름 뽑기
    brand_html = html.select('#goodsRankList > li > div.li_inner > div.article_info > p.item_title > a')
    brand_list = []
    
    for i in brand_html:
        brand_list.append(i.string)
        
    musinsa_rank_df['브랜드명']=brand_list
    
    #링크와 의류명 뽑기
    link_name_html = html.select('#goodsRankList > li > div.li_inner > div.article_info > p.list_info > a')
    link_list = []
    name_list = []
    
    for i in link_name_html:
        link_list.append(i['href'])
        name_list.append(i['title'])
        
    musinsa_rank_df['의류명']=name_list
    musinsa_rank_df['링크']=link_list
    
    #상세 페이지 크롤링
    musinsa_rank_df2 = specific_info(link_list)
    
    #데이터 프레임 옆으로 합치기
    musinsa_rank_df = pd.concat([musinsa_rank_df, musinsa_rank_df2], axis=1)
    
    return musinsa_rank_df

def specific_info(link_list):
    headers={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.67 Safari/537.36'}
    
    musinsa_rank_df = pd.DataFrame()
    part_num_list=[]
    sex_list=[]
    view_list=[]
    sales_list=[]
    like_list=[]
    
    
    for link in tqdm(link_list):
#         print(link)
        response_1=requests.get(link,headers=headers)
        html_1=bs(response_1.text, 'lxml')
        
        #품번 리스트 생성
        part_num_html=html_1.select('#product_order_info > div.explan_product.product_info_section > ul > li:nth-child(1) > p.product_article_contents > strong')
        part_num=part_num_html[0].get_text().split('/')[-1].strip()
        part_num_list.append(part_num)
        
        #성별 리스트 생성
        sex_html=html_1.select("#product_order_info > div.explan_product.product_info_section > ul > li > p.product_article_contents > span.txt_gender")
        sex=sex_html[0].get_text().replace('\n',' ').strip()
        sex_list.append(sex)
        
        #셀레니움으로 원하는 데이터 가져오기
        driver = webdriver.Chrome('chromedriver.exe')
        driver.get(link)
        sel_html=driver.page_source
        html_2=bs(sel_html)
        
        #조회수 가져오기
        view_html=html_2.find_all("strong", {"id":"pageview_1m"})
        view=view_html[0].get_text()
        view_list.append(view)
        
        #누적 판매 가져오기
        sales_html=html_2.find_all("strong", {"id":"sales_1y_qty"})
        sales=sales_html[0].get_text()
        sales_list.append(sales)
        
        #좋아요 수 가져오기
        like_html=html_2.find_all("span", {"class": "prd_like_cnt"})
        like=like_html[0].get_text()
        like_list.append(like)
        
        #드라이버 닫아주기
        driver.close()
        
        #시간 추가
        time.sleep(0.01)
        
    musinsa_rank_df['품번']=part_num_list
    musinsa_rank_df['성별']=sex_list
    musinsa_rank_df['조회수']=view_list
    musinsa_rank_df['누적판매량(1년)']=sales_list
    musinsa_rank_df['좋아요']=like_list
    
    return musinsa_rank_df

for a in [1, 2, 3, 4, 5]:
    for b in [2, 3, 4, 5]:
        category_num=a
        page_num=b
        final_df=musinsa_rank(category_num,page_num)

        file_name = f"musinsa_ranking_category{category_num}_page{page_num}.csv"
        final_df.to_csv(file_name, index=False)
        pd.read_csv(file_name)

데이터 크롤링 : 각 카테고리(상의, 하의, 아우터, 신발, 가방) 종류마다 탑 5 페이지(450등까지)의 정보 크롤링

# 참고 코드 (https://velog.io/@chaliechu117/%EB%AC%B4%EC%8B%A0%EC%82%AC-%EC%8B%A4%EC%8B%9C%EA%B0%84-%EB%9E%AD%ED%82%B9-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%BD%91%EA%B8%B0)

 

프로젝트 배경 : 

코로나 이후 많은 사람들이 온라인 매장에 관심을 갖기 시작했고, 사이트들은 옷의 종류는 많지만 원하는 것을 검색했을 때 원하는 제품을 바로 얻어내지 못하고, 이는 구매에 어려움으로 이어질 수 있음.

 

프로젝트 목표 :

1. 검색어를 통한 의류 추천 웹 사이트 구현

 

프로젝트 결과 :

파이프라인.

추천 알고리즘 웹 구현(Flask)

@app.route('/')
def home():
    global df
    df = pd.read_csv('clean.csv')
    return render_template('index.html')

@app.route('/', methods=['POST'])
def get_recommendation():
    global df
    gender = request.form['gender']
    category = request.form['category']
    keyword = request.form['keyword']
    # 이제 추천 로직을 수행합니다.
    # 결과는 HTML 문자열로 생성합니다.
    # 카테고리에 따른 필터링
    if category == '상의':
        filtered_df = df[df['category'].isin(['상의'])]
        df = filtered_df.drop(['category'], axis=1)

    elif category == '하의':
        filtered_df = df[df['category'].isin(['하의'])]
        df = filtered_df.drop(['category'], axis=1)
    
    elif category == '아우터':
        filtered_df = df[df['category'].isin(['아우터'])]
        df = filtered_df.drop(['category'], axis=1)
    
    elif category == '가방':
        filtered_df = df[df['category'].isin(['가방'])]
        df = filtered_df.drop(['category'], axis=1)
    
    elif category == '신발':
        filtered_df = df[df['category'].isin(['신발'])]
        df = filtered_df.drop(['category'], axis=1)
    
    else: df = df

    if gender == '남':
        filtered_df = df[df['gender'].isin(['남', '남 여'])]
    elif gender == '여':
        filtered_df = df[df['gender'].isin(['여', '남 여'])]
    else:
        filtered_df = df
    
    model = TfidfVectorizer()
    tfidf_vectors = model.fit_transform(filtered_df['name'])
    keyword_vector = model.transform([keyword])

    cosine_similarities = cosine_similarity(keyword_vector, tfidf_vectors)

    results = cosine_similarities[0].argsort()[:-4:-1]
    recommended_products = filtered_df.iloc[results]
    reco_brand = recommended_products['brand']
    recommended_products = recommended_products['name']


    
    result = '<h1>당신을 위한 추천 아이템</h1>'
    result += '<p>Gender : {}</p>'.format(gender)
    result += '<p>Category : {}</p>'.format(category)
    result += '<p>Keyword : {}</p>'.format(keyword)
    result += '<h1>1위 브랜드 : {}</h1>'.format(reco_brand.iloc[0])
    result += '<p>추천 아이템 1위 : {}</p>'.format(recommended_products.iloc[0])
    result += '<p>추천 아이템 2위 : {}</p>'.format(recommended_products.iloc[1])
    result += '<p>추천 아이템 3위 : {}</p>'.format(recommended_products.iloc[2])
    # 추천 결과를 반환합니다.
    return result

코사인유사도 :

검색 키워드를 코사인 유사도를 통해 데이터에 있는 가장 가까운 데이터에서 순위가 높은 3개 출력

 

DashBoard 구현(Flask)

@app.route('/dashboard')
def dashboard():
    # 데이터 로드
    df1 = pd.read_csv('final.csv')
    group_brand = df1.groupby('브랜드명')['누적판매량(1년)']
    
    brand_list = []
    for row in df1['브랜드명']:
        brand_list.append(row)
    
    sales_list = []
    for amount in df1['누적판매량(1년)']:
        sales_list.append(amount)
        


    # 대시보드 출력
    return render_template('brand_for_sales.html')

@app.route('/dashboard2')
def dashboard2():
    return render_template('brand_for_gender.html')

@app.route('/dashboard3')
def dashboard3():
    return render_template('brand_for_total.html')

 

 

모델 서빙 결과 및 대시보드 설명 영상 : 

 

한계점 및 개선 가능 사항 :

한계점.

1.검색어를 통해 브랜드 및 상품을 추천하기 때문에 검색기의 경우만 다르고 실제 출력 결과는 비슷할 수 있음.

 

개선 가능 사항.

1. dashboard 구현 방식 변경

2. 웹사이트 배포, 디자인 변경