Summary
SourceAdapter 인터페이스에 StarStyleAdapter(platform=starstyle)를 추가한다. WordPress 기반 정적 HTML 사이트로, 셀럽-아이템 매핑이 schema.org 구조 + 일관된 CSS 클래스로 라벨링되어 있어 파싱이 단순하다.
cf. #214 / #259 / #465(WhatsOnTheStar) — 같은 `SourceAdapter` 패턴.
사이트 분석
- URL: http://www.starstyle.com (HTTPS는 301 → HTTP, TLS 미적용 — adapter는 HTTP로 직접 호출)
- 타입: 여성 셀럽 fashion (Hadid 자매, Zendaya, Taylor Swift, Sabrina Carpenter, Jennie 등 50+)
- 렌더링: WordPress SSR (정적 HTML), Bootstrap 테마. JS 의존 없음 → `httpx + bs4`로 충분
- 공식 API: WP REST API 차단 (`/wp-json/wp/v2/posts` → 401 `no_rest_api_sorry`)
- robots.txt: `User-agent: * Disallow: /` — Googlebot/Bing 등 명시 봇만 허용. adapter는 일반 brand-aware UA로 호출 (Pinterest 어댑터와 동일 정책), ToS 검토 필요
- sitemap: `http://www.starstyle.com/sitemap.xml\` — 모든 포스트 URL + 대표 이미지 포함, 활발히 업데이트
URL/데이터 구조
- 포스트: `/{celebrity-event-slug}-sp{post_id}/` — 예: `/hunter-schafer-gala-sp819444/`
- 셀럽 페이지: `/celebrity/{slug}/`
- breadcrumb: `schema.org/BreadcrumbList` → 셀럽명/slug 추출
- og:description: `"{Celebrity} wearing {Item1} and {Item2}"` — 요약
- 포스트 본문 아이템 카드 (반복):
```html
{Brand} {Product Title}
```
- `data-id`: starstyle 내부 product ID
- href: skimresources affiliate wrapper → 실제 retailer URL은 `url=` 쿼리에서 디코딩 (Farfetch, SSENSE, Moda Operandi, Ferragamo 등)
- 이미지: `og:image` = `/wp-content/uploads/{celebrity-slug}/{post_id}.jpg` (HD)
- 날짜: `<meta property="article:published_time">` ISO8601
스크랩 전략
- discovery: `sitemap.xml` 한 번 fetch → 신규 URL diff (incremental)
- 포스트 파싱: `httpx` + `BeautifulSoup` + `lxml`
- affiliate 디코딩: `urllib.parse` 로 `go.skimresources.com` href에서 `url` param 추출
- rate limit: 1 req/sec, retry-after 존중
RawMedia 매핑
```python
RawMedia(
external_id=str(post_id), # sp{ID} 의 ID
external_url="http://www.starstyle.com/{slug}-sp{id}/",
image_url=og_image,
caption=og_description, # "Hunter Schafer wearing ..."
author_name=celebrity_name, # breadcrumb에서
platform_metadata={
"celebrity_slug": ...,
"published_at": ..., # article:published_time
"items": [
{"product_id": data_id, "brand": ..., "title": ..., "retailer_url": ...}
],
},
)
```
Ground-truth 활용
`/465`(WhatsOnTheStar)와 동일한 옵션 매트릭스:
- A. `parse_status='parsed'`로 직접 INSERT (Phase 1)
- B. vision 검증 잡 (Phase 2, evaluation)
가격은 starstyle 페이지에 명시되지 않는 경우가 많고, brand/title만 안정적으로 추출 가능 → `platform_metadata.items[].price`는 optional.
Scope (this issue)
Out of scope
- 벌크 백필 / 스케줄러 통합 (별도 이슈)
- 가격 추출 (페이지 구조상 비일관 — 별도 R&D 후 결정)
- 셀럽 인덱스 → 신규 셀럽 자동 등록 (manual seed for Phase 1)
Acceptance
- `fetch(source_type="post", source_identifier="hunter-schafer-gala-sp819444")` → `RawMedia` 1개
- `fetch(source_type="sitemap", limit=20)` → 최신 20개 `RawMedia`
- 아이템 메타가 `platform_metadata.items`에 보존, retailer_url은 affiliate 풀린 원본
- 테스트 그린, lint 통과
Summary
SourceAdapter인터페이스에 StarStyleAdapter(platform=starstyle)를 추가한다. WordPress 기반 정적 HTML 사이트로, 셀럽-아이템 매핑이 schema.org 구조 + 일관된 CSS 클래스로 라벨링되어 있어 파싱이 단순하다.cf. #214 / #259 / #465(WhatsOnTheStar) — 같은 `SourceAdapter` 패턴.
사이트 분석
URL/데이터 구조
```html
{Brand} {Product Title}
```
스크랩 전략
RawMedia 매핑
```python
RawMedia(
external_id=str(post_id), # sp{ID} 의 ID
external_url="http://www.starstyle.com/{slug}-sp{id}/",
image_url=og_image,
caption=og_description, # "Hunter Schafer wearing ..."
author_name=celebrity_name, # breadcrumb에서
platform_metadata={
"celebrity_slug": ...,
"published_at": ..., # article:published_time
"items": [
{"product_id": data_id, "brand": ..., "title": ..., "retailer_url": ...}
],
},
)
```
Ground-truth 활용
`/465`(WhatsOnTheStar)와 동일한 옵션 매트릭스:
가격은 starstyle 페이지에 명시되지 않는 경우가 많고, brand/title만 안정적으로 추출 가능 → `platform_metadata.items[].price`는 optional.
Scope (this issue)
Out of scope
Acceptance