수집기 (Collectors)¶
세무 법령·판례·예규·상담·회계기준 등 14곳의 외부 데이터 소스를 크롤링하여 JSONL/JSON으로 로컬에 저장하는 Python 모듈. BaseCollector 추상 클래스를 상속하여 source별로 구현되며, 정적 HTML은 requests, JavaScript 렌더링 페이지는 Selenium으로 처리한다. 대용량 수집은 JSONL 증분 저장 + progress 파일로 중단·재개를 지원한다.
수집기 카탈로그¶
수집기 (@app.command) |
클래스 | 출처 | 인덱스 | 비고 |
|---|---|---|---|---|
laws |
LawAPICollector |
law.go.kr (법제처 API) | tax-laws |
카테고리별 법령 XML/API, Selenium 불필요 |
precedents |
PrecedentCollector |
law.go.kr 공동활용 API | tax-precedents |
판례/예규, 세목별/키워드별 수집 |
counsel |
CounselCollector |
call.nts.go.kr (국세상담센터) | tax-counsel |
세목별 QnA, BeautifulSoup HTML 파싱 |
glossary |
GlossaryCollector |
taxlaw.nts.go.kr (세무용어사전) | tax-glossary |
약 1,718건, Selenium 내부 API 호출 |
enforcement |
EnforcementCollector |
taxlaw.nts.go.kr (집행기준) | tax-enforcement |
법령별 실무 기준, Selenium DOM 순회 |
basic-rules |
BasicRulesCollector |
taxlaw.nts.go.kr (기본통칙) | tax-basic-rules |
법령별 기본 해석, Selenium 일괄 추출 |
old-and-new |
OldAndNewCollector |
law.go.kr 신구법 비교 API | tax-oldnew |
현행 ↔ 직전법 조문 비교, XML API |
three-way |
ThreeWayComparisonCollector |
law.go.kr 3단 비교 API | tax-threeway |
법-시행령-시행규칙 매핑, JSON API |
written-inquiry |
WrittenInquiryCollector |
taxlaw.nts.go.kr (서면질의) | tax-written-inquiry |
약 132,000건, Selenium + JSONL 재개 |
nts-precedent |
NTSPrecedentCollector |
taxlaw.nts.go.kr/pd/ | tax-precedents 보강 |
31개 법령별 판례, 출력은 PrecedentIndexer 호환 |
tribunal |
TribunalCollector |
tt.go.kr (조세심판원) | tax-tribunal |
결정례, Selenium |
supreme-court |
SupremeCourtCollector |
law.go.kr 공동활용 API (target=prec) | tax-supreme-court |
대법원 세무 판례, XML API |
scourt |
ScourtCollector |
scourt.go.kr | tax-scourt |
'두' 사건 PDF → pdftotext 추출 |
taxoffice |
TaxofficeCollector |
taxoffice.co.kr (세림세무법인) | tax-taxoffice |
Topic/한페이지 게시판, requests + BeautifulSoup |
accounting |
AccountingStandardsCollector |
kasb.or.kr (한국회계기준원) | tax-accounting |
K-IFRS, 일반기업회계기준, 질의회신 |
전체 14개 ES 인덱스 중 tax-precedents는 precedents + nts-precedent 두 수집기가 함께 채운다. nts-precedent는 출력 형식이 PrecedentIndexer 호환이며, --convert 옵션으로 JSONL → JSON 변환.
사용자 여정¶
단일 수집 (예: 조세심판원)¶
sequenceDiagram
autonumber
participant U as 운영자
participant CLI as semugpt-collect tribunal
participant SE as Selenium / requests
participant SITE as 외부 사이트 (tt.go.kr)
participant FS as 로컬 디스크 (data/tribunal/)
U->>CLI: uv run semugpt-collect tribunal -n 100
CLI->>SE: TribunalCollector.collect(max_items=100)
SE->>SITE: 검색 결과 페이지 로드 (Selenium)
loop 각 결정례
SE->>SITE: 상세 페이지 진입
SE->>FS: JSONL append (save_every=50)
SE->>FS: progress file 갱신
end
SE-->>CLI: 수집 통계 (collected, errors)
CLI-->>U: rich progress 출력
중단 후 재개¶
sequenceDiagram
participant U as 운영자
participant CLI as semugpt-collect ...
participant PROG as data/{source}/.progress_*.json
participant FS as data/{source}/*.jsonl
U->>CLI: 동일 명령 재실행
CLI->>PROG: 마지막 진행 상태 로드 (items_collected, current_page)
CLI->>FS: JSONL 마지막 라인 부터 이어쓰기
Note over CLI: 이미 수집된 ID는 skip
CLI-->>U: "Resuming from page N (X items already collected)"
진행 상태 확인 / JSONL → JSON 변환¶
sequenceDiagram
participant U as 운영자
participant CLI
U->>CLI: semugpt-collect tribunal --progress
CLI-->>U: 현재 수집 건수 / 마지막 시각
U->>CLI: semugpt-collect tribunal --convert
Note over CLI: JSONL을 인덱서 호환 JSON으로 변환
CLI-->>U: data/tribunal/tribunal_data.json 생성
백엔드 구현¶
| 계층 | 클래스 / 파일 | 역할 |
|---|---|---|
| Abstract base | BaseCollector (packages/data-pipeline/.../collectors/base.py) |
requests.Session 초기화 (브라우저 UA, 쿠키 수집, SSL 우회), 재시도 백오프, save_json/save_csv/save_ndjson 유틸 |
| Collector (HTTP) | LawAPICollector, PrecedentCollector, OldAndNewCollector, ThreeWayComparisonCollector, SupremeCourtCollector, CounselCollector, TaxofficeCollector, ScourtCollector |
requests 기반, 정적 HTML / API |
| Collector (Selenium) | GlossaryCollector, EnforcementCollector, BasicRulesCollector, WrittenInquiryCollector, NTSPrecedentCollector, TribunalCollector, AccountingStandardsCollector |
JavaScript 렌더링 페이지, headless Chrome (옵션: pip install semugpt-data-pipeline[selenium]) |
| Patcher | NTSPrecedentPatcher (collectors/nts_precedent_patcher.py) |
기존 NTS 판례 JSONL에 ntstDcmId 후처리 추가 (AJAX API 또는 Selenium) |
| Patcher | TTPatcher (collectors/tt_patcher.py) |
조심/국심 케이스에 tt.go.kr 직접 URL 패치 (ES + JSONL 동시) |
| Selenium util | collectors/selenium_utils.py |
Chrome 드라이버 생성, 메모리 정리 헬퍼 |
| CLI entry | cli/collect.py |
typer 기반 명령 등록, 각 수집기 import 및 옵션 라우팅 |
도메인 규칙¶
| 규칙 | 위치 | 값 / 설명 |
|---|---|---|
| 기본 User-Agent | DEFAULT_USER_AGENT (module-level in base.py:47, used by BaseCollector) |
Chrome 131 (macOS) — 정부 API의 curl/python UA 차단 우회 |
| SSL 검증 끄기 | BaseCollector._request() |
verify=False — 일부 정부 사이트의 만료 인증서 대응. urllib3 InsecureRequestWarning 비활성화 |
| 세션 초기화 | BaseCollector._create_session(init_session=True) |
base URL 한 번 GET하여 쿠키 수집 → Referer 헤더 설정 |
| 재시도 정책 | BaseCollector._request() |
max_retries=3, 점진적 백오프 (delay * (attempt + 1)) |
| 요청 간 딜레이 | BaseCollector.delay |
기본 0.5s (settings에서 override) — 정부 사이트 rate limit 회피 |
| JSONL 증분 저장 | save_every 파라미터 |
10/20/50개마다 fsync. Chrome 크래시·중단 대비 |
| Progress 파일 | .progress_{source}.json |
items_collected, current_page, last_saved_at 저장 — 재실행 시 자동 로드 |
| JSONL → JSON 변환 | --convert 옵션 |
JSONL을 ES 인덱서 호환 JSON ({collected_at, count, items[]})으로 변환 |
| 병렬 수집 | --parallel N (NTS precedent / basic-rules) |
ThreadPoolExecutor로 법령 단위 병렬 수집 |
| 필수 JSONL 필드 | 수집기별 상이 | id, type, title, content, source, source_url, crawled_at (수집기마다 추가 메타) |
API 엔드포인트¶
해당 없음 — Collector는 외부 사이트를 호출하는 클라이언트이지 HTTP 서버가 아니다. CLI 명령 목록은 CLI 레퍼런스 참조.
데이터 모델¶
수집 결과는 ES 적재 전에 로컬 디스크에 저장되며, 인덱서가 다시 읽는 중간 포맷이다. 정규화된 RDB 스키마는 없다.
erDiagram
JSONL_RECORD {
string id "고유 식별자 (예: tribunal_2023001)"
string type "문서 타입 (예: tribunal_decision)"
string title "제목"
string content "본문 (full text)"
string source "출처 도메인 (예: tt.go.kr)"
string source_url "원본 URL"
string crawled_at "ISO datetime"
}
PROGRESS_FILE {
int items_collected
int current_page
string last_saved_at
}
JSON_BUNDLE {
string collected_at "ISO datetime"
int count
string items "JSONL_RECORD 배열"
}
JSONL_RECORD ||--|| PROGRESS_FILE : "1:1 source별"
JSONL_RECORD }o--|| JSON_BUNDLE : "여러 records가 하나의 bundle로 묶임 (--convert)"
각 source별 추가 필드 (예: WrittenInquiry의 inquiry_year, inquiry_tax_category, inquiry_sequence 또는 Precedent의 case_number, decision_date)는 indexer 매핑 단에서 정규화된다.
설정¶
| 항목 | 위치 | 비고 |
|---|---|---|
| 데이터 디렉토리 | Settings.data_dir (config/settings.py) |
기본 data/. 각 수집기는 data/{source}/ 하위에 저장 |
| 요청 딜레이 | Settings.request_delay |
기본 0.5초, env var SEMUGPT_REQUEST_DELAY 등으로 override |
| 요청 타임아웃 | Settings.timeout |
기본 30초 |
| 최대 재시도 | Settings.max_retries |
기본 3회 |
law.go.kr Open API 인증 (oc) |
Settings.law_api_oc |
기본값 "onbiztax" — supreme-court, precedent에서 사용 |
| Selenium 의존성 | optional extra selenium |
pip install semugpt-data-pipeline[selenium] 또는 uv sync --extra selenium |
| Chrome (headless) | 시스템 PATH | --headless/--no-headless 옵션. macOS는 별도 chromedriver 불필요 (webdriver-manager) |
pdftotext (scourt) |
시스템 PATH | scourt collector가 호출 — brew install poppler |
알려진 이슈 / 개선 예정¶
- 수집은 로컬 CLI 전용 — Celery/FastAPI/Docker worker가 모두 제거됨 (Memory: data-pipeline). 운영 시 정기 수집은 운영자가 직접
semugpt-collect명령을 실행하거나 cron으로 걸어야 한다. CI/CD 자동 수집은 미구현. semugpt-uploadentry point는 stub —pyproject.toml에semugpt-upload = "semugpt_pipeline.cli.upload:app"등록되어 있으나cli/upload.py파일이 존재하지 않아 실행 시 ImportError. 사용 안 함.- Selenium 의존 수집기는 Chrome 크래시 위험 — 대용량(
written-inquiry132K 건,nts-precedent31개 법령)은 메모리 누수로 Chrome이 죽는다.save_every+ progress 파일로 재개 가능하지만, 운영자 모니터링 필요. - SSL
verify=False영구화 — 정부 사이트 인증서 문제로 검증을 끄고 있어 MITM 위험은 있으나 공개 데이터라 영향 작음. 환경 변수로 토글 가능하게 개선 여지. law.go.kroc키 하드코딩 —Settings.law_api_oc기본값"onbiztax"가 노출됨. 공동활용 신청 키라 보안 영향은 작지만, env var 우선 권장.taxoffice수집 — 세림세무법인 자체 사이트 (taxoffice.co.kr) 크롤링. 사이트 구조 변경 시 셀렉터 파손 가능 — 사이트와 협의 후 정식 API 채널 있으면 교체 권장.- NTS 판례 ID 패치 (
patch-precedent-ids) — 초기 수집 후ntstDcmId누락 케이스가 있어 별도 후처리 명령으로 보강. AJAX API 방식(--use-api) 권장, Selenium fallback은 대용량 시 불안정.
관련 문서¶
- 인덱서 (Indexers) — 수집된 JSONL/JSON을 ES에 적재
- CLI 레퍼런스 — 각
semugpt-collect서브명령의 옵션 표 packages/data-pipeline/CLAUDE.md(리포 안 직접 참조) — 새 collector 추가 시 8단계 체크리스트- 리포트 생성 — 수집된 데이터를 소비하는 RAG 검색