파진기와 함께 코딩을
[MD스터디] 올리브영으로 배워보는 크롤링 본문
하이!
첫 블로그 주제는 크롤링입니다
왜냐면!!!!! 제가 MD직무 스터디를 하는중인데
데이터분석을 위해서
크롤링을 할 일이 생겨서
마침!! 크롤링 해보고 싶기도해서
코딩 분야로 시작한건 아니지만 기술블로그의 주제로 적절한거같아서 !!
가져왔어요
1. 약관확인
일단 크롤링할때는 좀.. 먼저 체크해야할게 있습디다
크롤링 금지 되어있는 경우 -> 그럼 하면 안되니까 API 있는거 써..
근데 개인 연습용으로는 뭐.. 멋대로 쓰고
실제서비스 공개나 배포하믄 안되심 !
2. 크롤링 준비
자 이제 본격적으로 해보자
일단 언어는 파이썬이고, playwright, BeautifulSoup4 (줄여서 bs4) 활용
- *playwright : 마소에서 만든 오픈소스 웹 자동화 라이브러리. 요즘 많이 쓰이는 웹 자동화/크롤링 프레임워크. Selenium이랑 비슷하지만, 더 신세대 버전
- * Selenium : 실제 브라우저(Chrome/Edge/Firefox)를 원격으로 조종해서 페이지 열기, 클릭, 입력, 스크롤, 탭 전환 등 사람 행동을 자동화하는 라이브러리.
- *bs4 : HTML, XML 문서를 쉽게 파싱(parse = 구조 분석)할 수 있게 도와주는 파이썬 라이브러리야. 웹 크롤링에서 제일 많이 쓰이는 도구 중 하나
일단 터미널에서
pip install playwright
playwright install
pip show playwright
pip install selenium
pip install beautifulsoup4 lxml
이거 쳐서 플.라하고 셀레니 비..뭐시기를 깔아주쇼

잘깔렸죠?? 굿
아 그리구 이거 코드파일있는 상위 폴더 아래 data 폴더 만들어주세요
그래야 만들어진 데이터 cvs파일이 쏙쏙 들어감니다
아래처럼 해줬음!

3. 상품 크롤링 - 플.라
카테고리는 우먼케어구요? 그에 맞춘 제품, 브랜드, 제품링크를 다 크롤링 해주었으요
# [25_크롤링]올영_innercare.py
from playwright.sync_api import sync_playwright
from bs4 import BeautifulSoup
from pathlib import Path
import csv
import time
# ==== 경로를 스크립트 기준으로 고정 ====
BASE_DIR = Path(__file__).resolve().parent
DATA_DIR = BASE_DIR / "data"
if DATA_DIR.exists() and not DATA_DIR.is_dir():
DATA_DIR.unlink() # data가 '파일'이면 삭제
DATA_DIR.mkdir(parents=True, exist_ok=True)
OUT_CSV = DATA_DIR / "innercare_sales_list.csv"
def crawl_category_page_html(page, page_idx: int):
# 위생용품 카테고리 (중카테고리 페이지) + 판매정렬 prdSort=03 그대로
url = (
"https://www.oliveyoung.co.kr/store/display/getMCategoryList.do"
"?dispCatNo=100000200040001"
f"&pageIdx={page_idx}"
"&rowsPerPage=24&prdSort=03&searchTypeSort=btn_thumb"
"&plusButtonFlag=N&isLoginCnt=0&aShowCnt=0&bShowCnt=0&cShowCnt=0"
"&gateCd=Drawer&trackingCd=Cat100000200040001_MID"
)
page.goto(url, wait_until="domcontentloaded")
# 상품 카드가 로드될 때까지 대기
try:
page.wait_for_selector(".prd_info", timeout=8000)
except Exception:
# 비었을 경우 확인용 스크린샷
page.screenshot(path=str(DATA_DIR / f"innercare_p{page_idx}_empty.png"))
time.sleep(0.5)
return page.content()
def parse_list(html: str):
soup = BeautifulSoup(html, "lxml")
cards = soup.select(".prd_info")
data = []
for it in cards:
brand_el = it.select_one(".tx_brand")
name_el = it.select_one(".tx_name")
link_el = it.select_one("a")
if not (brand_el and name_el and link_el):
continue
brand = brand_el.get_text(strip=True)
name = name_el.get_text(strip=True)
href = link_el.get("href", "")
if href.startswith("/"):
href = "https://www.oliveyoung.co.kr" + href
data.append({
"product_name": name,
"product_brand": brand,
"product_link": href,
})
return data
def write_csv(rows):
print(f"[DEBUG] Writing to: {OUT_CSV}")
with OUT_CSV.open("w", encoding="utf-8-sig", newline="") as fw:
writer = csv.DictWriter(fw, fieldnames=["product_name", "product_brand", "product_link"])
writer.writeheader()
writer.writerows(rows)
if __name__ == "__main__":
num_pages = 5 # 필요한 만큼 늘리
total = []
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page()
for i in range(1, num_pages + 1):
html = crawl_category_page_html(page, i)
rows = parse_list(html)
print(f"[PAGE {i}] collected: {len(rows)} items")
# 다음 페이지가 비면 중단 (마지막 페이지 도달)
if i > 1 and len(rows) == 0:
print("[INFO] No more items. Stop.")
break
total.extend(rows)
browser.close()
if not total:
print("[WARN] 수집 0건 - 셀렉터(.prd_info)나 정렬/카테고리 파라미터 점검 필요")
write_csv(total)
print(f"[DONE] total collected: {len(total)} rows → {OUT_CSV}")

이러케!! 10초만에 알아서 엑셀로 정리 완
저 링크 부분 저거는 원하는거 맞춰서 바꾸셍요
아 그리고 짜증나게 자꾸 없는 데이터 가져오는 경우가 생겨서 if문써서 예외처리도 추가해줫스요
데이터 없어서 결과 안나오는건데 에러떠서 뭔 문젠가하고 한참 봄 ㅉ
3. 리뷰 크롤링 - 셀레니움
리뷰 크롤링할때는 셀레니움을 쓸거에요
왜냐면??
'상품 리스트'는 HTML 응답에 바로 들어있음 → BeautifulSoup/Playwright로 쉽게 파싱 가능
그러나 리뷰는 보통 동적 로딩(AJAX) 구조라서 상세 페이지 들어가서 버튼 클릭해야 뜨거나, 스크롤해야 다음 페이지 로딩됨 → 이건 정적 파싱으로는 못 잡아내고, 브라우저 동작을 흉내 내야 하기에...
무튼무튼!!
일단 리뷰페이지 들어가서 개발자모드키고 카테고리 싹 확인해주세요~
왜냐면 상품마다 시기마다 카테고리항목이 다르니,,
근데 지금 새벽 3시 21분이라 잘거고 2편으로 돌아올게요~
내일봬요 누나!
* 참고한 블로그
Oliveyoung 선케어 제품 리뷰 크롤링 및 데이터 분석 + 제품 추천 사이트
계속계속 부딪혀 보자구
velog.io
'데이터📊' 카테고리의 다른 글
| [MD스터디] 올리브영으로 배워보는 크롤링 2 (0) | 2025.09.27 |
|---|