# Ontology Crawler Platform 향수 구독 플랫폼을 첫 사용 사례로 삼되, 차, 커피, 캔들, 디퓨저, 영양제, 선물, 패션 소품 같은 개인화 구독 추천 서비스에 재사용할 수 있는 범용 크롤러/온톨로지 기반 지식 DB MVP입니다. ## 핵심 아이디어 이 시스템은 웹에서 가져온 문장을 곧바로 사실로 저장하지 않습니다. 모든 정보는 `Claim`으로 저장됩니다. ```yaml subject: Product A predicate: hasTopNote object: Bergamot source: OfficialSite evidence_text: Top notes: Bergamot, Neroli confidence: 0.95 ``` 추천 시스템은 원문 복제가 아니라, 출처, 근거, 신뢰도, 갱신일을 가진 온톨로지 매핑 지식을 사용합니다. ## 아키텍처 ```mermaid flowchart LR A["URL Discovery"] --> B["Page Fetch"] B --> C["HTML Clean"] C --> D["Site Parser Plugin"] D --> E["Extractor Provider"] E --> F["Ontology Mapping"] F --> G["Deduplication"] G --> H["Confidence Scoring"] H --> I["Knowledge DB"] I --> J["Recommendation API"] I --> K["Update Scheduling"] ``` 구성 단위: - `Project`: 향수 구독, 차 구독, 선물 추천 같은 프로젝트 단위 설정 - `Source`: 공식몰, 마켓플레이스, 리뷰 사이트 같은 데이터 출처 - `Page`: 수집된 URL과 정제 텍스트 요약 - `Entity`: 상품, 브랜드, 노트, 무드, 계절, 상황 등 의미 객체 - `Claim`: 출처가 주장한 정보 단위 - `Evidence`: Claim의 근거 문장 - `Relation`: Entity 간 집계 관계 - `ExtractionLog`: 추출 방식, Provider, 로그 ## 폴더 구조 ```text crawler_platform/ app/ main.py config/ core/ crawler/ extractor/ ontology/ database/ recommendation/ scheduler/ domains/ perfume/ tea/ coffee/ candle/ supplement/ gift/ api/ cli/ configs/ perfume_subscription.yaml tests/ README.md ``` ## DB 스키마 초기 MVP는 SQLAlchemy ORM으로 SQLite와 PostgreSQL을 모두 지원합니다. 필수 테이블: - `projects`: 프로젝트 이름, 도메인, JSON 설정 - `sources`: 출처 타입, 신뢰도, robots 정책, rate limit - `pages`: URL, fetch 상태, content hash, 정제 텍스트 요약 - `entities`: 범용 의미 객체 - `attributes`: Entity 속성 - `relations`: Entity 간 집계 관계 - `claims`: 출처가 주장한 subject-predicate-object 정보 - `evidence`: Claim 근거 텍스트 - `extraction_logs`: 추출 Provider와 로그 - `crawl_jobs`: 예약 수집 작업 - `user_profiles`: 추천 사용자 - `user_preferences`: 취향 구조 - `feedback_logs`: 추천 피드백 ## 설치 ```bash pip install -r requirements.txt playwright install chromium ``` 정적 페이지는 `requests + BeautifulSoup`로 처리합니다. 동적 페이지가 필요하면 config에서 `fetcher: playwright`로 바꾸면 됩니다. ## CLI 사용 DB 초기화: ```bash python -m crawler_platform.app.cli.main --db sqlite:///crawler_platform.db init-db ``` 향수 프로젝트 생성: ```bash python -m crawler_platform.app.cli.main --db sqlite:///crawler_platform.db create-project --config configs/perfume_subscription.yaml ``` 온톨로지 조회: ```bash python -m crawler_platform.app.cli.main ontology --domain perfume ``` 단일 URL 수집: ```bash python -m crawler_platform.app.cli.main --db sqlite:///crawler_platform.db crawl-url \ --config configs/perfume_subscription.yaml \ --source official_brand_site \ --url https://example.com/perfume/product-page ``` 네트워크 없이 로컬 샘플 HTML로 파이프라인을 확인할 수도 있습니다. ```bash python -m crawler_platform.app.cli.main --db sqlite:///crawler_platform.db crawl-url \ --config configs/perfume_subscription.yaml \ --source official_brand_site \ --url tests/fixtures/sample_perfume.html ``` 분석기 Provider를 바꿀 수도 있습니다. 기본값은 규칙 기반이며, AI Provider는 모델/API 키 또는 로컬 서버 설정이 필요합니다. ```bash python -m crawler_platform.app.cli.main --db sqlite:///crawler_platform.db crawl-url \ --config configs/perfume_subscription.yaml \ --source official_brand_site \ --url tests/fixtures/sample_perfume.html \ --extractor-provider ollama \ --extractor-model llama3.1 ``` 지원 Provider: - `rule_based`: 정규식/키워드 기반 기본 분석기 - `openai`: OpenAI 호환 Chat Completions API, `OPENAI_API_KEY`와 모델 필요 - `ollama`: 로컬 Ollama, 기본 URL `http://localhost:11434/api/chat` - `lm_studio`: LM Studio OpenAI 호환 서버, 기본 URL `http://localhost:1234/v1/chat/completions` LM Studio 사용 순서: 1. LM Studio에서 `Developer` 또는 Local Server 화면을 엽니다. 2. OpenAI Compatible Server를 켭니다. 3. 서버 주소가 보통 `http://localhost:1234/v1`인지 확인합니다. 4. 웹 UI의 Analyzer에서 `LM Studio`를 선택합니다. 5. Base URL은 비워두거나 `http://localhost:1234/v1`을 넣습니다. 6. `연결 테스트`로 모델 목록을 불러옵니다. 7. 모델이 자동 입력되면 `수집 실행`을 누릅니다. Claim 확인: ```bash python -m crawler_platform.app.cli.main --db sqlite:///crawler_platform.db claims --project perfume_subscription ``` 추천 예시: ```bash python -m crawler_platform.app.cli.main --db sqlite:///crawler_platform.db recommend \ --project perfume_subscription \ --target-type Perfume \ --preferences-json "{\"preferred_notes\":[\"Bergamot\",\"Musk\"],\"preferred_moods\":[\"Fresh\"],\"season_context\":\"Summer\"}" ``` ## FastAPI 실행 ```bash uvicorn crawler_platform.app.main:app --reload ``` 브라우저에서 관리자 UI를 열 수 있습니다. ```text http://127.0.0.1:8000/ ``` 관리자 UI에서 가능한 작업: - 프로젝트 config 경로로 프로젝트 생성 - Source 선택 후 URL 또는 로컬 HTML 샘플 수집 - 분석기 Provider 선택: Rule-based, OpenAI, Ollama, LM Studio - 도메인 온톨로지, Entity, Claim 조회 - Claim 신뢰도와 사유 수동 수정 - Entity ID 기준 병합 - 추천용 태그 확인 - 사용자 취향 입력 후 추천 결과 테스트 주요 엔드포인트: - `GET /health` - `GET /` - `GET /projects` - `POST /projects` - `GET /ontology/{domain}` - `POST /crawl` - `POST /crawl-site` - `GET /projects/{project_name}/entities` - `GET /projects/{project_name}/claims` - `PATCH /claims/{claim_id}/confidence` - `POST /entities/merge` - `GET /projects/{project_name}/recommendation-tags` - `POST /recommend` ## 사이트 순회 수집 단일 상품 URL뿐 아니라 Seed URL에서 시작해 같은 도메인의 링크를 따라가는 수집도 지원합니다. ```text Seed URL → robots 확인 → 링크 추출 → same-domain 필터 → URL queue 저장 → depth / max pages 제한 → 각 페이지 fetch → 상품/브랜드/리뷰 페이지 판별 → 분석 → DB 저장 → 다음 링크 반복 ``` 웹 UI에서는 `Crawl site from seed`를 사용합니다. API 예시: ```bash curl -X POST http://127.0.0.1:8000/crawl-site \ -H "Content-Type: application/json" \ -d '{ "config_path": "configs/perfume_subscription.yaml", "source_name": "official_brand_site", "url": "https://example-brand.com", "extractor_provider": "rule_based", "max_depth": 2, "max_pages": 50, "same_domain_only": true, "analyze_page_types": ["product", "brand", "review"] }' ``` 주의: 검색 결과 페이지나 robots가 막는 페이지는 수집하지 않습니다. 그런 데이터는 공식 API Provider로 붙이는 방식이 맞습니다. ## 향수 도메인 MVP 기본 엔티티: - `Perfume` - `Brand` - `Note` - `Accord` - `Mood` - `Season` - `Occasion` - `Review` - `Price` - `ProductPage` 기본 관계: - `hasBrand` - `hasTopNote` - `hasMiddleNote` - `hasBaseNote` - `hasAccord` - `evokesMood` - `suitableForSeason` - `suitableForOccasion` - `similarTo` - `soldBy` - `hasPrice` - `hasReviewKeyword` 규칙 기반 추출기는 `Top notes`, `Middle notes`, `Base notes`, 가격, 무드, 계절, 사용 상황, 리뷰 키워드를 우선 추출합니다. ## 확장 방법 새 도메인을 추가할 때는 다음을 추가하면 됩니다. 1. `configs/{project}.yaml`에 `domain`, `target_entities`, `fields`, `sources`, `ontology` 정의 2. `crawler_platform/app/core/ontology/definitions.py`에 도메인 온톨로지 추가 3. 필요하면 `crawler_platform/app/domains/{domain}/extractor.py`에 도메인별 Extractor 구현 4. 사이트별 HTML 구조가 특수하면 `SiteParser`를 구현하고 `ParserRegistry`에 등록 5. OpenAI, Ollama, LM Studio 등 AI 추출은 `AIExtractor`를 상속하는 Provider로 추가 ## 컴플라이언스 설계 - `robots.txt` 확인 구조 포함 - Source별 rate limit과 User-Agent 적용 - retry, timeout 고려 - 원문 전체 저장 대신 `evidence_text`와 정제 요약 중심 저장 - 상품 설명은 복제 저장보다 Claim, 태그, 요약, 근거 중심으로 사용 ## 테스트 ```bash pytest ``` 외부 테스트 러너가 없을 때는 기본 `unittest` 스모크 테스트를 실행할 수 있습니다. ```bash python -m unittest tests.test_smoke_unittest -v ``` 현재 테스트는 향수 규칙 기반 추출, config 로더, 로컬 HTML fetch 경로를 검증합니다.