import pandas as pd
import requests
from bs4 import BeautifulSoup as bs
import time
from tqdm import tqdm
url="https://www.musinsa.com/ranking/best?period=now&age=ALL&mainCategory=001&subCategory=&leafCategory=&price=&golf=false&kids=false&newProduct=false&exclusive=false&discount=false&soldOut=false&page=1&viewType=small&priceMin=&priceMax="
우선 url 을 살펴보자!
pd.read_html(url)
위의 코드를 실행하면 테이블이 없다는 에러가 뜨는 것을 확인하였으므로 request를 통해 호출해보자!
response=requests.get(url)
response.status_code
200은 수신이 양호하다는 뜻이다. request로 호출을 받아봤고 bs를 이용하여 html을 가져와, 본격적으로 내가 얻고자 하는 데이터들을 추출해보자! 원하는 정보는 다음과 같다.
html=bs(response.text, 'lxml')
html
경로: #goodsRankList > li:nth-child(1) > div.li_inner > div.article_info > p.item_title > a
실시간 순위 옷들의 브랜드부터 가져와보자
li:nth-child(1) 속의 숫자는 곧 순위에 해당한다.
우선 순위별로 브랜드 이름만을 추출하기 위해 brand_list를 만드는데, li:nth-child(1)을 li로 바꾸면 첫페이지의 모든 브랜드 순위를 가져올 수 있다.
brand_html=html.select('#goodsRankList > li > div.li_inner > div.article_info > p.item_title > a')
brand_html[0].string
brand_list=[]
for i in brand_html:
brand_list.append(i.string)
brand_list[0:5]
경로: #goodsRankList > li:nth-child(1) > div.li_inner > div.article_info > p.list_info > a
html.select('#goodsRankList > li:nth-child(1) > div.li_inner > div.article_info > p.list_info > a')
a=html.select('#goodsRankList > li:nth-child(1) > div.li_inner > div.article_info > p.list_info > a')
a[0]['href']
위 방법을 통해 데이터를 추출하는 것이 더 좋아 보이므로 이를 이용하여 데이터를 뽑아보자!
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'])
link_list[0:5], name_list[0:5]
경로: #goodsRankList > li:nth-child(1) > p
rank_html=html.select('#goodsRankList > li > p')
rank_no_list=[]
for i in rank_html:
rank_no_list.append(i.string.strip())
rank_no_list[0:5]
위의 작업에서 link_list 속에 각 옷에 대한 링크를 넣어주었다. 각각의 링크 속에는 옷에 대한 상세정보와 다양한 정보들을 가지고 있다.
그 중 원하는 정보를 추려 데이터를 가져와보자!
품번, 성별, 조회수(1개월), 누적판매, 좋아요수를 가지고 오는것이 좋을 것 같다!
a=requests.get(link_list[0])
a.status_code
위와 같이 상세 정보에 대한 페이지에 접근을 하려고 하니 403 오류가 뜨는 것을 확인할 수 있었다. 이런 경우 headers을 설정해주어야 했고, header에 대한 값을 추가해주었다.
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'}
response_1=requests.get(link_list[32],headers=headers)
response_1.status_code
정상적인 접근이라는 정보인 User Agent 정보를 추가하였더니 정상적으로 페이지를 가지고 올 수 있었다.
html_1=bs(response_1.text, 'lxml')
html_1
경로: #product_order_info > div.explan_product.product_info_section > ul > li:nth-child(1) > p.product_article_contents > strong
a=html_1.select('#product_order_info > div.explan_product.product_info_section > ul > li:nth-child(1) > p.product_article_contents > strong')
a[0].string, a[0].get_text()
위에서 보면 알 수 있다시피 .string을 사용하면 None을 반환하는 것을 알 수 있다. 그 이유는 단순히 문자열이 없기 때문이다. 그렇기 때문에 유니코드 형식으로 텍스트를 문자열도 반환하는 get_text()를 사용해줘야 원하는 값이 나온다!
내가 원하는 값을 얻으려는 정보에 대한 코드를 정리하자면 다음과 같다
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
경로: #product_order_info > div.explan_product.product_info_section > ul > li:nth-child(2) > p.product_article_contents > span.txt_gender
a=html_1.select("#product_order_info > div.explan_product.product_info_section > ul > li:nth-child(2) > p.product_article_contents > span.txt_gender")
a[0].get_text()
response_2=requests.get(link_list[2],headers=headers)
html_2=bs(response_2.text, 'lxml')
a2 = html_2.select("#product_order_info > div.explan_product.product_info_section > ul > li > p.product_article_contents > span.txt_gender")
a2[0].get_text().replace('\n',' ').strip()
서로 다른 성별 값을 가지고 있는 링크를 비교해보니, 성별 정보가 하나든 두개든 \n이 사이사이에 있다는 것을 알 수 있다. 그러므로 단순히 \n 값을 없애고, 양옆의 공백만 지워주면 충분할 것 같다! 또한 li:nth-child(2) 에서 3번째 child에 성별의 값이 들어가 있는 요소 또한 존재했다 고로 child를 특정하지 않고 li만 사용해야 할 것 같다!
최종적으로 얻고자 하는 형태의 정보를 얻기 위한 코드는 다음과 같다
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
bs4를 이용하여 위에서 데이터를 뽑아왔던 방법대로 이용해보아도 원하는 데이터가 뽑히지 않다는 것을 확인할 수 있었다. (계속 빈 리스트를 가져올 뿐이었다. 고로 셀레니움을 사용해보기로 했다
from selenium import webdriver
셀레니움을 설치해주었고, webdriver이라는 것을 가지고 왔다. 셀레니움은 처음 써보지만, 나의 버전과 맞는 chromedriver이라는 것을 다운받아 준 후 가상의 chrome 창을 열어 그 곳에서 원하는 데이터를 가지고 오는 것 같다.
driver = webdriver.Chrome('chromedriver.exe')
driver.get(link_list[0])
sel_html=driver.page_source
html_2=bs(sel_html)
위와 같이 셀리니움을 이용하여 내가 사용하고자 하는 사이트의 페이지를 열어 html doc를 가지고 올 수 있었다. 하지만 또 다시 한번 문제가 발생했다.
html_2.select('pageview_1m')
위의 코드를 이용하자 또 다시 빈 리스트를 가지고 오는 것을 확인 할 수 있었다.
html_2.find_all("strong", {"id":"pageview_1m"})
하지만 find_all을 사용하니 문제가 해결되었다.
view_html=html_2.find_all("strong", {"id":"pageview_1m"})
view=view_html[0].get_text()
view
드라이버를 열었으니 누적 판매와 좋아요도 바로 추출해보자
sales_html=html_2.find_all("strong", {"id":"sales_1y_qty"})
sales=sales_html[0].get_text()
sales
like_html=html_2.find_all("span", {"class": "prd_like_cnt"})
like=like_html[0].get_text()
like
드라이버를 닫아주자!
driver.close()
지금까지 내가 한 코드들을 이용하여 함수로 만들어 보자!
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
category_num=input("Category 번호를 입력하세요: ")
page_num=input("Page 번호를 입력하세요: ")
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)
추차차 짱!