카페24 기반 화장품 쇼핑몰 6곳에서 상품을 수집하고, 상세페이지의 전성분 텍스트를 읽어
8개 타깃 성분(히알루론산, 판테놀, 알로에, 세라마이드, 센텔라, 나이아신아마이드, 비타민C,
징크) 중 어떤 게 포함됐는지 매칭해 백엔드 Product 시드 데이터(JSON)를 만든다.
결과물은 두 파일로 나뉜다.
output/products_seed.json— 6개 사이트를 자동 크롤링해서 만든 데이터.output/products_manual.json— 자동 크롤링만으로는 비타민C/알로에/징크 표본이 너무 적어서, 팀원이 직접 찾은 상품을 수동으로 추가한 데이터. 신뢰도 표시를 위해 자동 크롤링 결과와 파일을 분리해뒀다.
원래 후보는 14개 사이트였고, 플랫폼별로 크롤링 난이도가 크게 갈렸다.
- 카페24 계열 —
product/list.html같은 공통 URL 패턴을 쓰고 서버 렌더링이라 일반 크롤링(requests + BeautifulSoup)으로 충분히 뚫린다. 파서 하나를 만들면 사이트 간 재사용도 잘 된다. - 네이버 스마트스토어/브랜드관 계열(5곳) — JS 렌더링이 심하고 봇 차단이 강해 Selenium 같은 브라우저 자동화가 필요하고, 그마저도 자주 막힌다. 시간 대비 리스크가 커서 이번 크롤러 범위에서 제외했다.
- 카페24 계열 중에서도 아크로패스는 스킨/토너·앰플/세럼·크림·미스트 카테고리에 해당하는 상품이 거의 없어서(마이크로니들 패치가 메인) 제외했다.
그래서 남은 카페24 계열 6곳을 대상으로 정했다.
| 사이트 키 | 브랜드 | 운영사 |
|---|---|---|
paparecipe |
파파레서피 | 주식회사 에이비티아시아 |
beauty107 |
원오세븐 (107 Beauty) | 주식회사 소서 |
derma42 |
더마포티투 | 주식회사 에이티지코퍼레이션 |
kopher |
코페르 (KOPHER) | (주)베스트이노베이션 |
pith |
피쓰 (Pith) | 주식회사 엠레드코스메틱 |
kyyb |
KYYB | 주식회사비팩토리 |
pip install -r requirements.txtpython run.py --sites all
python run.py --sites paparecipe,derma42 --out output/sample.json--sites에 넘길 수 있는 키: paparecipe, beauty107, derma42, kopher, pith, kyyb
(각각 파파레서피, 원오세븐, 더마포티투, 코페르, 피쓰, KYYB).
- 목록 수집 (
crawler/list_scraper.py): 사이트별 카테고리 목록 페이지를 페이지네이션 끝까지 순회하며 상품명/가격/이미지/상세링크를 모은다. 카페24 사이트마다 CSS 클래스가 달라서, 특정 셀렉터 대신 "상세페이지 링크를 포함한<li>"를 상품 카드로 인식하는 방식으로 통일했다. - 카테고리 분류 (
crawler/category_classifier.py): 상품명 키워드로SKIN_TONER/AMPOULE_SERUM/CREAM/MIST_OIL/CLEANSER중 하나로 분류.CLEANSER는 폼/오일/밀크/워터 등 텍스처 구분 없이 "클렌저/클렌징" 키워드가 있으면 전부 포함한다 (2026-08-12 확정 — 처음엔 클렌징폼만 넣기로 했다가 스코프가 넓어졌다). 이 5개에 해당 없는 상품(선크림, 립밤, 패치 등)은 제외. - 전성분 추출 (
crawler/detail_scraper.py): 상세페이지에서 "전성분" 표기를 찾아 그 옆/아래에 있는 성분 목록 텍스트를 가져온다. 이 정보가 아예 없는 상품(사이트에 따라 흔함)은 건너뛴다. - 성분 매칭 (
crawler/ingredient_matcher.py): 전성분 텍스트에서 8개 타깃 성분 중 포함된 걸 전부 찾는다.Product.ingredient가 단일 FK라, 성분이 여러 개 매칭되면 같은 상품을 성분 개수만큼 row로 복제한다.
총 123 row (실제 상품 수 기준 50개). 성분 여러 개가 매칭된 상품은 성분 개수만큼 row가 나뉘어 있어서, row 수가 실제 상품 수보다 많다. 필수 필드(name/brand/category/ imageUrl/linkUrl/ingredientName) 미기입, 중복, 깨진 URL은 전수 검사해서 없는 것을 확인했다.
| 브랜드 | row 수 | 비고 |
|---|---|---|
| 파파레서피 | 85 | 카테고리 URL이 상품 유형별로 분리돼 있어 자동 파이프라인으로 전체 수집 (그중 클렌저 4) |
| 원오세븐 | 19 | 위와 동일 |
| 더마포티투 | 7 | 위와 동일 |
| 피쓰 | 12 | 상세 이미지 안의 전성분을 수동으로 읽어서 반영 (아래 "알려진 제약" 참고) |
| 코페르, KYYB | 0 | 전성분 고시 자체가 없어서 제외 (아래 참고) |
성분별
| 성분 | row 수 |
|---|---|
| 히알루론산 | 29 |
| 센텔라 | 29 |
| 판테놀 | 22 |
| 나이아신아마이드 | 21 |
| 세라마이드 | 19 |
| 비타민C | 2 |
| 알로에 | 1 |
| 징크 | 0 |
카테고리별
| 카테고리 | row 수 |
|---|---|
| CREAM | 49 |
| AMPOULE_SERUM | 36 |
| SKIN_TONER | 17 |
| MIST_OIL | 17 |
| CLEANSER | 4 |
비타민C/알로에/징크는 6개 사이트만으로는 표본이 너무 적어서(위 표 참고), 팀원이 다른 브랜드에서 직접 찾은 상품 29개를 추가했다. 자동 크롤링 결과와 신뢰도가 다르다는 걸 파일명으로 구분할 수 있게 별도 파일로 뒀다.
총 29 row. imageUrl은 각 상품 링크에서 og:image 메타태그를 읽어와 채웠는데,
12개만 성공했고 나머지 17개는 null이다.
- 13개는 올리브영 상품(직링크 또는
oy.run단축링크로 결국 올리브영으로 연결)이라 403으로 막혔다 — 네이버 계열과 같은 봇 차단 이슈. - 4개(시드물 3개, 닥터에디션 1개)는
og:image가 있긴 했는데 확인해보니 사이트 공용 기본 이미지(placeholder)였다. 상품마다 달라야 할 이미지가 전부 똑같은 URL을 가리켜서 가짜로 판단하고null처리했다.
이미지가 없는 17개는 BE에서 imageUrl nullable 처리하거나, 팀원이 직접 이미지 URL을
채워야 한다.
성분별: 알로에 11, 징크 10, 비타민C 8 카테고리별: CREAM 12, AMPOULE_SERUM 8, CLEANSER 6, SKIN_TONER 2, MIST_OIL 1
카테고리는 표에 없어서 크롤러와 같은 상품명 키워드 규칙으로 분류했고, 판단이 애매했던 것들은 다음과 같이 처리했다 (전부 재확인 필요):
- "젤"만 있고 스킨/토너/크림/미스트 키워드가 없는 수딩젤 5개(큐어 알로에 수딩젤, 아로마티카 수딩 알로에 베라 젤 등)는 CREAM으로 넣었다.
- 구달 "청귤 비타C 잡티케어 패드 알파"는 이름에 "토너"가 없지만 토너패드로 보고 SKIN_TONER로 넣었다.
- 시드물 "알로에 스킨 화이트닝 (미스트)"는 이름에 "스킨"이 있어 자동 로직이면 SKIN_TONER로 잘못 잡히지만, 실제 미스트 제품이라 MIST_OIL로 수동 보정했다.
- CLEEVA "클리바 르 클레어 앰플"은 표에 성분이 "비타민"이라고만 적혀 있어(다른 행은 다 "비타민 C") 오타로 보고 비타민C로 넣었다.
세 사이트(Pith, KYYB, KOPHER)는 자동 파이프라인(detail_scraper.py)이 상세페이지에서
"전성분" 텍스트를 못 찾는다. 근데 원인을 까보면 사이트마다 다르다.
- KYYB, KOPHER: 상세페이지의 "구매안내" 탭을 직접 열어봐도 결제/배송/교환/ 서비스문의만 있고 전성분 고시 항목 자체가 텍스트로도 이미지로도 없다. 판매자가 아예 안 올린 것으로 보이고, 크롤러를 더 손봐도 구할 수 있는 데이터가 아니라서 그대로 제외 상태로 뒀다 (코페르 13개, KYYB 6개 후보 상품 모두 미매칭).
- Pith: 텍스트로는 없지만, 상세 디자인 이미지 맨 마지막 장("Product information/
상품정보 고시")에 전성분이 이미지로 박혀 있다. 대상 상품이 7개뿐이라(그중 1개는
화장품이 아니라 실리콘 키링 액세서리라 전성분 자체가 없어 제외) OCR/비전 API를 따로
붙이지 않고, 이미지를 직접 열어 읽어서 8개 성분에 매칭한 뒤
output/products_seed.json에 수동으로 반영했다 (crawler/detail_scraper.py상단 docstring에 근거 기록). 그래서 Pith의 12개 row는run.py를 다시 돌려도 재생성되지 않는다 — 재크롤링 시 유실되지 않게 주의할 것. - 네이버 스마트스토어 계열(드뿌띠, 뷰라페 등 5곳)은 JS 렌더링과 봇 차단이 강해 이 크롤러 범위에 포함하지 않았다.
products_seed.json(자동 크롤링 123 row) + products_manual.json(수동 보강 29 row)를
합친 파일. BE에 실제로 넘기는 건 이 파일이다.
총 152 row.
카테고리별
| 카테고리 | row 수 |
|---|---|
| CREAM | 61 |
| AMPOULE_SERUM | 44 |
| SKIN_TONER | 19 |
| MIST_OIL | 18 |
| CLEANSER | 10 |
성분별
| 성분 | row 수 |
|---|---|
| 히알루론산 | 29 |
| 센텔라 | 29 |
| 판테놀 | 22 |
| 나이아신아마이드 | 21 |
| 세라마이드 | 19 |
| 알로에 | 12 |
| 비타민C | 10 |
| 징크 | 10 |