
사이트별로 코드를 작성하는 대신 런타임에 스크래퍼 로직 생성하기
요약
pluckmd는 사이트별 전용 스크래퍼 코드를 작성하는 대신, 런타임에 AdapterSpec을 생성하여 데이터를 추출하는 방식을 제안합니다. 캐시, 휴리스틱, LLM을 단계적으로 활용하여 비용 효율적으로 웹 데이터를 마크다운으로 변환합니다.
핵심 포인트
- 사이트별 개별 핸들러 없이 데이터 기반의 AdapterSpec 활용
- 캐시 -> 휴리스틱 -> LLM 순의 단계적 해결로 비용 최적화
- 추출 로직을 코드가 아닌 일반 객체 데이터로 취급
- URL 패턴 정규화 및 와일드카드를 통한 휴리스틱 추출
pluckmd는 에이전트(agent)가 블로그 포스트를 마크다운(markdown)으로 가져와 위키(wiki)에 인덱싱하고, 학습을 위한 대화형 HTML을 생성할 수 있도록 존재합니다. 이 포스트는 그 첫 번째 단계, 즉 사이트별 코드가 없는 부분에 대해 다룹니다. 왜냐하면 설계(design) 부분이 흥미로운 지점이기 때문입니다.
만약 제가 실제로 일상에서 어떻게 사용하는지에 대한 실용적인 측면이 궁금하시다면, 별도로 작성해 두었습니다: https://dev.to/taisei_ide/how-i-use-pluckmd-to-read-blogs-with-an-ai-agent-1jpe
이 도구는 사이트별 코드 없이도 블로그에서 기사를 다운로드합니다. Medium을 위한 핸들러(handler)도, Substack을 위한 핸들러도 없으며, 도메인에 기반한 그 어떤 것도 없습니다. 작동 방식은 다음과 같습니다.
핵심 아이디어: 추출(extraction)을 코드가 아닌 데이터로 취급하는 것입니다.
AdapterSpec (어댑터 스펙)
현재 어떤 사이트에 있는지에 따라 분기(branching)하는 대신, pluckmd는 AdapterSpec을 해결(resolve)합니다. 이는 어떤 선택자(selector)가 기사 링크를 찾는지, URL 패턴은 어떻게 생겼는지, 그리고 페이지네이션(pagination)이 어떻게 동작하는지를 명시하는 일반 객체(plain object)입니다.
interface AdapterSpec {
listing: ListingExtractionSpec; // 기사 링크를 찾는 방법
article: ArticleExtractionSpec; // 본문을 가져오는 방법
...
데이터이기 때문에, 동일한 형태를 휴리스틱(heuristic), LLM, 에이전트(agent), 또는 사람이 직접 입력하는 방식으로 얻을 수 있습니다. 이들은 모두 동일한 결과물을 생성하며, 모두 동일한 검증 과정을 거칩니다.
해결 방식, 가장 저렴한 경로 우선
캐시(cache) -> 휴리스틱 (local, 무료) -> LLM (필요한 경우에만)
먼저 캐시를 확인하며, 오래된 항목이 몰래 들어오지 못하도록 오늘의 DOM과 대조하여 재검증합니다. 그다음 로컬 휴리스틱(local heuristics)을 사용합니다. LLM은 휴리스틱이 확신하지 못할 때만 호출됩니다. 작동하는 모든 결과는 다시 기록되므로, 특정 사이트에서의 두 번째 실행은 기본적으로 즉각적입니다.
휴리스틱이 기사 목록을 찾는 방법
이 부분은 자신이 어떤 사이트를 보고 있는지 전혀 알지 못합니다. 모든 링크를 가져와 경로를 정규화(normalize)하고, 변하는 부분들을 와일드카드(wildcard)로 축소합니다.
/blog/my-first-post -> /blog/*
/blog/another-article -> /blog/*
/about -> /about
해당 형태(shape)별로 그룹화합니다. 동일한 패턴이 3번 이상 반복되는 모든 그룹은 기사 목록(article list)의 후보가 됩니다. 링크의 개수, 페이지 내에서 차지하는 비율, 경로 깊이(path depth), 그리고 메인 콘텐츠 영역 내에 위치하는지 여부에 따라 점수를 매깁니다. 가장 높은 점수를 받은 것이 선정됩니다.
경로 내의 숫자와 긴 해시(hash) 값은 변수로 취급되므로, 기사 ID나 날짜가 그룹화를 파편화하지 않습니다. 이 모든 과정은 이름이 아닌 구조(structure)를 기반으로 합니다.
검증 게이트 (The validation gate)
런타임 생성(runtime generation)을 신뢰할 수 있게 만드는 규칙은 다음과 같습니다. 실제 DOM에서 스스로를 증명하기 전까지는 아무것도 사용하거나 캐시(cache)하지 않습니다.
- 링크 선택자(link selector)가 최소 3개의 링크와 일치해야 함
- 그중 최소 절반 이상이 URL 패턴과 일치해야 함
- 선택자 기반의 본문 추출(body extraction)인 경우, 본문의 길이가 최소 80자 이상이어야 함
사양(spec)이 실패하면 폐기됩니다. 이것이 잘못된 LLM의 추측이 출력값이나 캐시를 조용히 오염시키는 것을 방지하는 방법입니다. 모든 소스에 동일한 게이트가 적용됩니다.
한 페이지, 세 가지 방식
정적 페치(static fetch), 헤드리스 Playwright 렌더링(headless Playwright render), 그리고 로그인된 Chrome 탭은 매우 다른 특성을 가집니다. pluckmd는 이 세 가지를 하나의 인터페이스 뒤에 배치하며, 스크롤이나 '다음' 클릭과 같은 실시간 동작을 위한 DomEvaluator를 제공합니다. 링크 수집기(link collector)와 추출기(extractor)는 자신이 작업 중인 페이지를 어떤 백엔드가 생성했는지 알지 못합니다. 네 번째 소스를 추가하는 것은 단 하나의 인터페이스를 구현하는 것만을 의미합니다.
에이전트 탈출구 (The agent escape hatch)
휴리스틱(heuristics)이 포기하고 설정된 LLM도 없는 경우, 에러를 발생시키지 않습니다. 대신 페이지 구조와 후보 선택자가 포함된 요청 파일(request file)을 작성하며, 코딩 에이전트(coding agent)가 이를 읽어 사양(spec)을 생성합니다. 사용자는 명령 하나로 이를 검증하고 캐시할 수 있습니다. 따라서 가장 까다로운 사이트라도 API 호출 대신 에이전트를 통해 경로를 찾아 해결할 수 있습니다.
그것이 전부입니다. 소스(source), 리졸버(resolver), 그리고 페이지 백엔드(page backend)를 모두 교체 가능하게 만드는 단일 데이터 계약(data contract)입니다.
Repo (MIT): https://github.com/taisei-ide-0123/pluckmd
만약 범용적 추출(generic extraction) 문제를 다른 방식으로 해결하셨다면 진심으로 듣고 싶습니다. "휴리스틱(heuristic)을 신뢰할 것인가"와 "모델(model)을 호출할 것인가" 사이의 신뢰 임계값(confidence threshold) 설정이 제가 가장 확신하지 못하는 부분입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기