# 크롤링 방법론 이론과 논문 근거 > **이 문서의 역할**: DMF_Crawler 가 "매일 06:00 에 식약처 DMF 공고/현황을 1회 크롤링해 신규·변경·취하를 탐지한다"는 요구를 만족시키기 위해, 어떤 이론·논문·표준을 근거로 주기·politeness·파서 구조·diff 엔진·LLM 역할 경계를 정했는지 확정하는 단일 정본(SSOT) 이다. --- ## 0. 한눈에 보기 이 문서가 최종적으로 내린 결론이다. 근거는 각 섹션에 있다. - **크롤러 아키텍처는 Mercator(Heydon & Najork, 1999)의 6요소로 축소 적용한다.** URL Frontier → DNS → Protocol Module(HTTP) → RIS(재읽기 가능 스트림) → Content-Seen Test(fingerprint) → URL-Seen Test(DUE). 우리 규모(호스트 1개, URL 수십~수백)에서는 frontier 를 "우선순위 있는 리스트 + 호스트별 직렬 큐" 로, content-seen 을 "레코드 지문 테이블" 로 축소한다. **체크포인트(checkpointing)는 규모와 무관하게 반드시 구현한다** — Mercator 가 장기 실행 프로세스의 필수 요소로 지목한 항목이고, 우리는 재부팅 자동 복구를 요구사항으로 갖고 있기 때문이다. - **재방문 정책은 Cho & Garcia-Molina(TODS 2003)의 결론에 따라 "균등(uniform) 고정 주기"를 채택한다.** 이들은 웹 페이지 변경이 Poisson 과정으로 잘 모델링되며(변경 간격이 λe^(−λt) 지수분포), **균등 정책이 비례(proportional) 정책보다 평균 freshness 가 높다**는 반직관적 결과를 증명했다. 우리처럼 "매일 06:00 1회"는 이론적으로 정당한 선택이며, 자주 바뀐다고 특정 페이지만 더 자주 긁는 최적화는 하지 않는다. - **하루 1회(I = 1일) 주기에서 게시판당 기대 freshness 는 F̄ = (1 − e^(−λI))/(λI), 기대 age 는 Ā = I/2 − 1/λ + (1 − e^(−λI))/(λ²I) 로 계산한다.** DMF 공고처럼 λ ≈ 0.2~1 건/일 수준이면 F̄ ≈ 0.90~0.63, Ā ≈ 0.08~0.42일이다. 즉 "최악의 경우 하루 늦게 감지"가 설계상 허용 오차이며, 이 값을 SLA 로 문서화한다. - **λ(변경률)는 매일 관측 결과로 온라인 추정한다.** n회 방문 중 X회에서 변경이 관측되면, 구간 검열(interval-censored) Poisson 의 최우추정은 λ̂ = −ln(1 − X/n)/I 이다. λ̂ 가 급등하면(예: 평소의 3배) 그날은 "이상 급증" 플래그를 리포트에 띄운다. 주기 자체는 바꾸지 않는다(균등 정책 유지). - **본 추출은 100% 결정론적 파서가 담당하고, LLM 은 (a) 셀렉터 후보 생성, (b) 셀렉터 검증/복구 제안, (c) 사람이 읽을 요약문 작성 세 가지만 담당한다.** 근거: AutoScraper(EMNLP 2024)·AXE(2026)·"Automatic XPath generation agents"(2025) 모두 **LLM 이 매 페이지를 읽는 구조는 비용·재현성 면에서 실패**하고 **LLM 이 한 번 만든 XPath/wrapper 를 반복 실행하는 구조가 이긴다**는 동일한 결론에 도달했다. Prompt2DAG(2025)는 결정론적 템플릿 방식 92.3% vs 하이브리드 78.5% 성공률로 결정론 우위를 정량화했다. - **셀렉터는 ROBULA+(Leotta et al., JSEP 2016) 원칙으로 작성한다.** 절대 XPath 대비 취약성 90% 감소, Selenium IDE 로케이터 대비 63% 감소. 실무 규칙: `id`/`data-*`/`itemprop`/ARIA role/헤더 텍스트 앵커 우선, 시각적 class 와 깊은 경로 체인 금지, "앵커 노드 기준 상대 XPath"(Huang & Song 2025) 사용. - **schema drift(셀렉터 조용한 파손)는 RAPTURE(Kushmerick, AAAI 1999) 방식으로 매 실행마다 자동 검증한다.** 필드별 통계 특징(레코드 수, 문자열 길이 평균/분산, 숫자 비율, 날짜 파싱 성공률, null 비율)의 과거 분포 대비 이탈 확률을 계산해 임계 이하이면 크롤 결과를 **채택하지 않고** 알림을 띄운다. Lerman/Minton/Knoblock(JAIR 2003)은 이 접근으로 37건 파손 중 35건 탐지(precision 0.73 / recall 0.95)를 달성했다. - **중복·변경 판정은 2단 구조다.** ① 레코드 식별키(DMF 등록번호 등) 기반 upsert 로 신규/변경/취하를 확정하고, ② 상세 본문 텍스트에는 64-bit SimHash + Hamming 거리 k=3 (Manku·Jain·Das Sarma, WWW 2007 검증값) 을 적용해 "의미 없는 표기 흔들림"과 "실질 변경"을 구분한다. 저장은 **스냅샷 + 이벤트 로그 병행**(Fowler, Event Sourcing 2005 / Kimball SCD Type 2): 현재 상태 테이블과 append-only 변경 이벤트 테이블을 둘 다 유지한다. - **robots.txt 는 RFC 9309(2022-09, Koster·Illyes·Zeller·Sassman) 를 그대로 따른다.** 캐시 24시간 이내, 5xx 면 **완전 금지로 간주**, 4xx 면 접근 허용, 파싱 한계 최소 500 KiB, product token 은 `a-z A-Z _ -` 만. **crawl-delay 는 RFC 9309 에 정의되지 않았고 Google 도 지원하지 않는다** — 따라서 crawl-delay 가 있으면 "존중하되", 없어도 우리 자체 하한(2초)을 반드시 건다. - **한국 법적 리스크는 낮으나 0은 아니다.** 대법원 2022. 5. 12. 선고 2021도1533 판결(숙박앱 크롤링 무죄)은 "보호조치 없고 이용약관상 제한이 비회원에 미치지 않는 공개 정보"의 크롤링에 대해 정보통신망법 침입·저작권법 DB제작자 권리 침해·업무방해를 모두 부정했다. 반대로 서울남부지법 2021. 9. 8. 선고 2021고단588 판결은 **회원 계정 로그인 + 약관상 금지 명시** 상황에서 유죄를 선고했다. → **로그인하지 않는다. 약관·robots 를 매 실행 확인한다. 서버 부하를 유발하지 않는다.** 이 세 가지가 우리의 법적 안전선이다. --- ## 1. 목차 - [0. 한눈에 보기](#0-한눈에-보기) - [1. 목차](#1-목차) - [2. 논문·문헌 마스터 표](#2-논문문헌-마스터-표) - [3. 크롤러 아키텍처 고전 — 프론티어·politeness·중복 제거·재방문](#3-크롤러-아키텍처-고전--프론티어politeness중복-제거재방문) - [4. 증분 크롤링과 변경 감지 이론](#4-증분-크롤링과-변경-감지-이론) - [5. 구조적 데이터 추출 — wrapper induction 부터 셀렉터 안정성까지](#5-구조적-데이터-추출--wrapper-induction-부터-셀렉터-안정성까지) - [6. LLM 기반 추출 최신 연구(2023~2026)와 역할 분리 원칙](#6-llm-기반-추출-최신-연구20232026와-역할-분리-원칙) - [7. 중복·변경 탐지 알고리즘 — SimHash/MinHash/키 기반 upsert/스냅샷 vs 이벤트 로그](#7-중복변경-탐지-알고리즘--simhashminhash키-기반-upsert스냅샷-vs-이벤트-로그) - [8. robots.txt RFC 9309 규범과 politeness 실무](#8-robotstxt-rfc-9309-규범과-politeness-실무) - [9. 한국 문헌·선행 시스템·법적 맥락](#9-한국-문헌선행-시스템법적-맥락) - [10. 이 프로젝트의 크롤링 정책 확정안](#10-이-프로젝트의-크롤링-정책-확정안) - [부록 A. 출처 목록](#부록-a-출처-목록) - [부록 B. 미해결 질문 / 실측 필요 항목](#부록-b-미해결-질문--실측-필요-항목) --- ## 2. 논문·문헌 마스터 표 raw dump 에서 확인된 **모든** 논문·표준·기술문서를 한 표에 모았다. "확인" 열은 리서치 에이전트가 WebFetch 로 **실제 본문을 열어 확인**했는지 여부다(✅ = 본문 확인, 🔶 = 검색 결과 메타데이터만, ❌ = 접근 실패). ### 2.1 크롤러 아키텍처 고전 | # | 제목 | 저자 | 연도 | 게재처 | URL | 확인 | 이 프로젝트에 주는 시사점 | |---|---|---|---|---|---|---|---| | A1 | Mercator: A scalable, extensible Web crawler | Allan Heydon, Marc Najork (Compaq SRC, Palo Alto) | 1999 (1999-06-26) | World Wide Web journal, Vol. 2, No. 4, pp. 219–229 | https://link.springer.com/article/10.1023/A:1019213109274 | 🔶 | 확장 가능한 크롤러의 **부품 목록**을 정의한 원전이다. URL Frontier / DNS Resolver / Protocol Module / RIS / Content-Seen Test / URL-Seen Test(DUE) / Processing Module 이라는 분해는 규모와 무관하게 유효하므로, 우리 코드도 이 7개 모듈 이름을 그대로 파이썬 모듈명으로 쓴다. "동일 웹서버에 동시 다운로드 금지"라는 최소 politeness 정의도 여기서 온다. | | A2 | High-Performance Web Crawling (SRC Research Report 173) | Marc Najork, Allan Heydon | 2001-09-26 | Compaq Systems Research Center, SRC Research Report 173 (26 pages) | https://www.cs.cornell.edu/courses/cs685/2002fa/mercator.pdf | ✅ | Mercator 후속 보고서로 **frontier 의 front-end/back-end 2단 구조**를 명시한다. front-end 는 우선순위 k개 FIFO 큐, back-end 는 n개 FIFO 큐이며 각 back-end 큐는 "한 호스트의 URL만" 담아 강한 politeness 를 보장한다. URL-Seen 은 Rabin fingerprint 8바이트 체크섬 해시테이블(10억 URL ≈ 5GB), robots.txt 는 호스트→규칙 LRU 캐시 기본 2^18 엔트리. 백그라운드 스레드가 기본 10초마다 깨어나 통계 로깅·종료 조건 확인·체크포인트를 수행한다 → 우리의 스케줄러/헬스체크 루프 설계를 그대로 이 패턴으로 만든다. | | A3 | An Introduction to Heritrix: an open source archival quality web crawler | Gordon Mohr, Michael Stack, Igor Ranitovic, Dan Avery, Michele Kimpton | 2004-07 | Proc. 4th International Web Archiving Workshop (IWAW'04), Bath, UK, pp. 109–115 | https://docs.huihoo.com/heritrix/An-Introduction-To-Heritrix.ppt | 🔶 | Internet Archive 의 아카이브 품질 크롤러. "정중함(politeness)을 설정으로 노출한다"는 운영 철학이 핵심이다. 우리는 Heritrix 를 쓰지 않지만 **politeness 파라미터를 코드에 하드코딩하지 않고 설정 파일로 노출**하는 원칙을 그대로 가져온다. | | A4 | Heritrix3 (소스 저장소) | Internet Archive | 현재 | GitHub, Java, Apache License 2.0 | https://github.com/internetarchive/heritrix3 | ✅ | robots.txt 및 META nofollow 준수, 크롤 잡(crawl job) 단위 설정, WARC 출력, Frontier 큐잉이 4대 개념. WARC 개념은 우리에게 **"원본 HTML 스냅샷을 날짜별로 보존한다"**는 요구로 번역된다(디버깅·소급 재파싱·법적 증빙 용도). | | A5 | Heritrix 설정 문서 (configuring-jobs) | Internet Archive | 현재 | heritrix.readthedocs.io | https://heritrix.readthedocs.io/en/latest/configuring-jobs.html | ✅ | 실제 politeness 프로퍼티 이름과 기본값을 확인했다: `delayFactor`(기본 5.0, "직전 URI 를 가져오는 데 걸린 시간의 배수"), `minDelayMs`(delayFactor 계산값보다 우선하는 최소 대기), `maxDelayMs`(기본 30000ms), `maxPerHostBandwidthUsageKbSec`, `robotsPolicyName`(obey / classic / robotsTxtOnly / ignore, 기본 obey, RFC 9309 경로 와일드카드 지원), `metadata.operatorContactUrl`, `maxToeThreads`, `extract404s`. **우리의 백오프 정책은 delayFactor=5.0, minDelayMs=2000, maxDelayMs=30000 을 그대로 채택한다.** | | A6 | Web Crawling (서베이) | Christopher Olston (Yahoo! Research), Marc Najork (Microsoft Research) | 2010 | Foundations and Trends in Information Retrieval, Vol. 4, No. 3, pp. 175–246, DOI 10.1561/1500000017 | https://doi.org/10.1561/1500000017 | ✅ (초록) | "웹 크롤링은 BFS 의 단순 응용처럼 보이지만 실제로는 초대형 자료구조 관리 같은 시스템 문제부터 **'변화하는 콘텐츠를 얼마나 자주 재방문할 것인가'** 같은 이론 문제까지 걸쳐 있다"는 문장이 이 프로젝트의 전체 난이도 지도를 요약한다. 우리 문제는 시스템 축은 거의 0(호스트 1개)이고 **재방문·변경 탐지 축이 전부**임을 이 서베이가 정당화한다. | | A7 | A Brief History of Web Crawlers | Seyed M. Mirtaheri, Mustafa Emre Dinçktürk, Salman Hooshmand, Gregor V. Bochmann, Guy-Vincent Jourdan, Iosif Viorel Onut | 2014 | arXiv:1405.0749 | https://arxiv.org/abs/1405.0749 | ✅ (초록만) | 크롤러 평가 기준을 정립하려는 서베이. 초록 수준에서만 확인되어 Mercator/Heritrix/Cho 관련 상세 서술은 확인하지 못했다(⚠️ 본문 미확인). 배경 읽기용으로만 참조한다. | | A8 | Web crawler (Wikipedia) — Re-visit policy / Politeness policy / Crawler identification | — | 현재 | Wikipedia | https://en.wikipedia.org/wiki/Web_crawler | ✅ | 실무 crawl-delay 값의 **실측 레퍼런스**: Mercator 계열은 "직전 다운로드 소요 시간의 10배"를 기다리는 적응형, Cho 는 10초, WIRE 는 기본 15초, 실제 관측된 접근 간격은 20초~3–4분. freshness 는 0/1 이진값, age 는 마지막 수정 이후 경과 시간. "최적 정책은 비례보다 균등에 가깝다", "특정 페이지 접근은 가능한 한 균등 간격으로 배치해야 한다"(Coffman et al.), 변경은 지수분포로 잘 모델링된다. **우리 하한값 2초는 이 스펙트럼의 보수적 끝단이 아니므로, DMF 게시판이 응답이 느릴 경우 delayFactor 로 자동 확대되게 한다.** | ### 2.2 증분 크롤링 · 변경 감지 이론 | # | 제목 | 저자 | 연도 | 게재처 | URL | 확인 | 이 프로젝트에 주는 시사점 | |---|---|---|---|---|---|---|---| | B1 | The Evolution of the Web and Implications for an Incremental Crawler | Junghoo Cho, Hector Garcia-Molina | 2000 | Proc. 26th VLDB, pp. 200–209, Morgan Kaufmann | https://dl.acm.org/doi/10.5555/645926.671679 | ❌ (403) | 웹 변경 이력을 실측해 **증분(incremental) 크롤러 아키텍처**를 제안한 원전. 핵심 대비는 "주기적 크롤(periodic: 전체를 새로 긁고 통째로 교체)" vs "증분 크롤(incremental: 변경분만 갱신, 로컬 컬렉션을 항상 유지)". 우리는 매일 전체 목록을 긁되 **저장은 증분 upsert** 로 하는 하이브리드를 택한다. ⚠️ ACM 403 및 Semantic Scholar 429 로 본문 미확인 — 인용 시 2차 출처 기준. | | B2 | Effective Page Refresh Policies for Web Crawlers | Junghoo Cho, Hector Garcia-Molina | 2003-12 | ACM Transactions on Database Systems (TODS), Vol. 28, Issue 4, pp. 390–426, DOI 10.1145/958942.958945 (Semantic Scholar citationCount 314) | https://dl.acm.org/doi/10.1145/958942.958945 | 🔶 (메타데이터 API 로 확인) | **이 프로젝트의 주기 정책 근거 1순위.** ① Poisson 과정이 웹 페이지 변경을 잘 기술한다 — 변경률 λ 인 페이지의 변경 간격은 지수분포 λe^(−λt). ② 제안된 refresh 정책이 freshness 를 유의하게 개선한다. ③ **균등(uniform) 정책이 비례(proportional) 정책을 이긴다** — 자주 바뀌는 페이지에 자원을 몰아주면 오히려 평균 freshness 가 떨어진다. 우리는 "매일 06:00 전 게시판 균등 1회"를 이 결과로 정당화한다. ⚠️ 원문 PDF(oak.cs.ucla.edu, ACM)는 접속 실패 — 수식은 표준 유도로 §4.2 에 재구성. | | B3 | Tractable near-optimal policies for crawling | Yossi Azar (Tel-Aviv Univ.), Eric Horvitz (MSR), Eyal Lubetzky (NYU Courant), Yuval Peres (MSR), Dafna Shahaf (HUJI) | 2018-07-23 | PNAS Vol. 115, Issue 32, pp. 8099–8103, DOI 10.1073/pnas.1801519115 | https://www.pnas.org/doi/10.1073/pnas.1801519115 | ✅ (PMC 전문) | 문제 정식화가 우리에게 그대로 쓸 수 있다: 페이지별 **Poisson 변경률 Δᵢ**, **요청률 μᵢ**, **총 폴링 대역폭 제약 R**. 최적 무작위 정책은 "utility/change-rate 비로 정렬 후 할당 0인 페이지를 골라내기"로 O(n log n)에 구해지며, EDF(earliest-deadline-first)로 비무작위화한 결정론 정책이 최적해의 **99%** 성능을 낸다. → 우리가 나중에 대상 게시판을 여러 개로 늘릴 때, "중요도(μ)/변경률(Δ) 비로 정렬 후 상위 몇 개만 하루 2회" 같은 확장을 이 알고리즘으로 정당화할 수 있다. 지금은 n 이 작아 균등으로 충분하다. | | B4 | Staying up to Date with Online Content Changes Using Reinforcement Learning for Scheduling | Andrey Kolobov, Yuval Peres, Cheng Lu, Eric J. Horvitz | 2019 | Advances in Neural Information Processing Systems 32 (NeurIPS 2019) | https://papers.nips.cc/paper/2019/hash/ad13a2a07ca4b7642959dc0c4c740ab6-Abstract.html | ✅ | **변경 관측이 불완전하고(mixed content change observability) 변경 모델 파라미터를 처음엔 모를 때**도 최적성 보장이 있는 스케줄링. 18.5M URL 을 14주간 매일 크롤한 실험으로 검증. → 우리도 λ 를 사전에 모르는 상태에서 시작하므로, "관측하면서 λ 를 온라인 갱신한다"는 §4.6 의 설계가 이 논문 계열의 표준 관행임을 근거로 삼는다. ⚠️ harmonic policy 등 세부 알고리즘은 초록 수준까지만 확인. | | B5 | A Scalable Crawling Algorithm Utilizing Noisy Change-Indicating Signals | Róbert Busa-Fekete, Julian Zimmert, András György, Linhai Qiu, Tzu-Wei Sung, Hao Shen, Hyomin Choi, Sharmila Subramaniam, Li Xiao | 2025-02-04 (rev. 2025-03-20) | arXiv:2502.02430 | https://arxiv.org/abs/2502.02430 | ✅ | sitemap·CDN 시그널 같은 **잡음 섞인 부가 정보**(오탐도 있고 실제 변경을 놓치기도 함)를 최적으로 활용하는 크롤 스케줄링. Azar et al. 2018 의 "변경·요청이 독립 Poisson" 가정을 완화한다. → DMF 게시판의 "총 게시물 수", "최근 게시일", RSS/Atom, `Last-Modified` 헤더가 정확히 이 **noisy change-indicating signal** 이다. 우리는 이것들을 **1차 필터**로 쓰되 절대 신뢰하지 않고, 목록 페이지 1페이지는 항상 실제로 긁는다. | | B6 | 웹 사이트 컨텐츠 변경 모니터링 시스템 (The Monitoring System for Informing the Change of Contents on the Web Sites) | 김원중, 조이기, 손철수 | 2002 | 한국정보통신학회논문지 제6권 제4호, pp. 505–512 | https://www.kci.go.kr/kciportal/ci/sereArticleSearch/ciSereArtiView.kci?sereArticleSearchBean.artiId=ART000881131 | ✅ | 국내 최초급 변경 모니터링 시스템 논문. 구성은 우리와 동일한 3요소다: ① **HTML 태그를 이용해 웹 문서를 의미 있는 단위로 구조화·분류**해 변경을 감지, ② 사용자 정의 모니터링 주기, ③ 변경 시 알람/E-mail 자동 통지. → "전체 페이지 diff" 가 아니라 **구조화된 단위(레코드) diff** 가 정답이라는 것이 20년 전에 이미 확립됐음을 보여준다. 우리 diff 엔진이 레코드 단위인 이유. | | B7 | 실시간 웹 크롤링 분산 모니터링 시스템 설계 및 구현 (Design and Implementation of Real-time Web Crawling Distributed Monitoring System, R-WCMS) | 김영아, 김계희, 김현주, 김창근 (경남과학기술대학교 컴퓨터공학과) | 2019 | 융합정보논문지 Vol. 9, No. 1, pp. 45–53 | https://scienceon.kisti.re.kr/srch/selectPORSrchArticle.do?cn=JAKO201909258120005 | ✅ | Apache Kafka + Spark Streaming + Hadoop 으로 수집 시간 15–17% 단축. **우리에게 주는 시사점은 반대 방향이다** — 단일 게시판 일 1회 감시에 이런 스택은 명백한 과잉이다. "수집 시간 예측을 통한 효율화" 아이디어만 취해, 우리는 실행 소요 시간을 기록해 이상 지연(예: 평소의 3배)을 헬스체크 알림 조건으로 쓴다. | | B8 | 실시간 웹 게시판 모니터링 및 모바일웹을 이용한 알람 서비스 개발 | 김종근, 심근호, 이요셉, 임영환 | 2012 | 디지털콘텐츠학회논문지 제13권 제1호, pp. 1–11 | https://www.kci.go.kr/kciportal/ci/sereArticleSearch/ciSereArtiView.kci?sereArticleSearchBean.artiId=ART001648031 | ✅ | 기존 방식(DB 직접 접근 / 공개 API)의 한계 = **비공개 게시판 접근 불가, 실시간 알림 어려움**. 이메일 대신 모바일 웹 알림으로 전환. → 우리 프로젝트에서 "알림 채널"을 xlsx 리포트 하나에만 걸지 말고, 서비스 사망 시 **Windows 토스트 알림**이라는 별도 out-of-band 채널을 갖는 설계가 선행 연구와 일치함을 보여준다. | | B9 | 웹 크롤링 모니터링 시스템 및 방법 (특허) | — | 등록 | KR101757822B1 (Google Patents) | https://patents.google.com/patent/KR101757822B1/ko | 🔶 | 검색 결과로만 확인. ⚠️ 청구항 미확인 — 상용화 계획이 없는 사내 도구이므로 침해 리스크 평가는 보류하되, 존재는 기록해 둔다. | | B10 | 결정 이론 웹 크롤링, 및 웹 페이지 변경의 예측 (특허) | — | 등록 | KR101213930B1 (Google Patents) | https://patents.google.com/patent/KR101213930B1/ko | 🔶 | 검색 결과로만 확인. 제목상 Azar/Kolobov 계열의 결정이론 스케줄링에 대응. ⚠️ 청구항 미확인. | | B11 | Clustering-based incremental web crawling | — | 2010 | ACM Transactions on Information Systems | https://dl.acm.org/doi/10.1145/1852102.1852103 | 🔶 | 검색 결과로만 확인. 유사 변경 패턴 페이지를 군집화해 증분 크롤 효율을 올리는 계열. n 이 작은 우리에겐 적용 대상 아님. | | B12 | A Dynamic Page-Refresh Index Policy for Web Crawlers | — | 2014 | Springer LNCS (978-3-319-08219-6_4) | https://link.springer.com/chapter/10.1007/978-3-319-08219-6_4 | 🔶 | 검색 결과로만 확인. refresh 정책 후속 연구로 기록만 남긴다. | | B13 | Towards a Quality-Oriented Real-Time Web Crawler | — | 2010 | Springer LNCS (978-3-642-16515-3_10) | https://link.springer.com/chapter/10.1007/978-3-642-16515-3_10 | 🔶 | 검색 결과로만 확인. | | B14 | Management Of Volatile Information In Incremental Web Crawler | — | 2009 | arXiv:0910.1869 | https://arxiv.org/pdf/0910.1869 | 🔶 | 검색 결과로만 확인. | | B15 | Learning to Crawl | — | 2019 | arXiv:1905.12781 | https://arxiv.org/pdf/1905.12781 | 🔶 | 검색 결과로만 확인. Kolobov 계열 RL 크롤 스케줄링. | | B16 | Online Learning for Active Cache Synchronization | — | 2020 | arXiv:2002.12014 | https://arxiv.org/pdf/2002.12014 | 🔶 | 검색 결과로만 확인. | | B17 | Look back, look around: a systematic analysis of effective predictors for new outlinks in focused Web crawling | — | 2021 | arXiv:2111.05062 | https://arxiv.org/pdf/2111.05062 | 🔶 | 검색 결과로만 확인. "새 링크(=새 공고)를 예측하는 신호"라는 주제가 우리 문제와 유사하나 focused crawling 문맥. | ### 2.3 구조적 데이터 추출 (wrapper induction 계열) | # | 제목 | 저자 | 연도 | 게재처 | URL | 확인 | 이 프로젝트에 주는 시사점 | |---|---|---|---|---|---|---|---| | C1 | Wrapper Induction for Information Extraction | Nicholas Kushmerick, Daniel S. Weld, Robert B. Doorenbos | 1997-08 (IJCAI-97, Nagoya, Japan, Aug 23–29) | Proc. 15th International Joint Conference on Artificial Intelligence (IJCAI), pp. 729–737 | https://www.semanticscholar.org/paper/Wrapper-Induction-for-Information-Extraction-Kushmerick-Weld/f9e7402ad740b73cc0bb64178f86df3478c3aaf5 | 🔶 | wrapper 자동 생성 개념의 원전. **hlrt** wrapper 클래스는 효율적으로 학습 가능하면서 당시 조사 대상 인터넷 리소스의 **48%** 를 처리할 만큼 표현력이 있었다. PAC 분석으로 표본 복잡도를 상한하고, 라벨링이 불완전해도 성능이 완만히 저하됨을 보였다. 가장 단순한 클래스는 **LR wrapper**(문서를 문자열로 보고 좌/우 구분자로 필드를 잘라냄). → "필드 앞뒤의 안정적인 구분자(레이블 텍스트)로 값을 자른다"는 우리 fallback 파서의 이론적 근거. Weld 개인 출판 목록에서도 IJCAI-97 항목이 확인되며, **Artificial Intelligence 저널 버전은 확인되지 않았다**(⚠️ 저널 버전 존재 미확인). | | C2 | The Wrapper Induction Environment (WIEN) | Nicholas Kushmerick (Dublin City University) | 1998 | AAAI Workshop WS-98-10, pp. 022– | https://cdn.aaai.org/Workshops/1998/WS-98-10/WS98-10-022.pdf | 🔶 | WIEN 은 문서를 문자 시퀀스로 보고 여러 wrapper 언어 클래스를 정의한다(가장 단순한 것이 LR). | | C3 | Regression testing for wrapper maintenance (RAPTURE) | Nicholas Kushmerick (University College Dublin) | 1999 | Proc. 16th National Conference on Artificial Intelligence (AAAI-99), 논문번호 AAAI99-011, 6 pages | https://cdn.aaai.org/AAAI/1999/AAAI99-011.pdf | ✅ (PDF 텍스트 추출) | **schema drift 탐지의 원전이며 우리 검증 로직의 직접 설계도.** 원문 인용: "The wrapper verification problem is to determine whether a wrapper is correct. Standard regression testing approaches are inappropriate, because both the formatting regularities and a site's underlying content may change." RAPTURE 는 도메인 독립·완전 구현된 휴리스틱 검증 알고리즘으로, wrapper 의 **기대 출력과 관측 출력 사이의 유사도**를 계산한다. 27개 실제 인터넷 사이트 실험에서 표준 회귀 테스트 대비 큰 성능 향상. 비교 대상인 STRAWMAN 은 "같은 질의에 대해 과거 정상 페이지와 현재 페이지 출력을 비교"하는 단순 방식이다. → **우리는 STRAWMAN(전날 결과와 오늘 결과 직접 비교)이 아니라 RAPTURE(통계적 특징 분포 비교)를 써야 한다.** 게시판 내용 자체가 매일 바뀌는 게 정상이기 때문이다. | | C4 | Wrapper Maintenance: A Machine Learning Approach | Kristina Lerman, Steven N. Minton, Craig A. Knoblock | 2003 | Journal of Artificial Intelligence Research (JAIR), Vol. 18, pp. 149–181 | https://arxiv.org/abs/1106.4872 | ✅ | 양성 예시만으로 데이터의 구조 정보를 학습하는 효율적 알고리즘 → 두 응용: **wrapper verification**(파손 탐지)과 **wrapper reinduction**(자동 복구). 실측: 27개 wrapper 를 1년간 추적해 37건의 파손 중 **35건 탐지, precision 0.73 / recall 0.95**. reinduction 은 10개 소스에서 precision 0.90 / recall 0.80. → **파손 탐지는 recall 을 높이고(0.95) precision 은 희생해도 된다**(오탐 = 사람이 한 번 확인하면 끝, 미탐 = 잘못된 리포트가 배포됨)는 임계값 정책을 이 수치가 정당화한다. | | C5 | RoadRunner: Towards Automatic Data Extraction from Large Web Sites | Valter Crescenzi, Giansalvatore Mecca, Paolo Merialdo | 2001 | Proc. 27th VLDB, Rome, Italy | http://www.vldb.org/conf/2001/P109.pdf | ✅ (PDF 텍스트 추출) | **완전 자동 wrapper 추론.** 핵심: 데이터 집약 사이트의 페이지는 백엔드 DBMS 내용을 스크립트가 찍어낸 것이므로, **같은 클래스의 페이지 2장을 비교**하면 템플릿(상수)과 데이터(변수)를 분리할 수 있다. 형식화는 **union-free regular expression(UFRE)**: 알파벳 Σ ∪ {#PCDATA, ·, +, ?, (, )} 위의 문자열로, `a·b`, `(a)+`, `(a)?` 로 구성되며 `(a)* = ((a)+)?`. UFRE ↔ nested type 대응(#PCDATA→string, +→list, ?→nullable). 알고리즘 **match(σ₁, σ₂)** = 두 UFRE 의 least upper bound 계산, 매칭 기법 이름은 **ACME (Align, Collapse under Mismatch, and Extract)**. 페이지를 XHTML 로 정규화 후 토큰 리스트(태그 or 문자열)로 만들고, page 1 을 초기 wrapper 로 삼아 page 2(sample)를 파싱하며 mismatch 를 풀어 일반화한다. mismatch 는 두 종류: **string mismatch → 필드(#PCDATA) 발견**(예: 'John Smith' vs 'Paul Jones' → #PCDATA 로 일반화하되 'Books of:' 같은 상수는 필드가 아님), **tag mismatch → iterator 또는 optional 발견**(먼저 반복 패턴을 찾고 실패하면 optional 로 처리; cross-search 로 optional 이 wrapper 쪽인지 sample 쪽인지 판별 후 `()?` 형태로 일반화). → **우리 크롤러의 "LLM 셀렉터 생성" 단계는 사실상 RoadRunner 를 LLM 으로 대체하는 것이다.** 따라서 LLM 에게도 "목록 페이지 2장 이상을 주고 공통 템플릿을 찾게" 하고, 상수/변수 구분과 optional 필드 존재를 명시적으로 물어야 한다. | | C6 | Automatic Wrappers for Large Scale Web Extraction | Nilesh Dalvi (Yahoo! Research), Ravi Kumar (Yahoo! Research), Mohamed Soliman (Univ. of Waterloo) | 2011-03-12 | Proceedings of the VLDB Endowment (PVLDB), Vol. 4, No. 4, pp. 219–230 | https://arxiv.org/abs/1103.2406 | ✅ | **잡음 섞인 학습 데이터로도 동작하는** wrapper induction 프레임워크. 사전(dictionary)·정규식으로 자동 생성한 저비용·저품질 주석만으로 비지도 wrapper 학습이 가능해져 사이트별 사람 감독 없이 웹 스케일로 확장. Yahoo! 프로덕션 투입. → **우리도 "정답 라벨"을 사람이 만들 필요가 없다**: DMF 등록번호 패턴(정규식), 날짜 형식, 회사명 사전 같은 약한 신호로 필드를 자동 라벨링해 셀렉터 후보를 검증할 수 있다. ⚠️ 시간적 구조 변화 대한 강건성 평가 방법은 초록 수준에서 확인되지 않음. | | C7 | Robust web extraction: an approach based on a probabilistic tree-edit model | Nilesh Dalvi, Philip Bohannon, Fei Sha | 2009 | Proc. ACM SIGMOD 2009, Providence, Rhode Island, USA | https://www.researchgate.net/publication/221214620_Robust_web_extraction_An_approach_based_on_a_probabilistic_tree-edit_model | 🔶 | 스크립트 생성 사이트의 페이지들은 공통 HTML 트리 구조를 공유하므로 wrapper 가 잘 동작하지만, **스크립트와 트리 구조가 시간에 따라 진화하면 wrapper 가 깨져 유지보수 비용이 커진다**는 문제를 확률적 tree-edit 모델로 다룬다. ⚠️ raw dump 의 검색 결과가 저자 조합(Dalvi/Kumar/Soliman vs Dalvi/Bohannon/Sha)을 혼동해 서술하므로, **저자는 Dalvi·Bohannon·Sha 로 표기하되 미검증**으로 둔다. | | C8 | Robula+: An algorithm for generating robust XPath locators for web testing | Maurizio Leotta, Andrea Stocco, Filippo Ricca, Paolo Tonella | 2016 | Journal of Software: Evolution and Process (JSEP), Vol. 28, Issue 3, pp. 177–204, DOI 10.1002/smr.1771 | https://onlinelibrary.wiley.com/doi/10.1002/smr.1771 | ✅ | **셀렉터 안정성의 정량적 근거.** "test code fragility problem" — 애플리케이션이 진화하면 로케이터가 깨지고 수동 수리 비용이 든다. Robula+ 가 생성한 XPath 는 **절대 로케이터 대비 평균 90%, Selenium IDE 로케이터 대비 63% 취약성 감소**. 현재 강건 XPath 자동 생성의 state of the art 로 평가된다. → 우리 셀렉터 작성 규칙(§5.6)의 근거이자, 셀렉터 자동 생성 도구를 붙일 때의 알고리즘 선택. | | C9 | robula-plus (TypeScript 구현) | Cyluxx | 현재 | GitHub | https://github.com/cyluxx/robula-plus | ✅ | API: `getRobustXPath(element, document)`, `getElementByXPath(xPath, document)`, `uniquelyLocate(xPath, element, document)`. Node.js 필요, `npm install` → `npm run build`. **라이선스가 미정이라 공개 install 패키지가 없다**("The License of this code needs some clarification, so until then there will be no public install package available") → **의존성으로 채택 불가.** 알고리즘 아이디어만 우리 코드에 재구현한다. | | C10 | robula-plus (Python 포팅) | ZeusFSX | 현재 | GitHub | https://github.com/ZeusFSX/robula-plus | 🔶 | 파이썬 버전 존재만 확인. ⚠️ 라이선스·유지보수 상태 미확인 — 채택 전 실사 필요. | | C11 | WebTables: Exploring the Power of Tables on the Web | Michael J. Cafarella, Alon Halevy, Daisy Zhe Wang, Eugene Wu, Yang Zhang | 2008 | Proceedings of the VLDB Endowment, Vol. 1, No. 1, pp. 538–549, DOI 10.14778/1453856.1453916 | http://www.vldb.org/pvldb/vol1/1453916.pdf | 🔶 | 웹에서 **141억 개 테이블**을 추출해 통계적 분류로 **1억 5,400만 개**만이 고품질 관계형 데이터임을 밝혔다(약 1.1%). 나머지는 레이아웃용. → **`` 을 봤다고 데이터 테이블이라 가정하지 마라.** 우리 파서는 헤더 행 존재, 열 수 일관성, 셀 타입 동질성 같은 "관계형성 판정"을 먼저 통과시킨 뒤 파싱해야 한다. | | C12 | Schema Extraction for Tabular Data on the Web | Marco D. Adelfio, Hanan Samet | 2013 | PVLDB Vol. 6 | http://www.vldb.org/pvldb/vol6/p421-adelfio.pdf | 🔶 | 웹 테이블에서 스키마(헤더 행 식별 포함)를 추출하는 기법. 우리 헤더 판정 로직 참고용. | | C13 | On Extracting Data from Tables that are Encoded using HTML | — | 2019 | arXiv:1903.08305 | https://arxiv.org/pdf/1903.08305 | 🔶 | rowspan/colspan 처리 등 HTML 테이블 파싱의 실무 함정 정리. | | C14 | Web Table Extraction, Retrieval and Augmentation: A Survey | — | 2020 | arXiv:2002.00207 | https://arxiv.org/pdf/2002.00207 | 🔶 | 웹 테이블 처리 전반 서베이. | | C15 | Identifying Web Tables: Supporting a Neglected Type of Content on the Web | — | 2015 | arXiv:1503.06598 / Springer | https://arxiv.org/pdf/1503.06598 | 🔶 | 레이아웃 테이블 vs 데이터 테이블 분류. C11 의 실무 보완. | | C16 | An Annotated Corpus of Webtables for Information Extraction Tasks | — | 2020 | arXiv:2008.07680 | https://arxiv.org/pdf/2008.07680 | 🔶 | 웹 테이블 주석 코퍼스. | | C17 | Structured Data Extraction: Wrapper Generation (교재 챕터) | — | 2011 | Springer (978-3-642-19460-3_9) | https://link.springer.com/chapter/10.1007/978-3-642-19460-3_9 | 🔶 | wrapper 생성 전반의 교과서적 정리. | | C18 | Schema-guided wrapper maintenance for web-data extraction | — | 2003 | Proc. 5th ACM Intl. Workshop on Web Information and Data Management (WIDM) | https://dl.acm.org/doi/abs/10.1145/956699.956701 | 🔶 | 스키마를 힌트로 wrapper 를 유지보수. 우리가 "필드 스키마를 JSON Schema 로 명시"할 때의 이론적 지지. | | C19 | Intelligent Self-repairable Web Wrappers | — | 2011 | Springer (978-3-642-23954-0_26) | https://link.springer.com/chapter/10.1007/978-3-642-23954-0_26 | 🔶 | 자가 수리 wrapper. LLM 기반 자동 복구의 전신. | | C20 | Leveraging Flexible Tree Matching to Repair Broken Locators in Web Automation Scripts | — | 2021 | arXiv:2106.04916 | https://arxiv.org/pdf/2106.04916 | 🔶 | 깨진 로케이터를 트리 매칭으로 복구. LLM 없이도 셀렉터 복구가 가능한 경로. | | C21 | Robust wrappers for web extraction (미국 특허) | — | 등록 | US 8,762,829 | https://image-ppubs.uspto.gov/dirsearch-public/print/downloadPdf/8762829 | 🔶 | 검색 결과로만 확인. | | C22 | Web data extraction, applications and techniques (서베이) | — | 2014 | Knowledge-Based Systems | https://dl.acm.org/doi/abs/10.1016/j.knosys.2014.07.007 | 🔶 | 웹 데이터 추출 전반 서베이. | ### 2.4 LLM 기반 추출 · 웹 에이전트 (2023~2026) | # | 제목 | 저자 | 연도 | 게재처 | URL | 확인 | 이 프로젝트에 주는 시사점 | |---|---|---|---|---|---|---|---| | D1 | AutoScraper: A Progressive Understanding Web Agent for Web Scraper Generation | Wenhao Huang, Zhouhong Gu, Chenghao Peng, Zhixu Li, Jiaqing Liang, Yanghua Xiao, Liqian Wen, Zulong Chen | 2024 (arXiv 2024-04-19 제출, 2024-09-26 개정) | EMNLP 2024 Main, arXiv:2404.12753, ACL Anthology 2024.emnlp-main.141 | https://arxiv.org/abs/2404.12753 | ✅ | **"LLM 으로 스크레이퍼를 생성한다"는 패러다임을 정의한 논문.** 2단계 프레임워크(progressive generation + synthesis)가 HTML 의 계층 구조와 페이지 간 유사성을 활용한다. 새 평가지표 **executability** 제안. 주장: wrapper 기반 방법은 적응성이 낮고, language agent 방법은 재사용성이 낮은데 AutoScraper 는 둘 다 이긴다. GPT-4-Turbo 로 SWDE 에서 zero-shot 임에도 지도학습 방법보다 높은 F1. → **우리 아키텍처의 정당화 그 자체**: LLM 은 스크레이퍼를 "생성"하고, 실행은 생성된 결정론적 스크레이퍼가 한다. | | D2 | AutoScraper (구현체) | EZ-hwh | 현재 | GitHub, Python, Apache License 2.0, **492 stars / 45 forks / 12 watchers** | https://github.com/EZ-hwh/AutoScraper | ✅ | `crawler_generation.py` 로 스크레이퍼를 만들고 `crawler_extraction.py` 로 추출 실행. LLM(ChatGPT/GPT-4)에 **reflexion 패턴** 적용. 평가는 SWDE 벤치마크 + DS1 + Klarna. README 에 "실제 웹사이트 적응"과 "공개 데모"가 TODO 로 남아 있어 **연구 코드이지 프로덕션 코드가 아니다** → 의존성으로 채택하지 않고, 2단계 분리 아이디어와 reflexion 루프만 차용한다. | | D3 | Automatic XPath generation agents for vertical websites by LLMs | Jing Huang, Jie Song | 2025 | Journal of King Saud University Computer and Information Sciences, DOI 10.1007/s44443-025-00071-w | https://link.springer.com/article/10.1007/s44443-025-00071-w | ✅ | **우리 "셀렉터 생성" 파이프라인의 최적 설계도.** 3단계 분해: ① **속성 추출** — HTML 을 먼저 Markdown 으로 변환해 LLM 이해도를 높인 뒤 **seed page 2~3장**에서 목표 정보를 뽑는다. ② **목표 노드 위치 특정** — 추출된 텍스트를 포함하는 후보 DOM 노드를 찾고, **perturbation testing**(각 노드의 텍스트를 변조해 LLM 출력이 바뀌는지 관찰; 바뀌면 그 노드가 정답)으로 확정. ③ **XPath 생성** — 구조 변화에 취약한 절대 경로 대신 **anchor node(주변의 구별되는 요소) 기준 상대 XPath** 를 만든다. 결과: **F1 86.83–92.46**, 비교 대상 대비 **14.84–23.86%p** 우위. LLM 호출 수는 직접 추출의 n 회 대비 **n_s + p·n_s + 1 회**(n_s = seed 페이지 수) — 페이지가 10개만 넘어도 큰 절감. → 우리는 seed 페이지 3장, perturbation 검증, anchor 기반 상대 XPath 를 그대로 채택한다. | | D4 | AXE: Low-Cost Cross-Domain Web Structured Information Extraction | Abdelrahman Mansour, Khaled W. Alshaer, Moataz Elsaban | 2026-02-02 제출 (2026-03-30 개정) | arXiv:2602.01838 | https://arxiv.org/abs/2602.01838 | ✅ | "HTML DOM 을 읽어야 할 텍스트 덩어리가 아니라 **가지치기해야 할 트리**로 취급한다"가 논지. 3요소: ① **DOM Pruning**(보일러플레이트·무관 노드 제거로 고밀도 컨텍스트 생성), ② **0.6B 파라미터 소형 LLM** 으로 구조화 출력 생성, ③ **Grounded XPath Resolution (GXR)** — 추출된 모든 값이 **소스 노드로 물리적으로 추적 가능**하도록 보장. 결과: **SWDE F1 88.1%** zero-shot 으로 더 크고 완전 학습된 모델을 상회. → **GXR 은 우리 감사(audit) 요구와 정확히 일치한다**: xlsx 리포트의 모든 셀은 "어느 URL 의 어느 노드에서 왔는가"를 역추적할 수 있어야 한다. 또한 **DOM pruning 을 LLM 호출 전에 결정론적으로 수행**해 토큰과 비용을 줄인다. | | D5 | Co-Scraper: query-aware DOM Pruning and Reusable Scraper Synthesis for Lightweight Web Data Extraction | Shoupeng Wang, Jiantao Qiu, Wuyang Zhang, Conghui He | 2026-06-12 제출 | arXiv:2606.14821 | https://arxiv.org/abs/2606.14821 | ✅ | 유사 페이지에 **재사용 가능한 스크레이퍼**를 생성하는 2단계(질의 기반 DOM pruning + 추출 전략 귀납). 파인튜닝된 LM 이 HTML 을 실행 가능한 wrapper 로 변환. **F1 94.78%, 재사용 성공률 90.39%.** → "재사용 성공률"이라는 지표를 우리도 도입한다: 어제 만든 셀렉터가 오늘도 동작한 비율을 매일 기록하고, 이 값이 떨어지면 셀렉터 재생성을 트리거한다. | | D6 | Mind2Web: Towards a Generalist Agent for the Web | Xiang Deng, Yu Gu, Boyuan Zheng, Shijie Chen, Sam Stevens, Boshi Wang, Huan Sun, Yu Su | 2023 | NeurIPS 2023, Datasets and Benchmarks Track (Spotlight) | https://papers.nips.cc/paper_files/paper/2023/hash/5950bf290a1570ea401bf98882128160-Abstract-Datasets_and_Benchmarks.html | ✅ | 137개 웹사이트·31개 도메인에서 **2,000개 이상의 open-ended 태스크**와 크라우드소싱 액션 시퀀스. 핵심 기술적 교훈: "**실제 웹사이트의 raw HTML 은 LLM 입력 한계를 초과하므로, 먼저 소형 LM 으로 필터링하면 LLM 의 효과성과 효율성이 크게 개선된다**"(MindAct 의 2단계 구조). → 우리에게 그대로 적용: **LLM 에 HTML 전체를 넣지 마라.** 관련 서브트리만 잘라서 넣는다(D4 의 DOM pruning 과 동일한 결론에 독립적으로 도달). ⚠️ MindAct 의 후보 랭킹·객관식 액션 예측 세부와 cross-task/website/domain 분할 수치는 공식 페이지에서 확인 실패. | | D7 | WebVoyager: Building an End-to-End Web Agent with Large Multimodal Models | Hongliang He, Wenlin Yao 외 6인 | 2024 (arXiv 2024-01-25 최초, 2024-06-06 최종) | ACL 2024 (Main), arXiv:2401.13919 | https://arxiv.org/abs/2401.13919 | ✅ | 스크린샷(시각) + 텍스트를 함께 처리하는 멀티모달 웹 에이전트. 15개 실제 사이트 태스크에서 **성공률 59.1%** — GPT-4(All Tools) 및 텍스트 전용 WebVoyager 를 상회. GPT-4V 자동 평가는 사람 판단과 **85.3% 일치**. 태스크는 Mind2Web 태스크를 시드로 변형·생성. → **59.1% 는 프로덕션 자동화에 쓸 수 없는 수치다.** "브라우저를 LLM 이 직접 조종해 매일 데이터를 긁는다"는 설계는 이 논문이 반증한다. 우리는 브라우저 조종을 **최초 1회 셀렉터 발견/디버깅**에만 쓰고, 일상 실행은 HTTP + 결정론적 파서로 한다. | | D8 | OpenWebVoyager: Building Multimodal Web Agents via Iterative Real-World Exploration, Feedback and Optimization | — | 2024 | arXiv:2410.19609 | https://arxiv.org/pdf/2410.19609 | 🔶 | WebVoyager 오픈 계열. 검색 결과로만 확인. | | D9 | The AI Committee: A Multi-Agent Framework for Automated Validation and Remediation of Web-Sourced Data | Sunith Vallabhaneni, Thomas Berkane, Maimuna Majumder | 2025-12-25 제출 | arXiv:2512.21481 | https://arxiv.org/abs/2512.21481 | ✅ | LLM 에이전트의 전형적 실패를 명시적으로 나열한다: **"값을 환각하거나 누락, 페이지 의미 오해, 무효 정보 탐지 실패"**. 각 에이전트가 출처 검증·팩트체크 등 별개 품질보증 과업을 맡는 다중 에이전트로 3개 실세계 데이터셋에서 **완전성 최대 78.7%, 정밀도 최대 100%**(태스크별 학습 없이). 오픈소스 공개. → **완전성 78.7% 라는 숫자가 "LLM 에게 본 추출을 맡기면 안 되는" 결정적 근거다.** 규제 데이터(DMF)에서 21%의 누락은 허용 불가. LLM 은 **검증자(validator)** 역할일 때만 가치가 있다. | | D10 | DELM: a Python toolkit for Data Extraction with Language Models | — | 2025 | arXiv:2509.20617 | https://arxiv.org/pdf/2509.20617 | 🔶 | 동시 실행, 재시도 로직, 그리고 **완전히 렌더된 프롬프트·스키마·모델·생성 파라미터를 키로 하는 결정론적 캐싱**. → 우리도 LLM 호출에 동일한 캐시 키를 쓴다: `sha256(prompt + schema + model_id + temperature + top_p)`. 같은 입력에 두 번 과금하지 않고, 리포트 재현성도 확보된다. | | D11 | A Reliability Evaluation of Hybrid Deterministic-LLM Based Approaches for Academic Course Registration PDF Information Extraction | — | 2026 | ResearchGate / arXiv:2604.00003 (Tabular PDF Information Extraction with Local LLMs and Layout-Aware Parsing) | https://arxiv.org/pdf/2604.00003 | 🔶 | 세 전략 비교: **LLM-only / Hybrid Deterministic-LLM(정규식 + LLM) / Camelot 파이프라인 + LLM fallback**. 결론: 하이브리드가 LLM-only 대비 효율이 좋고, **결정론적 메타데이터에서 특히 그렇다**. → DMF 등록번호·날짜·업체명처럼 형식이 고정된 필드는 **절대 LLM 에 맡기지 않는다.** 정규식/파서로 뽑고, LLM 은 자유서술 필드(변경 사유 요약 등)에만 쓴다. | | D12 | Prompt2DAG: A Modular Methodology for LLM-Based Data Enrichment Pipeline Generation | — | 2025 | arXiv:2509.13487 | https://arxiv.org/html/2509.13487v1 | 🔶 | **결정론적 Template 기반 방법이 성공률 92.3% 로 최고**, 생성형 중에서는 Hybrid 가 78.5% 로 최고 품질. → 파이프라인 자체를 LLM 이 매번 만들게 하지 말고, **템플릿(코드)을 고정하고 파라미터(셀렉터·필드 매핑)만 LLM 이 채우게** 한다. | | D13 | A Hybrid LLM and Supervised Model Pipeline for Polymer Property Extraction from Tables in Scientific Literature | — | 2025 | ACL Anthology 2025.wasp-main.11 | https://aclanthology.org/2025.wasp-main.11/ | 🔶 | 테이블 추출에서 LLM + 지도학습 모델 하이브리드. 도메인은 다르나 "테이블에서 규제/과학 데이터 추출" 이라는 구조가 우리와 동일. | | D14 | RATE: An LLM-Powered Retrieval Augmented Generation Technology-Extraction Pipeline | — | 2025 | arXiv:2507.21125 | https://arxiv.org/html/2507.21125v1 | 🔶 | 검색 결과로만 확인. | | D15 | ZeroShotCeres: Zero-Shot Relation Extraction from Semi-Structured Webpages | — | 2020 | ACL | https://www.researchgate.net/publication/343297191_ZeroShotCeres_Zero-Shot_Relation_Extraction_from_Semi-Structured_Webpages | 🔶 | LLM 이전의 zero-shot 반구조 웹 추출. SWDE 계열 비교 기준선. | | D16 | From one tree to a forest: a unified solution for structured web data extraction | — | 2011 | SIGIR | https://www.researchgate.net/publication/221299838_From_one_tree_to_a_forest_a_unified_solution_for_structured_web_data_extraction | 🔶 | 검색 결과로만 확인. | ### 2.5 중복·유사도 탐지, 저장 모델 | # | 제목 | 저자 | 연도 | 게재처 | URL | 확인 | 이 프로젝트에 주는 시사점 | |---|---|---|---|---|---|---|---| | E1 | Detecting Near-Duplicates for Web Crawling | Gurmeet Singh Manku (Google), Arvind Jain (Google), Anish Das Sarma (Stanford) | 2007 | Proc. 16th International World Wide Web Conference (WWW 2007) | https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/33026.pdf | ✅ (PDF 텍스트 추출) | **우리 near-duplicate 파라미터의 출처.** 원문 기여 3가지: (A) Charikar 의 simhash 가 다중 십억 페이지 저장소의 근접 중복 식별에 실용적임을 입증하고, **80억(8B) 웹페이지 저장소에 대해 64-bit simhash 지문과 k=3 이 합리적임을 실험으로 검증**. (B) **Hamming Distance Problem** 해법: f-bit 지문 집합에서 주어진 지문과 최대 k 비트 다른 것을 빠르게 찾기. (C) 중복 탐지 알고리즘 서베이. 크기 이점: Broder 의 shingle 기반 지문은 지문당 **24바이트**가 필요한 반면 simhash 는 **8바이트(64bit)** 로 충분. simhash 의 두 상충 성질: (A) 문서 지문은 그 특징들의 "해시"이고 (B) **유사 문서는 유사한 해시값**을 갖는다(암호학적 해시엔 없는 성질). 소박한 해법의 비용: 64-bit·k=3 이면 정렬 테이블 탐색에 C(64,3) = **41,664 회 프로브** 필요 → 실용 알고리즘은 순열 + 블록 분할(예: 64비트를 11,11,11,11,10,10 의 6블록으로 나눠 3개 선택 = C(6,3)=**20개 테이블**, pᵢ=31/32/33, 프로브당 평균 2^(34−31)=8개 지문 회수; 또는 16bit×4 블록 → **16개 테이블**, pᵢ=28, 프로브당 2^(34−28)=64개; 또는 13,13,13,13,12 의 5블록에서 2개 선택 = **10개 테이블**). 압축: 연속 지문이 상위 d 비트를 공유하므로 XOR 최상위 1비트 위치에 Huffman 코드(블록 크기 B 전형값 1024바이트)를 적용해 테이블 크기를 약 절반으로. | | E2 | Similarity Estimation Techniques from Rounding Algorithms (SimHash 원전) | Moses Charikar | 2002 | Proc. 34th Annual ACM Symposium on Theory of Computing (STOC 2002) | https://en.wikipedia.org/wiki/SimHash | ✅ (Wikipedia 경유) | 알고리즘: 입력을 특징들로 분해 → 각 특징을 해시 → 비트 위치별로 1의 개수와 0의 개수를 세어(가중치 적용) 1이 우세하면 최종 해시의 그 비트를 1, 아니면 0. 유사 입력 → **Hamming 거리가 작은 해시**. 전체 문서 비교(O(n²)) 없이 정렬만으로 유사 항목 발견 가능. ⚠️ STOC 2002 원문 PDF 는 Semantic Scholar 429 로 직접 확인 실패. | | E3 | On the Resemblance and Containment of Documents (MinHash 원전) | Andrei Z. Broder | 1997 | Compression and Complexity of Sequences 1997, IEEE, pp. 21–29 | https://www.semanticscholar.org/paper/On-the-resemblance-and-containment-of-documents-Broder/8addb1718c2bc6bbb0d82cd1a57b41198bf65965 | 🔶 (Wikipedia 로 보완 확인) | **shingling**(텍스트를 단어 n-gram 으로 자름) + **resemblance = Jaccard 유사도**, **containment**, **min-wise independent permutations 스케치**. 핵심 성질: `P(h_min(A) = h_min(B)) = J(A,B)`. 표본 내 공통 shingle 수는 초기하분포. **AltaVista** 검색엔진의 중복 웹페이지 제거에 실제 사용. LSH 스킴이므로 근접 이웃 검색·군집화에 사용 가능(banding). → **우리는 SimHash 를 1순위로 쓴다**(지문 8바이트, 짧은 게시판 레코드에 적합). MinHash 는 첨부파일/장문 상세 페이지에 대해 **집합 유사도가 필요할 때**의 대안으로 남긴다. | | E4 | python-simhash | scrapinghub | 아카이브됨 (2026-06-24) | GitHub, Python + C 확장(GCC), **BSD-3-Clause**, 127 stars / 29 forks / 11 watchers | https://github.com/scrapinghub/python-simhash | ✅ | 함수: `fingerprint()`(해시 시퀀스에서 지문 생성), `hamming_distance()`, `simpair_indices()`(임계 내 유사 해시 탐색), `fnvhash()`(FNV-1a). 사용 예: `hash1 = fingerprint(map(hash, "some text we want to hash"))` → `hamming_distance(hash1, hash2)` → `2`. **아카이브된 저장소이므로 의존성으로 채택하지 않고**, 순수 파이썬 60줄로 직접 구현한다(§7.1 코드). BSD-3-Clause 라 참고·재구현에 제약 없음. | | E5 | Probabilistic near-duplicate detection using simhash | Sood, Loguinov | 2011 | Proc. 20th ACM CIKM | https://dl.acm.org/doi/10.1145/2063576.2063737 | 🔶 | simhash 의 확률적 확장. 검색 결과로만 확인. | | E6 | Event Sourcing | Martin Fowler | 2005-12-12 | martinfowler.com (Enterprise Application Architecture) | https://martinfowler.com/eaaDev/EventSourcing.html | ✅ | 정의: "애플리케이션 상태의 **모든 변경을 이벤트의 시퀀스로 포착**한다." 두 지속 요소 = **이벤트 로그**와 **애플리케이션 상태**. 상태는 이벤트에서 완전히 유도 가능하므로 **스냅샷은 성능 최적화이지 대체물이 아니다**. 기능: complete rebuild(상태를 버리고 전 이벤트 재실행), event replay(잘못된 이벤트를 되돌리고 수정 후 재처리), **temporal query**(특정 시점까지만 재생해 그 시점 상태 확인). 트레이드오프: 외부 시스템 상호작용·코드 변경·이벤트 되돌리기 로직에서 복잡도가 커지므로 기본 선택지가 아니며, **감사(audit)·디버깅·확장성에서 실질 이득이 있을 때만** 채택해야 한다. → **DMF 는 감사가 본질인 도메인이다.** "언제 무엇이 어떻게 바뀌었는가"가 산출물 그 자체이므로 event sourcing 의 채택 조건을 명백히 충족한다. | | E7 | Type 2 Slowly Changing Dimension (Kimball 기법) | Kimball Group | — | kimballgroup.com Dimensional Modeling Techniques | https://www.kimballgroup.com/data-warehouse-business-intelligence-resources/kimball-techniques/dimensional-modeling-techniques/type-2/ | ✅ | 변경 시 기존 행을 갱신하지 않고 **새 행을 추가**한다. 필수 요건: ① 자연키가 아닌 **대리키(surrogate key)** 를 PK 로, ② 3개 컬럼 — **Effective Date**(변경 발효), **Expiration Date**(행 만료), **Current Row Indicator**(활성 플래그). 완전한 이력 감사 추적을 유지하면서 참조 무결성 유지. → **우리 `dmf_record` 테이블의 정확한 스키마다.** 자연키 = DMF 등록번호, 대리키 = 자동 증가 id, `valid_from` / `valid_to` / `is_current`. | | E8 | Event Sourcing Pattern (Azure Architecture Center) | Microsoft | 현재 | Microsoft Learn | https://learn.microsoft.com/en-us/azure/architecture/patterns/event-sourcing | 🔶 | 검색 결과로만 확인. 구현 가이드로 참조. | | E9 | Change Data Capture: From Batch to Real-Time | Capital One | 현재 | capitalone.com/tech | https://www.capitalone.com/tech/software-engineering/batch-to-real-time-with-change-data-capture/ | 🔶 | **CDC vs Event Sourcing 구분**: CDC 는 CRUD 저장소에 사후적으로 이벤트 스트림을 덧씌운 것으로 **도메인 의도 없는 행 델타**이고, Event Sourcing 은 애플리케이션 수준의 **도메인 이벤트**를 포착한다. → 우리는 "신규 등록 / 성분 변경 / 취하"라는 **도메인 이벤트**를 쓴다. "row updated" 같은 CDC 수준 이벤트는 리포트에 쓸모가 없다. | | E10 | Event Sourcing (arc42 Quality Model) | arc42 | 현재 | quality.arc42.org | https://quality.arc42.org/approaches/event-sourcing | 🔶 | 스냅샷은 "일정 개수의 이벤트가 쌓이면 만드는 최적화"라는 실무 규칙 제시. → 우리는 이벤트 수 기준이 아니라 **매 실행마다 스냅샷을 남긴다**(하루 1회이므로 비용이 무의미하게 작다). | ### 2.6 표준 · 도구 문서 | # | 제목 | 저자/발행 | 연도 | 게재처 | URL | 확인 | 이 프로젝트에 주는 시사점 | |---|---|---|---|---|---|---|---| | F1 | RFC 9309: Robots Exclusion Protocol | M. Koster, G. Illyes, H. Zeller, L. Sassman (Google LLC) | 2022-09 | IETF, Internet Standard | https://www.rfc-editor.org/rfc/rfc9309.html | ✅ | §8 에 전문 인용. 1994년 Martijn Koster 가 정의한 방식을 표준화·확장. | | F2 | Google robots.txt 사양 문서 | Google | 현재 | developers.google.com | https://developers.google.com/search/docs/crawling-indexing/robots/robots_txt | ✅ | "Google 은 다음 필드를 지원한다(**crawl-delay 같은 다른 필드는 지원하지 않는다**)", "allow / disallow / user-agent 외의 규칙은 파서가 무시한다", "가장 구체적인 user agent 그룹을 선택한다", "일반적으로 최대 24시간 캐시, 갱신 불가 상황에선 더 길게", "**500 KiB 파일 크기 제한, 초과분 무시**". | | F3 | urllib.robotparser (Python 표준 라이브러리) | Python Software Foundation | 현재 | docs.python.org | https://docs.python.org/3/library/urllib.robotparser.html | ✅ | `RobotFileParser` 메서드: `set_url(url)`, `read()`, `parse(lines)`, `can_fetch(useragent, url)`, `crawl_delay(useragent)` (**3.6 추가**), `request_rate(useragent)` → `RequestRate(requests, seconds)` (**3.6 추가**), `site_maps()` (**3.8 추가**), `mtime()`, `modified()`. → **표준 라이브러리만으로 RFC 준수 파싱이 가능하다.** 외부 의존성 불필요. 단, 5xx→완전 금지 규칙은 직접 구현해야 한다(§8.3 코드). | | F4 | Scrapy AutoThrottle 문서 | Scrapy | 현재 | docs.scrapy.org | https://docs.scrapy.org/en/latest/topics/autothrottle.html | ✅ | 설정과 기본값: `AUTOTHROTTLE_ENABLED`(기본 `False`), `AUTOTHROTTLE_START_DELAY`(기본 `5.0`초), `AUTOTHROTTLE_MAX_DELAY`(기본 `60.0`초), `AUTOTHROTTLE_TARGET_CONCURRENCY`(기본 `1.0`, "값이 낮을수록 크롤러가 보수적이고 정중해진다"), `DOWNLOAD_DELAY`(최소 하한, AutoThrottle 이 이 아래로 내리지 않음). **알고리즘**: ① 시작 지연으로 출발 → ② 응답 수신 시 목표 지연 = `latency / N`(N = target concurrency) → ③ 다음 요청 지연 = (이전 지연 + 목표 지연) / 2 → ④ **비 200 응답은 지연을 늘리기만 하고 줄이지 않는다** → ⑤ 최종 지연은 `DOWNLOAD_DELAY` 와 `AUTOTHROTTLE_MAX_DELAY` 사이로 클램프. → Scrapy 를 안 쓰더라도 **이 4단계 알고리즘을 그대로 구현**한다(§10 표). ⚠️ 문서 내에 `ROBOTSTXT_OBEY` 언급은 없음. | | F5 | Heritrix User Manual | Kristinn Sigurðsson, Michael Stack 외 | — | crawler.archive.org | http://crawler.archive.org/articles/user_manual.pdf | 🔶 | 검색 결과로만 확인. A5 의 readthedocs 문서로 대체 확인함. | | F6 | Respecting Robots Exclusion Protocol or robots.txt at Scale | Rashad Moarref (GumGum Tech) | — | Medium | https://medium.com/gumgum-tech/respecting-robots-exclusion-protocol-or-robots-txt-at-scale-60ee57dc1295 | 🔶 | 대규모 robots.txt 준수 실무기. 검색 결과로만 확인. | | F7 | RFC 9309 — status, mechanics & checks | AgentGrade | 현재 | agentgrade.com | https://agentgrade.com/standards/rfc-9309 | 🔶 | 검색 결과로만 확인. | | F8 | How Attackers Exploit robots.txt? | Baeldung | 현재 | baeldung.com/cs | https://www.baeldung.com/cs/robots-txt-risk-threat | 🔶 | robots.txt 의 보안적 함의. 우리는 robots.txt 를 "숨겨진 경로 목록"으로 쓰지 않는다(윤리·법적 리스크). | ### 2.7 실무 블로그 (셀렉터 파손 / schema drift) | # | 제목 | 발행 | URL | 확인 | 핵심 내용 및 시사점 | |---|---|---|---|---|---| | G1 | How to Fix Web Scraping Errors: 2026 Complete Troubleshooting Guide | PromptCloud | https://www.promptcloud.com/blog/how-to-fix-web-scraping-errors-2026/ | 🔶 | 검색 요약으로 확인: **schema drift** = 페이지 구조가 조금 바뀌어 CSS/XPath 셀렉터가 깨지는 현상. 프로덕션 스크레이퍼는 DOM 스냅샷 시점 기준으로 셀렉터를 작성하므로, 사이트가 레이아웃을 개편하거나 class 명을 바꾸거나 테이블을 재구성하면 **셀렉터가 조용히 아무것도 반환하지 않거나 잘못된 데이터를 반환한다**. | | G2 | Managing Change in Web Scraping: 10 Critical Challenges | PromptCloud | https://www.promptcloud.com/blog/managing-change-in-web-scraping-10-challenges/ | 🔶 | 대응책: **야간 검증 실행**으로 출력 필드 개수를 과거 기준선과 비교, **셀렉터 버저닝**(스크레이퍼에 스키마 날짜 태깅 후 이상 자동 플래그). 가장 위험한 실패는 "셀렉터가 여전히 **무언가**에 매치되지만 그게 올바른 노드가 아닌" **조용한 정확성 드리프트(silent correctness drift)**. → 우리 검증기는 "0건"뿐 아니라 **"건수는 맞는데 값이 이상한"** 경우까지 잡아야 한다(§5.7). | | G3 | Web Scraping With XPath and CSS Selectors | Crawlbase | https://crawlbase.com/blog/web-scraping-with-xpath-and-css-selectors/ | 🔶 | 시각적 class 보다 안정 속성 우선: `data-testid`, `id`, `itemprop`, ARIA role 이 리스타일을 살아남을 확률이 훨씬 높다. 길고 깊은 셀렉터 체인은 레이아웃 전체를 인코딩하므로 경로 중간에 래퍼 하나만 추가돼도 깨진다. | | G4 | When the Scraper Breaks Itself: Building a Self-Healing CSS Selector Repair System | Vinicius Puerto (DEV) | https://dev.to/viniciuspuerto/when-the-scraper-breaks-itself-building-a-self-healing-css-selector-repair-system-312d | 🔶 | 자가 치유 셀렉터 시스템 실무 사례. 우리 LLM 복구 루프의 참고 패턴. | | G5 | What are CSS selectors and XPath in web extraction? / What is an xpath selector in web scraping? | Firecrawl Glossary | https://www.firecrawl.dev/glossary/web-extraction-apis/what-are-css-selectors-xpath-web-extraction · https://www.firecrawl.dev/glossary/web-scraping-apis/what-is-xpath-selector-in-web-scraping | 🔶 | 용어 정리. | --- ## 3. 크롤러 아키텍처 고전 — 프론티어·politeness·중복 제거·재방문 ### 3.1 Mercator 의 부품 목록 (Heydon & Najork 1999; Najork & Heydon 2001) SRC Research Report 173 의 Figure 1 이 명시하는 Mercator 구성 요소는 다음과 같다(원문 표기 그대로): ``` Protocol Modules: HTTP, FTP, Gopher Processing Modules: Link Extractor, GIF Stats, Tag Counter 공통 부품: DNS Resolver, RIS, Content Seen?, URL Filter, DUE, URL Frontier 저장소: Queue Files, URL Set, Log, Doc FPs ``` 원문이 정의하는 크롤러 기본 알고리즘: > "Remove a URL from the URL list, determine the IP address of its host name, download the corresponding document, and extract any links contained in it. For each of the extracted links, ensure that it is an absolute URL (derelativizing it if necessary), and add it to the list of URLs to download, provided it has not been encountered before. If desired, process the downloaded document in other ways (e.g., index its content)." **우리 프로젝트로의 매핑**: | Mercator 부품 | 원래 역할 | DMF_Crawler 에서의 축소 구현 | |---|---|---| | URL Frontier | 다운로드 대기 URL 우선순위 큐 | `frontier.py` — 시드 = DMF 공고 목록 URL(페이지 파라미터 포함). 우선순위 = 목록 페이지 > 상세 페이지. 호스트가 1개이므로 back-end 큐는 1개. | | DNS Resolver | 호스트명 → IP, 캐시 필수 | OS/`requests` 기본 해석기 + 세션 keep-alive. 별도 구현 불필요하나 **DNS 실패는 별도 에러 코드로 분류**(네트워크 장애 vs 사이트 개편 구분). | | Protocol Module (HTTP) | 프로토콜별 fetch | `fetcher.py` — `requests.Session` 1개, 재시도·백오프·조건부 요청 담당. | | RIS (RewindInputStream) | 임의 입력 스트림을 **여러 번 다시 읽을 수 있게** 하는 I/O 추상 | 원본 HTML 바이트를 `raw/YYYY-MM-DD/.html.gz` 로 **먼저 저장한 뒤** 파싱한다. 파싱 실패 시 재파싱·소급 재처리가 가능해진다. Mercator 가 RIS 를 둔 이유와 동일. | | Content-Seen Test | 다른 URL이지만 같은 내용인 문서를 걸러냄 (Doc FPs) | `dedup.py` — 레코드 지문(§7). Mercator 는 문서 단위였지만 우리는 **레코드 단위**로 내린다. | | URL Filter | 스코프 밖 URL 제거 | 도메인 화이트리스트 + 경로 정규식. 외부 링크는 절대 따라가지 않는다. | | DUE (URL-seen test) | 이미 큐에 넣은 URL 재삽입 방지. Rabin fingerprint **8바이트 체크섬** 해시테이블. 체크섬 상위 3바이트를 스파인 인덱스로, 하위 5바이트만 저장 → **10억 URL ≈ 5GB** | `set[str]` 로 충분(URL 수 수백). Rabin 지문은 불필요하나, **정규화된 URL 문자열**(쿼리 파라미터 정렬, 세션 ID 제거)을 키로 쓰는 원칙은 유지. | | Checkpointing | "장기 실행 프로세스의 필수 요소". 실패 시 체크포인트를 읽어 **정확히 그 시점 상태로 복구**할 수 있을 만큼의 상태를 안정 저장소에 기록 | `state.json` + SQLite WAL. 목록 페이지 n장 중 k장까지 처리했다는 커서를 기록해, 중단 후 재실행 시 이어서 진행. 재부팅 자동 복구 요구사항과 직결. | | 백그라운드 스레드 | 기본 **10초마다** 깨어나 통계 로깅 / 종료 조건 확인 / 체크포인트 시점 판단 | 헬스체크 루프. 우리는 실행이 짧으므로 10초 대신 **각 페이지 처리 후** 통계 기록 + 체크포인트. | | robots 캐시 | 호스트명 → robots 규칙, 기본 **2^18 엔트리 LRU** | 호스트 1개이므로 단일 엔트리. **RFC 9309 의 24시간 캐시 규칙**만 지킨다. | ### 3.2 Frontier 의 front-end / back-end 2단 구조 = politeness 의 구조적 보장 SRC 173 원문: > "The frontier consists of a front-end (the top part of the figure) that is responsible for prioritizing URLs, and a back-end (the bottom part of the figure) that is responsible for ensuring strong politeness. When a URL u is added to the frontier, a pluggable prioritizer component computes a priority value p between 1 and k based on the URL and its download history (e.g. whether the document has changed since the last download), and inserts u into front-end FIFO queue p. The back-end maintains n FIFO queues, each of which is guaranteed to be non-empty and to contain URLs of one [host]." **핵심 통찰 3가지**: 1. **politeness 를 "sleep 을 잘 넣자"는 규율 문제가 아니라 자료구조 문제로 바꿨다.** back-end 큐가 호스트당 1개이고 큐당 워커가 1개면, 동일 호스트 동시 접속은 **구조적으로 불가능**하다. → 우리 코드도 `HostQueue` 클래스를 만들어, 요청 함수가 이 큐를 통해서만 호출되게 강제한다. `requests.get` 을 아무 데서나 부르지 못하게 한다. 2. **우선순위 계산에 "지난번 다운로드 이후 문서가 변경되었는가"가 명시적으로 들어간다.** 이것이 §4 의 증분 크롤 이론과 아키텍처가 만나는 지점이다. 3. front-end/back-end 분리 덕에 **우선순위 로직과 politeness 로직이 서로 오염되지 않는다.** ### 3.3 Mercator 의 politeness 정의 (원문) > "Despite the need for speed, anyone running a web crawler that overloads web servers soon learns that such behavior is considered unacceptable. At the very least, a web crawler should not attempt to download multiple pages from the same web server simultaneously; better, it should impose a limit on the portion of a web server's resources it consumes." 두 단계가 명시돼 있다: **최소 요건 = 동시 접속 금지**, **더 나은 요건 = 서버 자원 점유율 상한**. 우리는 둘 다 채택한다(동시성 1 + delayFactor 5.0). 성능 참고치(원문): Compaq DS20E 666MHz Alpha 서버 4대, 160 Mbit/sec 회선 포화 상태에서 **17일간 하루 약 5,000만 문서** 다운로드. 우리는 하루 수십~수백 요청이다 — **5~6 자릿수의 여유**가 있으므로 속도 최적화는 전면 금지하고 전부 안전성에 쓴다. ### 3.4 robots.txt 캐싱 (Mercator 원문) > "Courteous web crawlers implement the Robots Exclusion Protocol, which allows web masters to declare parts of their sites off limits to crawlers. The Robots Exclusion Protocol requires a web crawler to fetch a resource named '/robots.txt' containing these declarations from a web site before downloading any real content from it. To avoid downloading this resource on every request, Mercator's HTTP protocol module maintains a fixed-sized cache mapping host names to their robots exclusion rules. By default, the cache is limited to 2^18 entries, and uses an LRU replacement strategy." → **매 요청마다 robots.txt 를 받지 마라.** 실행 시작 시 1회 받아 프로세스 수명 동안 캐시하고, RFC 9309 의 24시간 상한을 지키면 하루 1회 실행에서는 실행당 정확히 1회 fetch 가 된다. ### 3.5 Content-Seen Test (Mercator 원문) > "Once the document has been written to the RIS, the worker thread invokes the content-seen test to determine whether this document with the same content, but a different URL, has been seen before. If so, the document is not processed any further, and the worker thread goes back to step 1." → 우리의 대응물: **같은 DMF 레코드가 목록 페이지와 상세 페이지, 혹은 페이징 경계에서 중복 등장**할 수 있다. 레코드 지문 테이블로 한 번 본 레코드를 두 번 처리하지 않는다. ### 3.6 Heritrix — politeness 를 설정으로 노출한다 `heritrix.readthedocs.io/en/latest/configuring-jobs.html` 에서 확인된 프로퍼티(정확한 이름): | 프로퍼티 | 의미 | 기본값 | DMF_Crawler 채택값 | |---|---|---|---| | `delayFactor` | "직전 URI 를 가져오는 데 걸린 시간의 배수"만큼 대기 | 5.0 | **5.0 (그대로)** | | `minDelayMs` | 요청 간 최소 대기. **delayFactor 계산값보다 우선** | — | **2000** | | `maxDelayMs` | politeness 지연 상한 | 30000 | **30000 (그대로)** | | `maxPerHostBandwidthUsageKbSec` | 호스트당 최대 대역폭 | — | 미사용(요청 수가 적음) | | `robotsPolicyName` | `obey` / `classic` / `robotsTxtOnly` / `ignore`. RFC 9309 경로 와일드카드 지원 | `obey` | **obey (그대로)** | | `metadata.operatorContactUrl` | "문제 발생 시 크롤 대상 호스트 관리자가 참조할 URI 제공" | — | **User-Agent 에 연락처 포함** | | `maxToeThreads` | 워커 스레드 수(도메인 크롤 시 호스트 수의 약 2배 권장) | — | **1** | | `extract404s` | 404 응답에서 링크 추출 여부 | — | **false** | Heritrix3 README 에서 확인된 개념: robots.txt 및 META nofollow 준수, 크롤 잡 단위 설정, **WARC 출력**, Frontier 큐잉. Java, Apache License 2.0. → WARC 는 우리에게 **"원본 보존"** 원칙으로 번역된다. 파싱 결과만 저장하고 원본을 버리면, 셀렉터가 깨진 날의 데이터를 되살릴 방법이 없다. ### 3.7 Olston & Najork 서베이가 정의하는 문제 공간 > "Though the idea of web crawling may seem straightforward — a simple application of breadth-first-search — the field actually presents numerous challenges, ranging from systems concerns such as managing very large data structures to theoretical questions such as how often to revisit evolving content sources." > — Web Crawling, Foundations and Trends in IR 4(3):175–246, 2010, DOI 10.1561/1500000017 이 프로젝트에서 두 축의 무게는 극단적으로 비대칭이다: | 축 | 웹 스케일 크롤러 | DMF_Crawler | |---|---|---| | 초대형 자료구조 관리 | 지배적 난제 (URL frontier 수십억, 지문 테이블 수 TB) | **사실상 0** — SQLite 한 파일 | | 분산·병렬화 | 필수 | **불필요** — 단일 프로세스 | | politeness | 필수 | **필수 (동일)** | | 재방문 주기 / 변경 감지 | 이론적 난제 | **본질적 난제 — 이 프로젝트의 전부** | | 구조적 추출 정확도 | 부차적(검색 인덱싱은 잡음에 관대) | **치명적** — 규제 데이터는 1건 누락도 실패 | → 그러므로 이 프로젝트의 엔지니어링 예산은 **재방문 정책(§4) + 추출 정확도/검증(§5) + diff 정확도(§7)** 에 몰아야 한다. 성능·확장성 작업은 전부 금지 항목이다. ### 3.8 실측 crawl-delay 값 레퍼런스 (Wikipedia "Web crawler" 정리) | 크롤러/연구 | 접근 간격 정책 | |---|---| | MercatorWeb | 적응형 — **직전 다운로드 소요 시간의 10배** 대기 | | Cho | **10초** 고정 | | WIRE | 기본 **15초** | | 관측된 실제 접근 간격 | **20초 ~ 3–4분** | | Heritrix | `delayFactor` 5.0 × 직전 소요 시간, `minDelayMs`/`maxDelayMs`(기본 30000) 로 클램프 | → 우리의 `min_delay = 2.0s` 는 이 스펙트럼에서 **가장 공격적인 쪽**이다. 정당화: 하루 요청 총량이 수십 건에 불과하므로 절대 부하가 무의미하게 작다. 그러나 delayFactor 5.0 이 살아 있으므로, 서버 응답이 3초로 느려지면 자동으로 15초 간격이 된다. **안전은 하한이 아니라 적응 규칙이 보장한다.** --- ## 4. 증분 크롤링과 변경 감지 이론 ### 4.1 주기적(periodic) vs 증분(incremental) 크롤러 Cho & Garcia-Molina (VLDB 2000) 가 대비시킨 두 설계: | | 주기적 크롤러 | 증분 크롤러 | |---|---|---| | 동작 | 컬렉션 전체를 새로 긁고 통째로 교체 | 로컬 컬렉션을 유지하며 변경분만 갱신 | | 신선도 | 크롤 주기 = 최대 지연 | 지속적으로 최신에 가까움 | | 비용 | 매번 전체 | 변경 감지 비용 + 변경분만 | | 이력 | 없음(교체됨) | 자연스럽게 축적 | **DMF_Crawler 의 선택 = 하이브리드**: - **수집은 주기적으로** — 매일 06:00 에 목록 페이지 전체를 다시 긁는다. DMF 게시판은 총 레코드가 수천 건 규모이고 페이지 수가 적어, "전체를 보고 비교"하는 것이 "변경만 골라내는" 것보다 **단순하고 정확하다**. - **저장은 증분으로** — 긁은 결과를 통째로 덮어쓰지 않고, 키 기반 upsert 로 신규/변경/취하 이벤트를 생성한다. **"취하(사라짐)" 탐지는 전체 스냅샷 비교로만 가능하다** — 이것이 목록 전체를 매일 긁어야 하는 결정적 이유다. > ⚠️ VLDB 2000 원문은 ACM 403, Semantic Scholar 429 로 본문 확인 실패. 위 대비는 논문 초록·2차 출처 요약에 기반하며, 페이지 변경률 실측치·half-life 수치는 **확인하지 못했다**(부록 B 항목). ### 4.2 Poisson 변경 모델과 freshness / age Cho & Garcia-Molina (TODS 2003) 의 핵심 명제: > "A Poisson process is a good model to describe the changes of Web pages." > "the time between changes follow an exponential distribution λe^(−λt) if the change frequency of the page is λ" **정의** (Wikipedia "Web crawler" 의 Re-visit policy 절에서 확인된 표준 정의): - **Freshness** `F(p; t)` — 이진값. 로컬 사본이 시각 t 에 최신이면 1, 아니면 0. - **Age** `A(p; t)` — 로컬 사본이 낡은 채로 얼마나 오래 있었는가. 원본이 수정된 시점부터 경과한 시간. **고정 주기 I 로 재방문할 때의 기대값** (Poisson 변경률 λ 가정, 표준 유도): 한 번 동기화한 직후를 t=0 이라 하면, 시각 t 에 사본이 여전히 최신일 확률은 "그 사이 변경이 0회 발생할 확률" 이므로 ``` P[F(t) = 1] = e^(−λt) ``` 주기 I 에 대한 시간 평균 freshness: ``` 1 ⌠I 1 − e^(−λI) F̄(I) = ─ │ e^(−λt) dt = ─────────── I ⌡0 λI ``` 시각 t 에서의 기대 age 는 `E[A(t)] = t − (1 − e^(−λt))/λ` 이므로, 시간 평균 age: ``` 1 ⌠I ⎡ 1 − e^(−λt)⎤ I 1 1 − e^(−λI) Ā(I) = ─ │ ⎢t − ───────────⎥ dt = ─ − ─ + ─────────── I ⌡0 ⎣ λ ⎦ 2 λ λ²I ``` > ⚠️ 위 수식은 표준 Poisson 유도로 재구성한 것이다. Cho & Garcia-Molina 원문 PDF(`oak.cs.ucla.edu/~cho/papers/cho-tods03.pdf`, `dl.acm.org/doi/pdf/10.1145/958942.958945`)는 각각 ECONNREFUSED / 403 으로 **본문 확인 실패**. Poisson 모델과 지수분포 λe^(−λt) 라는 명제 자체는 검색 결과에서 확인됨. 수식 표기는 원문과 다를 수 있다(부록 B 항목). **DMF_Crawler 실측 대입 (I = 1일)**: | 게시판 성격 | λ (건/일) | λI | F̄ | Ā (일) | Ā (시간) | |---|---|---|---|---|---| | 매우 한산 (주 1회 변경) | 0.14 | 0.14 | 0.932 | 0.047 | 1.1h | | 한산 (5일에 1회) | 0.20 | 0.20 | 0.906 | 0.065 | 1.6h | | 보통 (2일에 1회) | 0.50 | 0.50 | 0.787 | 0.148 | 3.6h | | 활발 (매일 1회) | 1.00 | 1.00 | 0.632 | 0.264 | 6.3h | | 매우 활발 (하루 3회) | 3.00 | 3.00 | 0.317 | 0.394 | 9.5h | **해석 — 그리고 이것이 왜 우리 문제에서 오해를 부르는가**: 위 F̄ 는 "임의의 순간에 사본이 최신일 확률"이다. 그러나 **DMF_Crawler 의 실제 SLA 는 "게시된 공고를 며칠 안에 리포트에 싣는가"** 이고, 하루 1회 균등 크롤에서 이 값은 **λ 와 무관하게 항상 최대 24시간, 평균 12시간**이다(공고가 하루 중 균등하게 올라온다고 가정). freshness/age 는 "실시간 캐시 품질" 지표이지 "탐지 지연" 지표가 아니다. → **문서화할 SLA**: `최대 탐지 지연 = 24시간 + 실행 소요 시간`, `평균 탐지 지연 ≈ 12시간`. 이것이 요구사항("매일 06:00 1회")에서 수학적으로 도출되는 값이며, 더 좋게 만들려면 주기를 줄이는 방법밖에 없다. freshness 를 올리려고 스케줄링을 정교하게 만드는 것은 **효과가 없다.** ### 4.3 균등(uniform) vs 비례(proportional) — 반직관적 핵심 결과 Wikipedia "Web crawler" Re-visit policy 절에서 확인된 서술: > "Cho and Garcia-Molina [demonstrated] that the uniform policy outperforms the proportional policy in terms of average freshness" > "The optimal [policy] is closer to the uniform policy than to the proportional policy" > Coffman et al.: "accesses to any particular page should be kept as evenly spaced as possible" **두 정책**: - **Proportional**: 변경률 λᵢ 에 비례해 재방문 빈도를 배분. 직관적으로 옳아 보인다. - **Uniform**: 모든 페이지를 같은 빈도로 재방문. **왜 균등이 이기는가**: 매우 자주 바뀌는 페이지는 아무리 자주 긁어도 금방 낡는다 — 그 페이지에 자원을 쓰면 **한계 효용이 급감**한다. 반면 그 자원을 덜 바뀌는 페이지에 쓰면 그 페이지는 오래 최신 상태를 유지한다. 총 freshness 를 최대화하려면 **낭비되는 곳(초고빈도 변경 페이지)에서 자원을 빼야 한다.** **DMF_Crawler 의 결론 (확정)**: - 대상 게시판이 여러 개여도 **모두 하루 1회 균등**으로 긁는다. - "이 게시판은 자주 바뀌니까 하루 3번 긁자"는 최적화는 **하지 않는다.** - Coffman 의 "가능한 한 균등 간격" 원칙에 따라, **매일 정확히 06:00**(±지터 최소)에 실행한다. 실행 시각이 들쭉날쭉하면 간격 분산이 커져 이론적 이점이 사라진다. ### 4.4 λ 의 온라인 추정 — 구간 검열(interval-censored) 최우추정 우리는 방문 사이에 몇 번 바뀌었는지 볼 수 없다. **"바뀌었다 / 안 바뀌었다"만 관측**한다(interval censoring). 주기 I 로 n 회 방문해 그중 X 회에서 변경이 관측되었다면: ``` P[한 주기 안에 1회 이상 변경] = 1 − e^(−λI) X / n ≈ 1 − e^(−λI) ⇒ λ̂ = − ln(1 − X/n) / I ``` **경계 처리**: `X = n`(매번 변경)이면 λ̂ = ∞ 이므로, Laplace 보정을 적용한다. ``` λ̂ = − ln(1 − (X + 0.5) / (n + 1)) / I ``` `n < 14`(2주 미만 관측)이면 추정을 신뢰하지 않고 사전값 λ₀ = 0.5 건/일 을 쓴다. ```python # src/dmf_crawler/change_rate.py """Poisson 변경률 λ 의 구간 검열 최우추정 (Cho & Garcia-Molina 2003 의 Poisson 모델 기반). 관측 모델: - 주기 I(일) 로 n 회 방문했고, 그중 X 회에서 '이전 방문 대비 변경'이 관측되었다. - 한 주기 내 1회 이상 변경 확률 = 1 - exp(-lambda * I) """ from __future__ import annotations import math from dataclasses import dataclass PRIOR_LAMBDA_PER_DAY = 0.5 # 관측이 부족할 때 쓰는 사전값 MIN_OBSERVATIONS = 14 # 이 미만이면 사전값 사용 ANOMALY_RATIO = 3.0 # 최근 λ 가 장기 λ 의 이 배를 넘으면 이상 급증 @dataclass(frozen=True) class ChangeRateEstimate: lambda_per_day: float n_observations: int n_changes: int is_prior: bool def expected_freshness(self, interval_days: float = 1.0) -> float: """F̄(I) = (1 - e^(-λI)) / (λI)""" x = self.lambda_per_day * interval_days if x <= 1e-12: return 1.0 return (1.0 - math.exp(-x)) / x def expected_age_days(self, interval_days: float = 1.0) -> float: """Ā(I) = I/2 - 1/λ + (1 - e^(-λI)) / (λ²I)""" lam = self.lambda_per_day if lam <= 1e-12: return interval_days / 2.0 x = lam * interval_days return interval_days / 2.0 - 1.0 / lam + (1.0 - math.exp(-x)) / (lam * lam * interval_days) def estimate_lambda(n_observations: int, n_changes: int, interval_days: float = 1.0) -> ChangeRateEstimate: if interval_days <= 0: raise ValueError("interval_days must be positive") if n_observations < 0 or n_changes < 0 or n_changes > n_observations: raise ValueError("invalid observation counts") if n_observations < MIN_OBSERVATIONS: return ChangeRateEstimate(PRIOR_LAMBDA_PER_DAY, n_observations, n_changes, is_prior=True) # Laplace 보정: X = n 일 때 발산을 막는다. p = (n_changes + 0.5) / (n_observations + 1.0) p = min(max(p, 1e-9), 1.0 - 1e-9) lam = -math.log(1.0 - p) / interval_days return ChangeRateEstimate(lam, n_observations, n_changes, is_prior=False) def is_anomalous_burst(recent: ChangeRateEstimate, longterm: ChangeRateEstimate) -> bool: """최근 변경률이 장기 변경률 대비 급등했는지 판정. 주기는 바꾸지 않고 리포트에 플래그만 단다.""" if recent.is_prior or longterm.is_prior: return False if longterm.lambda_per_day <= 1e-9: return recent.lambda_per_day > 0.0 return recent.lambda_per_day / longterm.lambda_per_day >= ANOMALY_RATIO if __name__ == "__main__": for n, x in [(30, 6), (30, 15), (30, 30), (5, 2)]: est = estimate_lambda(n, x) print( f"n={n:3d} X={x:3d} lambda={est.lambda_per_day:6.3f}/day " f"F̄={est.expected_freshness():.3f} Ā={est.expected_age_days()*24:5.2f}h prior={est.is_prior}" ) ``` **이 추정값의 용도 (그리고 용도가 아닌 것)**: - ✅ 리포트의 "이 게시판은 평소 며칠에 한 번 바뀝니다" 컨텍스트 제공. - ✅ **이상 급증 탐지** — λ̂_recent(최근 14일) ≥ 3 × λ̂_longterm(전체) 이면 리포트 상단에 경고 배지. - ✅ **이상 정지 탐지** — 평소 λ 가 0.5 인 게시판에서 30일간 변경 0건이면 "셀렉터가 조용히 깨졌을 가능성"을 의심한다(§5.7 의 검증과 교차 확인). - ❌ **크롤 주기 변경에는 쓰지 않는다** (§4.3 균등 정책 유지). ### 4.5 조건부 요청 — 서버가 알려주는 "변경 없음" λ 추정과 별개로, HTTP 레벨에서 변경을 저비용으로 판별하는 표준 수단이 있다. | 헤더 | 방향 | 의미 | |---|---|---| | `Last-Modified` | 응답 | 리소스 최종 수정 시각 | | `ETag` | 응답 | 리소스 버전 식별자 | | `If-Modified-Since` | 요청 | 이 시각 이후 변경됐을 때만 본문 전송 | | `If-None-Match` | 요청 | 이 ETag 와 다를 때만 본문 전송 | | `304 Not Modified` | 응답 | 변경 없음 — 본문 없음 | **정책 (확정)**: - 저장해 둔 `ETag`/`Last-Modified` 가 있으면 **항상** 조건부 요청을 보낸다. - **304 를 받아도 "변경 없음"으로 즉시 확정하지 않는다.** 이는 §4.6 의 noisy signal 원칙에 따른다. 304 는 "본문 다운로드를 생략해도 좋다"는 신호일 뿐이며, 정부 게시판의 CMS 가 `Last-Modified` 를 정확히 관리한다는 보장이 없다. **목록 페이지 1페이지만은 조건부 요청 없이 무조건 다시 받아 파싱한다.** - 상세 페이지에 대해서는 304 를 신뢰해 다운로드를 생략하되, **주 1회(일요일)는 조건부 요청을 끄고 전체를 다시 받아 검증**한다. ### 4.6 잡음 섞인 변경 신호를 어떻게 다룰 것인가 (Busa-Fekete et al. 2025) arXiv:2502.02430 의 문제의식: > sitemap·CDN 같은 side information 은 유용하지만 **오탐(false positive)이 있고 실제 갱신을 놓치기도 한다**. 기존 연구(Azar et al. 2018)는 변경·요청이 각각 독립 Poisson 과정이라는 이상화된 가정 위에 최적 스케줄을 유도했으나, 실제 신호는 잡음이 있다. **DMF 게시판에서의 noisy change-indicating signal 목록과 신뢰도 등급**: | 신호 | 취득 비용 | 신뢰도 | 정책 | |---|---|---|---| | 목록 페이지의 "총 게시물 수" 표시 | 매우 낮음 | 중 — 증가는 신뢰, **감소·동일은 불신**(수정은 총수를 안 바꾼다) | 증가 시 즉시 상세 크롤 트리거 | | 목록 1페이지 최상단 게시일 | 매우 낮음 | 중 — 상단 고정 공고에 의해 왜곡됨 | 보조 신호로만 | | `Last-Modified` / `ETag` | 낮음(HEAD 또는 조건부 GET) | **낮음** — 정부 CMS 는 동적 생성으로 매번 바뀌거나 아예 없는 경우가 많음 | 상세 페이지에만, 주 1회 전체 재검증 | | RSS/Atom 피드(존재 시) | 낮음 | 중상 | 존재 여부 실측 필요 (부록 B) | | 공공데이터포털/식의약 데이터 포털 OpenAPI | 낮음 | **높음** — 정형 데이터 | 존재 시 **1순위 소스로 전환**, 크롤링은 대조·보완용 (부록 B) | | 목록 페이지 전체 파싱 결과 | 중 | **높음** | **최종 근거. 매일 무조건 수행.** | **확정 규칙**: 위 신호들은 **상세 페이지 크롤을 생략할지 결정하는 데만** 쓴다. "오늘은 신호가 없으니 크롤을 건너뛴다"는 절대 하지 않는다. 신호는 비용을 줄이고, 진실은 항상 목록 페이지 파싱이 정한다. ### 4.7 자원 배분 이론이 준비해 둔 확장 경로 (Azar et al. 2018 / Kolobov et al. 2019) 지금은 필요 없지만, 대상이 수십 개 게시판으로 늘어날 때를 위해 기록해 둔다. **Azar et al., PNAS 2018 의 정식화**: - 페이지 i 의 Poisson 변경률 `Δᵢ`, 사용자 요청률 `μᵢ`, 총 폴링 대역폭 `R`. - 목적: freshness 가중 효용 최대화(요청 시 최신 페이지를 제공). - Algorithm 1(이산시간) / Algorithm 2(연속시간): **utility-to-change-rate 비로 정렬 후 할당 0인 페이지를 식별**해 유일한 최적 무작위 정책을 `O(n log n)` 에 계산. - Algorithm 3: **EDF(earliest-deadline-first)** 로 비무작위화 → 실험에서 수치 최적해의 **99%** 달성. 확률적 할당을 실용적 순환 refresh 스케줄로 변환. **Kolobov et al., NeurIPS 2019**: 변경 관측이 부분적이고 파라미터를 모르는 상황에서도 최적성 보장이 있는 알고리즘. **18.5M URL 을 14주간 매일** 크롤한 실험으로 검증. → **확장 트리거**: 대상 게시판 수가 10개를 넘고, 실행 시간이 politeness 제약 때문에 30분을 넘기 시작하면, 그때 `μᵢ`(사용자가 실제로 어느 탭을 보는가) × `Δᵢ`(추정 λ) 로 정렬해 상위 그룹만 하루 2회로 올린다. **그 전에는 하지 않는다.** --- ## 5. 구조적 데이터 추출 — wrapper induction 부터 셀렉터 안정성까지 ### 5.1 Wrapper induction 의 계보 ``` 1997 Kushmerick, Weld, Doorenbos — Wrapper Induction for Information Extraction (IJCAI-97) · LR wrapper: 문서를 문자 시퀀스로 보고 좌/우 구분자로 필드를 자름 · hlrt 클래스: 효율적 학습 가능 + 조사 대상 인터넷 리소스의 48% 커버 · PAC 분석으로 표본 복잡도 상한, 불완전 라벨링에 완만한 성능 저하 ↓ 1998 Kushmerick — WIEN (AAAI Workshop WS-98-10) ↓ 1999 Kushmerick — RAPTURE (AAAI-99) ★ wrapper verification 문제 정의 ↓ 2001 Crescenzi, Mecca, Merialdo — RoadRunner (VLDB 2001) ★ 완전 자동, 2페이지 비교 ↓ 2003 Lerman, Minton, Knoblock — Wrapper Maintenance (JAIR 18:149-181) ★ verification + reinduction ↓ 2009 Dalvi, Bohannon, Sha — Robust web extraction (SIGMOD 2009) 확률적 tree-edit 2011 Dalvi, Kumar, Soliman — Automatic Wrappers for Large Scale Web Extraction (PVLDB 4(4):219-230) ↓ 2016 Leotta, Stocco, Ricca, Tonella — ROBULA+ (JSEP 28(3):177-204) ★ 강건 XPath 생성 ↓ 2024~ AutoScraper / AXE / Co-Scraper / LLM XPath agents — LLM 이 wrapper 생성자 자리를 차지 ``` **이 계보가 우리에게 주는 한 문장**: 30년간 이 분야의 모든 진보는 **"wrapper 를 누가 만드는가"** 를 바꿨을 뿐, **"실행은 결정론적 wrapper 가 한다"** 는 전제는 한 번도 바뀌지 않았다. LLM 시대의 최신 논문들(§6)도 여전히 wrapper 를 만들어 실행한다. ### 5.2 RoadRunner — 페이지 2장 비교로 템플릿과 데이터 분리 VLDB 2001 원문(PDF 텍스트 추출로 확인)에서: **전제**: "Pages in data-intensive sites are usually automatically generated: data are stored in a back-end DBMS, and HTML pages are produced using scripts – i.e., programs – from the content of the database." **형식화 — union-free regular expression (UFRE)**: > "Given a special symbol `#PCDATA`, and an alphabet of symbols Σ not containing `#PCDATA`, a union-free regular expression (UFRE) over Σ is a string over alphabet Σ ∪ {`#PCDATA`, `·`, `+`, `?`, `(`, `)`} defined as follows. First, the empty string, ϵ and all elements of Σ ∪ {`#PCDATA`} are union-free regular expressions. If a and b are UFRE, then `a·b`, `(a)+`, and `(a)?` are UFRE." `(a)* = ((a)+)?` 는 축약. UFRE ↔ nested type 대응: `#PCDATA` → string 필드, `+` → 리스트(중첩 가능), `?` → nullable 필드. **문제 정식화**: HTML 문자열 s₁…s_k 가 nested type τ 의 인스턴스 i₁…i_k 의 인코딩이라면, **L(σ) ⊇ {s₁…s_k} 인 최소 UFRE σ 를 찾으면 τ = type(σ)** 이고, σ 를 wrapper 로 써서 원본 데이터를 복원할 수 있다. 따라서 문제는 **두 UFRE 의 least upper bound 계산** 으로 환원된다 → 알고리즘 `match(σ₁, σ₂)`. **ACME 매칭 기법 (Align, Collapse under Mismatch, and Extract)**: 1. HTML 을 XHTML 로 정규화(태그가 제대로 닫히고 중첩되도록). "several tools are available to turn an HTML page into an XHTML one." 2. 어휘 분석기로 **토큰 리스트**(각 토큰은 HTML 태그 또는 문자열 값)로 변환. 논문 Figure 3 예시에서 두 HTML 샘플이 각각 20개, 27개 토큰으로 변환된다. 3. page 1 을 **초기 wrapper** 로 삼고 page 2 를 **sample** 로 파싱한다. 4. sample 의 토큰이 wrapper 문법에 맞지 않으면 **mismatch** 발생 → wrapper 를 일반화해 해소. 5. 모든 mismatch 를 해소하면 공통 wrapper 완성. **mismatch 두 종류와 처리**: | 종류 | 발생 조건 | 의미 | 처리 | |---|---|---|---| | **String mismatch** | wrapper 와 sample 의 대응 위치에 **다른 문자열** | 같은 클래스 페이지라면 **DB 필드 값 차이일 수밖에 없다** | 그 위치를 `#PCDATA` 로 일반화 = **필드 발견** | | **Tag mismatch** | 다른 태그끼리, 또는 태그 vs 문자열 | **iterator 또는 optional** | ① 먼저 반복 패턴(iterator) 탐색 → ② 실패하면 optional 로 처리 | **String mismatch 예시(원문)**: 토큰 4에서 `'John Smith'` vs `'Paul Jones'` → wrapper(초기값 = page 1)의 `'John Smith'` 를 `#PCDATA` 로 치환. 몇 단계 뒤 `'Database Primer'` vs `'XML at Work'` 도 동일. **중요**: 토큰 2의 `'Books of:'` 처럼 **두 페이지에서 동일한 상수 문자열은 필드가 되지 않는다** — 생성 스크립트가 HTML 레이아웃의 일부로 넣은 것이다. **Tag mismatch → optional 처리 (원문)**: 토큰 6에서 wrapper 쪽 `