|
| 1 | +# DEKK Crawler |
| 2 | + |
| 3 | +## 데이터 파이프라인 아키텍처 |
| 4 | + |
| 5 | +```mermaid |
| 6 | +sequenceDiagram |
| 7 | + actor Crawler as 크롤링 서버 |
| 8 | + participant API as DEKK 서버 |
| 9 | +
|
| 10 | + Crawler->>API: 1️⃣ POST /batches<br/>{platform: "MUSINSA"} |
| 11 | + activate API |
| 12 | + API-->>Crawler: {batchId: 42}<br/>(상태: COLLECTING) |
| 13 | + deactivate API |
| 14 | +
|
| 15 | + loop 청크 단위 반복 전송 (20건씩) |
| 16 | + Crawler->>API: 2️⃣ POST /batches/42/raw-data<br/>[snap1, snap2, ...] |
| 17 | + activate API |
| 18 | + API-->>Crawler: 200 OK |
| 19 | + deactivate API |
| 20 | + end |
| 21 | +
|
| 22 | + Crawler->>API: 3️⃣ POST /batches/42/complete<br/>{totalCount: 87, completedAt: "..."} |
| 23 | + activate API |
| 24 | + API-->>Crawler: 200 OK<br/>(상태: COLLECTED) |
| 25 | + deactivate API |
| 26 | +``` |
| 27 | + |
| 28 | +--- |
| 29 | + |
| 30 | +## 프로젝트 구조 |
| 31 | + |
| 32 | +``` |
| 33 | +DEKK-CRAWLER/ |
| 34 | +├── main.py # 진입점 - Delta 크롤링 실행 (cron 주기 실행) |
| 35 | +├── initial_load.py # 최초 1회 대규모 수집 (약 1000건, 병렬) |
| 36 | +├── requirements.txt # Python 패키지 의존성 |
| 37 | +├── .env # 환경 변수 설정 |
| 38 | +├── .gitignore # Git 제외 파일 목록 |
| 39 | +│ |
| 40 | +├── Dockerfile # 크롤러 컨테이너 이미지 정의 |
| 41 | +├── docker-compose.yml # Docker 컨테이너 오케스트레이션 |
| 42 | +├── entrypoint.sh # 컨테이너 시작 스크립트 |
| 43 | +├── crontab # 크론 스케줄 설정 |
| 44 | +│ |
| 45 | +├── core/ # 핵심 기능 모듈 |
| 46 | +│ ├── __init__.py |
| 47 | +│ ├── config.py # 전역 설정 및 상수 정의 |
| 48 | +│ ├── logger.py # 통합 로깅 시스템 |
| 49 | +│ ├── pipeline.py # 크롤링 파이프라인 오케스트레이터 |
| 50 | +│ ├── s3_uploader.py # 이미지 및 JSON 백업 S3 업로드 |
| 51 | +│ ├── backup_handler.py # 원본 데이터 S3 백업 처리 (중복 제거) |
| 52 | +│ ├── state_manager.py # Delta Crawling 상태 관리 (last_snap_id) |
| 53 | +│ └── delivery/ # 데이터 전송 모듈 |
| 54 | +│ ├── __init__.py # Delivery 팩토리 |
| 55 | +│ ├── base.py # BaseDelivery 추상 클래스 |
| 56 | +│ └── batch.py # BatchDelivery 구현체 (3-step API 전송) |
| 57 | +│ |
| 58 | +├── crawlers/ # 플랫폼별 크롤러 |
| 59 | +│ ├── __init__.py |
| 60 | +│ ├── base.py # BaseCrawler 추상 클래스 |
| 61 | +└────── musinsa.py # MusinsaCrawler 구현체 |
| 62 | +
|
| 63 | +``` |
| 64 | + |
| 65 | +**주요 파일 설명**: |
| 66 | + |
| 67 | +| 파일/디렉토리 | 역할 | |
| 68 | +| ------------------------ | ----------------------------------------------------------- | |
| 69 | +| `main.py` | 크론 주기 실행 진입점, Delta Crawling 수행 | |
| 70 | +| `initial_load.py` | 최초 실행 시 대량 데이터 수집 (상태 파일 없을 때 자동 실행) | |
| 71 | +| `core/pipeline.py` | 크롤링 → 백업 → 전송 → 상태 갱신 전체 흐름 조율 | |
| 72 | +| `core/s3_uploader.py` | 이미지 WebP 변환/업로드, 원본 데이터 JSON.GZ 백업 | |
| 73 | +| `core/backup_handler.py` | 원본 데이터 추출 및 S3 백업 로직 (중복 제거) | |
| 74 | +| `core/state_manager.py` | 마지막 처리 ID 관리로 중복 수집 방지 | |
| 75 | +| `core/delivery/` | Batch API 3-step 전송 (배치 생성 → 데이터 전송 → 완료 통보) | |
| 76 | +| `crawlers/musinsa.py` | 무신사 스냅 크롤링 | |
| 77 | +| `entrypoint.sh` | 상태 파일 유무 확인 → 초기 수집 or cron 시작 분기 | |
| 78 | + |
| 79 | +--- |
| 80 | + |
| 81 | +## 실행 흐름 |
| 82 | + |
| 83 | +``` |
| 84 | +docker compose up |
| 85 | + └── entrypoint.sh |
| 86 | + ├── [최초] crawler_state.json 없음 |
| 87 | + │ └── initial_load.py 실행 (max_scrolls=40, ~1000건, 병렬 수집) |
| 88 | + │ └── 완료 후 last_snap_id 저장 |
| 89 | + └── cron 시작 |
| 90 | + └── */10 * * * * → main.py (신규 스냅만 delta 수집) |
| 91 | +``` |
| 92 | + |
| 93 | +--- |
| 94 | + |
| 95 | +## 상태 관리 (Delta Crawling) |
| 96 | + |
| 97 | +> ### Delta Crawling (동적 수집 분기 전략) |
| 98 | +> |
| 99 | +> - **최초 실행 시 (상태 파일 없음)**: `entrypoint.sh`가 `initial_load.py`를 자동 실행. Playwright로 최대 40회 스크롤하여 약 1000건 수집 |
| 100 | +> - **이후 실행 시 (상태 파일 있음)**: `last_snap_id`를 만날 때까지의 신규 스냅만 가볍게 수집 (10분 주기) |
| 101 | +
|
| 102 | +`/app/data/crawler_state.json`에 플랫폼별 마지막 처리 snap ID를 저장합니다. |
| 103 | + |
| 104 | +```json |
| 105 | +{ |
| 106 | + "MUSINSA": "12345678" |
| 107 | +} |
| 108 | +``` |
| 109 | + |
| 110 | +Docker volume(`./data:/app/data`)으로 마운트되어 컨테이너 재시작 시에도 상태가 유지됩니다. |
| 111 | + |
| 112 | +#### 데이터 유실 방지 (Data Loss Prevention) 및 멱등성 보장 |
| 113 | + |
| 114 | +- **상태 갱신 지연**: 크롤링 즉시 상태를 갱신하지 않습니다. |
| 115 | +- **안전한 커밋**: Batch API 전송이 최종적으로 성공(`complete` 호출 완료)했을 때만 `last_snap_id`를 갱신합니다. |
| 116 | +- **자동 복구**: 네트워크 오류 시 상태가 갱신되지 않으므로, 다음 크론 주기(10분 뒤)에 동일한 데이터를 안전하게 재수집하여 재전송을 시도합니다. |
| 117 | + |
| 118 | +--- |
| 119 | + |
| 120 | +## Batch API 전송 흐름 |
| 121 | + |
| 122 | +크롤러는 3단계로 데이터를 전송합니다: |
| 123 | + |
| 124 | +1. **배치 생성** `POST /batches` - 플랫폼 정보와 함께 배치 생성, `batchId` 수신 |
| 125 | +2. **데이터 전송** `POST /batches/{batchId}/raw-data` - 수집한 스냅 데이터를 청크(20건) 단위로 반복 전송 |
| 126 | +3. **완료 통보** `POST /batches/{batchId}/complete` - 전송 완료 및 총 개수 전달 |
| 127 | + |
| 128 | +--- |
| 129 | + |
| 130 | +## 로깅 |
| 131 | + |
| 132 | +`core/logger.py`에서 싱글턴 로거를 생성합니다. 로그는 `/app/data/` 하위에 기록됩니다. |
| 133 | + |
| 134 | +| 파일 | 내용 | |
| 135 | +| ----------------------- | ---------------------------------------- | |
| 136 | +| `/app/logs/crawler.log` | INFO 이상 전체 로그 | |
| 137 | +| `/app/logs/error.log` | ERROR 이상만 | |
| 138 | +| 콘솔 (stdout) | INFO 이상 전체 (Docker 로그로 확인 가능) | |
| 139 | + |
| 140 | +--- |
| 141 | + |
| 142 | +## 환경 변수 |
| 143 | + |
| 144 | +`.env` 파일을 프로젝트 루트에 생성하세요. |
| 145 | + |
| 146 | +```dotenv |
| 147 | +# Batch API 서버 주소 |
| 148 | +BATCH_API_URL=http://your-spring-boot-server/api |
| 149 | +
|
| 150 | +# Delivery 모드 (현재 BATCH만 지원) |
| 151 | +DELIVERY_MODE=BATCH |
| 152 | +
|
| 153 | +# AWS S3 (크롤링한 이미지 저장) |
| 154 | +AWS_REGION=ap-northeast-2 |
| 155 | +S3_BUCKET_NAME=your-bucket-name |
| 156 | +
|
| 157 | +# AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY는 EC2/ECS IAM Role 사용 시 불필요 |
| 158 | +# 로컬 테스트 시에만 직접 설정 |
| 159 | +``` |
| 160 | + |
| 161 | +--- |
| 162 | + |
| 163 | +## 실행 방법 |
| 164 | + |
| 165 | +```bash |
| 166 | +# 빌드 및 실행 |
| 167 | +# - 최초: initial_load.py로 ~1000건 수집 후 cron 시작 |
| 168 | +# - 재시작: 상태 파일이 있으면 바로 cron 시작 |
| 169 | +docker compose up -d --build |
| 170 | + |
| 171 | +# 로그 확인 |
| 172 | +docker logs -f integrated-crawler-worker |
| 173 | +``` |
| 174 | + |
| 175 | +--- |
| 176 | + |
| 177 | +## 의존성 |
| 178 | + |
| 179 | +| 패키지 | 버전 | 용도 | |
| 180 | +| ---------------- | ------- | -------------------------------------------------------------- | |
| 181 | +| `requests` | 2.32.5 | Batch API HTTP 요청 (배치 생성, 데이터 전송, 완료 통보) | |
| 182 | +| `boto3` | 1.42.56 | AWS S3 연동 (이미지 업로드 및 원본 데이터 JSON.GZ 백업) | |
| 183 | +| `beautifulsoup4` | 4.14.3 | HTML 파싱 (`__NEXT_DATA__` 스크립트 태그에서 JSON 추출) | |
| 184 | +| `playwright` | 1.58.0 | 무신사 스냅 목록 페이지 동적 스크롤 (Headless Chromium) | |
| 185 | +| `python-dotenv` | 1.0.1 | `.env` 파일에서 환경 변수 로딩 | |
| 186 | +| `curl_cffi` | 0.7.4 | TLS 핑거프린팅 우회 (무신사 스냅 상세 페이지 및 상품 API 호출) | |
| 187 | +| `Pillow` | 11.0.0 | 이미지 처리 (WebP 변환 및 리사이징) | |
0 commit comments