15-crawler-patterns
웹 크롤링 파이프라인 패턴 라이브러리. 멀티소스 데이터 수집 → 정규화 → 중복제거 → 검증 → 저장의 5단계 파이프라인을 제공합니다.
출처
| 항목 | 내용 |
|---|
| 원본 프로젝트 | galaxy-con |
| 추출일 | 2026-03-08 |
| 버전 | 1.0.0 |
아키텍처
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 1. 수집 │ → │ 2. 정규화 │ → │ 3. 중복제거 │ → │ 4. 검증 │ → │ 5. 저장 │
│ (Sources) │ │ (Normalizer) │ │(Deduplicator)│ │ (Validator) │ │ (DB Writer) │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘
│ │ │ │ │
BaseSource korean-text deduplicator Zod 스키마 AbstractDbWriter
BaseApiSource global-text cross-source-dedup InMemoryDbWriter
fetcher currency-converter completeness
핵심 패턴
- 소스 어댑터 패턴:
BaseSource (HTML) / BaseApiSource (REST API) 추상 클래스
- 파이프라인 패턴: 5단계 순차 처리 (
run-pipeline.ts)
- 교차 소스 중복제거: 5계층 (키 → DB → URL → 제목유사도 → 지문)
- 의존성 주입: 소스 팩토리, DB Writer, 정규화 옵션 모두 외부 주입
파일 인덱스
| 파일 | 설명 |
|---|
types.ts | 공통 타입 (RawItem, NormalizedItem, CrawlResult, SourceAdapter 등) |
config.ts | 크롤러 공통 설정 + 소스별 설정 인터페이스 |
cli.ts | CLI 진입점 (인수 파싱 + 상태 출력 + 파이프라인 실행) |
| sources/ | |
sources/base-source.ts | HTML 크롤링용 소스 어댑터 추상 클래스 |
sources/base-api-source.ts | REST API용 소스 어댑터 추상 클래스 (Rate Limit + Retry 내장) |
| pipeline/ | |
pipeline/fetcher.ts | HTTP 클라이언트 (GET/POST, Rate Limit + Retry) |
pipeline/korean-text.ts | 한국어 텍스트 파서 (날짜, 상금, 제목 정규화, HTML 정리, 상태 판별) |
pipeline/global-text.ts | 영어 텍스트 파서 (날짜, 상금, 통화 감지) |
pipeline/currency-converter.ts | 환율 변환기 (고정 환율 + 선택적 API 갱신, 대상 통화 설정 가능) |
pipeline/normalizer.ts | Raw → Normalized 변환 (슬러그/카테고리/참가자격 함수 주입 가능) |
pipeline/deduplicator.ts | 배치 내 중복제거 + 유사 제목 비교 (Dice 계수) |
pipeline/cross-source-dedup.ts | 교차 소스 중복제거 (URL/제목/지문 5계층) + 충돌 병합 |
pipeline/completeness.ts | 데이터 완성도 점수 계산 (필드별 가중치) |
pipeline/validator.ts | Zod 스키마 검증 (카테고리 목록 주입 가능) |
pipeline/run-pipeline.ts | 5단계 파이프라인 실행 엔진 (소스팩토리/저장함수 주입) |
| storage/ | |
storage/db-writer.ts | DB 저장 추상 레이어 + InMemoryDbWriter (테스트용) |
의존성
| 패키지 | 용도 | 필수 여부 |
|---|
zod | 스키마 검증 (validator.ts) | 필수 |
선택적 환경변수:
OPEN_EXCHANGE_RATES_APP_ID: 실시간 환율 API 키 (미설정 시 고정 환율 사용)
사용 예시
1. 소스 어댑터 구현
import { BaseSource } from './15-crawler-patterns/sources/base-source';
import type { RawItem } from './15-crawler-patterns/types';
import type { SourceConfig } from './15-crawler-patterns/config';
const MY_SOURCE_CONFIG: SourceConfig = {
id: 'my-source',
name: '내 소스',
baseUrl: 'https://example.com',
listUrl: (page) => `https://example.com/items?page=${page}`,
detailUrl: (id) => `https://example.com/items/${id}`,
delay: 1500,
itemsPerPage: 20,
categories: {},
};
class MySource extends BaseSource {
constructor() {
super(MY_SOURCE_CONFIG);
}
async fetchList(page: number): Promise<RawItem[]> {
// HTML 파싱 로직 구현
return [];
}
async fetchDetail(item: RawItem): Promise<RawItem> {
// 상세 페이지 파싱 로직 구현
return item;
}
}
2. 파이프라인 실행
import { executePipeline } from './15-crawler-patterns/pipeline/run-pipeline';
const result = await executePipeline({
sourceId: 'my-source',
crawlOptions: { maxPages: 3, fetchDetails: true },
sourceFactory: (id) => new MySource(),
writeFn: async (items) => {
// DB 저장 로직
return { inserted: items.length, updated: 0, skipped: 0, errors: [] };
},
dryRun: false,
});
console.log(`수집: ${result.totalFound}, 저장: ${result.inserted}`);
3. CLI 실행
import { runCli } from './15-crawler-patterns/cli';
runCli({
sourceConfigs: { 'my-source': MY_SOURCE_CONFIG },
implementedSources: ['my-source'],
sourceFactory: (id) => new MySource(),
writeFn: myWriteFn,
projectName: '내 프로젝트 크롤러',
});
4. 텍스트 파서 단독 사용
import { parseKoreanDate, parsePrizeAmount } from './15-crawler-patterns/pipeline/korean-text';
import { parseEnglishDate, parseGlobalPrizeAmount } from './15-crawler-patterns/pipeline/global-text';
parseKoreanDate('2026년 4월 15일'); // Date
parsePrizeAmount('대상 1,000만원'); // 10000000
parseEnglishDate('March 15, 2026'); // Date
parseGlobalPrizeAmount('$50,000'); // { currency: 'USD', amount: 50000 }
커스터마이즈 포인트
| 항목 | 방법 |
|---|
| 소스 추가 | BaseSource 또는 BaseApiSource 상속 |
| 카테고리 매핑 | normalizer.ts의 categoryMapper 옵션 |
| 슬러그 생성 | normalizer.ts의 slugGenerator 옵션 |
| 참가 자격 | normalizer.ts의 eligibilityChecker 옵션 |
| 대상 통화 변경 | currency-converter.ts의 targetCurrency 매개변수 |
| DB 저장 | AbstractDbWriter 상속 또는 writeFn 직접 주입 |
| 검증 스키마 | validator.ts의 categories 옵션 |
| 완성도 가중치 | completeness.ts의 fieldWeights 매개변수 |