데이터분석 6기/본캠프

2025-05-27 무신사 크롤링

seyeon1130 2025. 5. 27. 20:50

무신사 사이트

무신사는 각 카테고리별로, 세부 카테고리별로 랭킹이 300개씩 있다.

나와있는 모든 랭킹을 크롤링 하는 것을 목표로 하고 크롤링을 시작했다.

 

저 목록을 보면 구조가 이런식으로 돼있다.

노란색이 전체 제품 구조이고, 파란색으로 칠한 개 하나하나의 제품이 들어있다.

 

정적 크롤링 시도

base_url = 'https://www.musinsa.com'
url = "https://www.musinsa.com/main/musinsa/ranking?storeCode=musinsa&sectionId=199&contentsId=&categoryCode=104000"
headers = {
    'User-Agent': 'Mozilla/5.0'
}
html = requests.get(url, headers=headers).text
soup = BeautifulSoup(html, 'html.parser')

data = []
items = soup.select('div.sc-1t5ihy5-0.clMPWr.gtm-view-item-list')  # 정확한 클래스 선택

for item in items:
    a_tag = item.select_one('a[href*="/products/"]')
    if not a_tag:
        continue

    product_url = base_url + a_tag['href']
    product_id = a_tag.get('data-item-id')
    brand = a_tag.get('data-item-brand')
    price = a_tag.get('data-price')
    original_price = a_tag.get('data-original-price')
    discount_rate = a_tag.get('data-discount-rate')

    img_tag = a_tag.select_one('img')
    product_name = img_tag['alt'].strip() if img_tag and 'alt' in img_tag.attrs else None

    data.append({
        '상세주소': product_url,
        '상품 ID': product_id,
        '브랜드': brand,
        '제품명': product_name,
        '정가': original_price,
        '할인가': price,
        '할인율': discount_rate
    })

# 확인용 출력
for d in data:
    print(d)

처음에는 이런식으로 정적 크롤링에 접근했는데, 보안상 이유인지 계속 아무 데이터도 들어오지 않았다.

그래서 결국 동적 크롤링으로 바꾸었다.

 

동적 크롤링 시도

 

from selenium import webdriver
from bs4 import BeautifulSoup
import time

# 셀레니움 웹드라이버 실행
driver = webdriver.Chrome()
driver.get("https://www.musinsa.com/main/musinsa/ranking?storeCode=musinsa&sectionId=199&contentsId=&categoryCode=104000")

time.sleep(3)  # JS 로딩 시간

html = driver.page_source
soup = BeautifulSoup(html, 'html.parser')
driver.quit()

# 기존 방식대로 크롤링
items = soup.select('div.gtm-view-item-list')
print(f"상품 개수: {len(items)}개")

for item in items:
    a_tag = item.select_one('a[href*="/products/"]')
    if not a_tag:
        continue

    product_url = "https://www.musinsa.com" + a_tag['href']
    product_id = item.get('data-item-id')
    brand = item.get('data-item-brand')
    price = item.get('data-price')
    original_price = item.get('data-original-price')
    discount_rate = item.get('data-discount-rate')

    img_tag = a_tag.select_one('img')
    product_name = img_tag['alt'].strip() if img_tag and 'alt' in img_tag.attrs else None

    print({
        '상세주소': product_url,
        '상품 ID': product_id,
        '브랜드': brand,
        '제품명': product_name,
        '정가': original_price,
        '할인가': price,
        '할인율': discount_rate
    })

 

클래스 안에 다른 태그 없이 그냥 data-item-id="4085614" 이런식으로 쭉쭉 써져있다면 .get() 함수를 사용하면 된다.

 

더 추가할 것

이제 상세페이지에 가면 성별, 조회수, 누적 판매, 찜 수. 무배당발 여부를 알 수 있다.

이것들을 추가로 크롤링 해야한다.

 

 

후기 평점과 개수, 카테고리도 함께 가져오려고 한다.

 

성별, 누적판매, 조회수

 

구조는 이런식으로 dl sc-2ll6b5-0.doA-dRN 안에 있는 dt 태그에 구분, 그리고 dd에 그 사항이 나와있는 형식이었다.

def parse_detail_page(url):
    # 셀레니움 크롬 드라이버 실행
    options = webdriver.ChromeOptions()
    options.add_argument('--headless')  # 창 안 띄우기
    options.add_argument('--no-sandbox')
    options.add_argument('--disable-dev-shm-usage')
    driver = webdriver.Chrome(options=options)

    driver.get(url)
    time.sleep(3)  # JS 로딩 시간

    soup = BeautifulSoup(driver.page_source, 'html.parser')
    driver.quit()

    #성별, 조회수, 누적판매
    info = {
        '성별': None,
        '조회수': None,
        '누적판매': None
    }

    dt_tags = soup.select('dl.sc-2ll6b5-0.doA-dRN dt')

    for dt in dt_tags:
        label = dt.get_text(strip=True).replace(':', '')
        dd = dt.find_next_sibling('dd')
        value = dd.get_text(strip=True) if dd else None

        if label == '성별':
            info['성별'] = value
        elif label == '조회수':
            info['조회수'] = value
        elif label == '누적판매':
            info['누적판매'] = value

이 페이지도 마찬가지로 동적으로밖에 안돼서 동적 크롤링을 진행했다.

우선 dt를 선택해준 다음, 그 안에서 dd를 찾아서 둘을 저장하는 식으로 진행했다.

 

대분류, 중분류, 소분류

 

이 코드를 보면 a 태그 안에 data-category-id = 1depth 부터 3depth 인 거 까지 있고, 각 분류는 data-category-name으로 지정돼있다.

# 대분류~소분류까지 전부 검색 후 info에 저장
    category_tags = soup.select('a[data-category-name]')
    for tag in category_tags:
        cid = tag.get('data-category-id')
        cname = tag.get('data-category-name')

        if cid == '1depth':
            info['대분류'] = cname
        elif cid == '2depth':
            info['중분류'] = cname
        elif cid == '3depth':
            info['소분류'] = cname

그래서 parse_detail_page 함수에 이런식으로 저장했다.

 

 

이렇게 저장 완료!!!

 

내일 이어서 찜 수를 해봐야겠다~~!

'데이터분석 6기 > 본캠프' 카테고리의 다른 글

2025-05-29 최종 프로젝트 데이터 살펴보기  (0) 2025.05.29
2025-05-28 spark 피하기  (0) 2025.05.28
2025-05-27 스파크 특강 ot  (1) 2025.05.27
2025-05-26 spark  (1) 2025.05.26
2025-05-24 정적 크롤링 과제  (0) 2025.05.24