콘텐츠로 이동

수집기 (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-precedentsprecedents + 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별 추가 필드 (예: WrittenInquiryinquiry_year, inquiry_tax_category, inquiry_sequence 또는 Precedentcase_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-upload entry point는 stubpyproject.tomlsemugpt-upload = "semugpt_pipeline.cli.upload:app" 등록되어 있으나 cli/upload.py 파일이 존재하지 않아 실행 시 ImportError. 사용 안 함.
  • Selenium 의존 수집기는 Chrome 크래시 위험 — 대용량(written-inquiry 132K 건, nts-precedent 31개 법령)은 메모리 누수로 Chrome이 죽는다. save_every + progress 파일로 재개 가능하지만, 운영자 모니터링 필요.
  • SSL verify=False 영구화 — 정부 사이트 인증서 문제로 검증을 끄고 있어 MITM 위험은 있으나 공개 데이터라 영향 작음. 환경 변수로 토글 가능하게 개선 여지.
  • law.go.kr oc 키 하드코딩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 검색