20년 동안 웹 페이지에서 데이터를 추출한다는 것은 셀렉터를 작성하는 일을 의미했다: 요소를 찾고, 텍스트를 가져오고, 사이트가 바뀌면 고치는 것. 대형 언어 모델은 다른 거래를 제안한다. 모델에게 페이지를 주고, 원하는 필드를 설명하면, 작성할 셀렉터도 없고 리디자인 때 깨질 셀렉터도 없이 구조화된 데이터를 돌려받는다.
그 거래는 실제로 존재하지만, 비용과 속도, 실패 모드라는 대가가 따르며, 파이프라인을 이를 중심으로 다시 구축하기 전에 이 세 가지 모두를 측정해볼 가치가 있다. 그래서 우리는 측정했다. 이 글은 실제 운영 중인 페이지에서 진행한 작지만 솔직한 테스트와, 놓친 부분이 드러낸 것, 규모 확장 시 드는 비용, 그리고 추출 파이프라인에서 모델이 있어야 할 위치를 다룬다.
핵심 요약
- 18개의 실제 뉴스 기사에서, 대형 Claude 모델은 헤드라인을 18개 모두 정확히 추출했고, 날짜는 18개 중 17개, 저자 목록은 18개 중 14개를 정확히 추출했으며, 이는 각 페이지 자체의 구조화된 데이터와 비교해 채점한 결과다.
- “미스” 대부분은 모델의 오류가 아니었다. 저자 정보가 불일치한 모든 페이지에서 구조화된 데이터는 일반적인 자리표시자를 담고 있었고, 그중 두 건에서는 모델이 실제로 눈에 보이는 페이지에 표시된 바이라인을 그대로 보고했다.
- 모델은 아무것도 지어내지 않았다: 모델이 반환한 모든 헤드라인과 저자는 페이지 텍스트 안에 나타난다. 그럼에도 모델은 사진작가를 저자로 지칭하는 한 가지 실제 오류를 범했다.
- 정가 기준으로 페이지당 약 1.9센트가 들었고, 중간값 기준 2.4초가 걸렸다. 구조화된 데이터를 파싱하는 데는 비용이 거의 들지 않고 밀리초 단위의 시간이 걸린다.
- 여러 서로 다른 사이트 템플릿이 존재하거나 구조화된 소스가 없는 필드처럼 셀렉터를 유지하는 비용이 큰 경우에 모델을 사용하고, 모델이 반환하는 모든 것을 검증하라.
우리가 테스트한 것
| 설정 | 값 |
|---|---|
| 페이지 | 2026년 9월 28일에 수집한, 주요 뉴스 발행사 한 곳의 섹션 프론트에 실린 실제 기사 18건 |
| 모델 입력 | 내비게이션 및 기타 페이지 구성 요소를 포함한 페이지의 표시 텍스트, 평균 약 8,700자 |
| 필드 | 헤드라인, 페이지에 표시된 게재일, 저자 이름 |
| 모델 | Claude Opus 5, 낮은 노력(effort) 수준, 출력을 제한하는 JSON 스키마 사용 |
| 정답 기준 | 해당 페이지 자체의 JSON-LD 구조화된 데이터 |
| 채점 방식 | 헤드라인과 날짜는 정확히 일치해야 하고, 저자 목록은 집합으로서 일치해야 함 |
정답 기준은 의도적으로 HTML 파싱을 멈춰라에서 다룬 종류의 데이터, 즉 발행사가 검색 엔진을 위해 삽입하는 구조화된 데이터를 사용한다. 대체로 옳다. 하지만 밝혀진 바로는, 항상 그런 것은 아니었다.
결과
| 측정 항목 | 결과 |
|---|---|
| 헤드라인 정확 | 18건 중 18건 |
| 날짜 정확 | 18건 중 17건 |
| 저자 정확 | 18건 중 14건 |
| 페이지 텍스트에서 확인된 추출 값 | 헤드라인 18건 중 18건, 저자 목록 18건 중 18건 |
| 페이지당 토큰 수 | 입력 약 3,380, 출력 66 |
| 지연 시간 | 중간값 2.4초, 범위 1.8초~10.6초 |
| 실행 비용 | 정가 기준 $0.33, 페이지당 약 1.9센트 |
놓친 부분이 흥미로웠다
헤드라인 수치는 모델을 과소평가하고 정답 기준을 과대평가한다. 각 불일치 사례를 살펴보면 다음과 같다:
- 오피니언 칼럼. 구조화된 데이터는 저자를 일반적인 소속 기자 자리표시자로 표기했다. 눈에 보이는 바이라인은 실제 칼럼니스트의 이름을 명시했다. 모델은 칼럼니스트를 반환했다.
- 통신사 기사(wire story). 구조화된 데이터는 다시 일반적인 자리표시자를 표기했고, 눈에 보이는 바이라인은 “Staff and agencies”로 표시되어 있었다. 모델은 페이지에 표시된 내용을 그대로 반환했다.
- 뉴스레터 가입 페이지가 데이터 집합에 섞여 들어와 있었다. 이 페이지에는 저자도 날짜도 표시되어 있지 않았다. 그럼에도 구조화된 데이터는 일반적인 저자와 날짜를 제공했지만, 모델은 그것을 지어내는 대신 두 필드를 모두 비워두었다.
- 포토 에세이. 구조화된 데이터는 동일한 일반 자리표시자를 표기했다. 페이지는 사진의 저작권을 특정 사진작가에게 표시했고, 모델은 그 사진작가를 저자로 반환했다. 이는 실제 오류다.
결국 불일치가 있던 4개 페이지 중 2건은 정답 기준이 눈에 보이는 페이지보다 부정확했음을 보여주었고, 1건은 모델이 추측을 올바르게 거부한 경우였으며, 1건은 실제 오류였다. 이는 물어볼 가치가 있는 질문을 바꾼다. 구조화된 데이터는 강력한 기본값이지만 절대적 진리(ground truth)는 아니며, 눈에 보이는 페이지를 읽는 모델은 구조화된 데이터가 틀린 부분을 잡아낼 수 있는 동시에, 자신이 보는 것을 가끔 잘못 읽을 수도 있다.
규모 확장 시 드는 비용
이 테스트는 Claude Opus 5의 정가, 즉 입력 토큰 100만 개당 $5, 출력 토큰 100만 개당 $25를 기준으로 18개 페이지에 $0.33이 들었다. 이는 페이지당 약 1.9센트로, 페이지 100만 건이면 약 $18,600에 해당한다. Anthropic의 Batch API는 기다릴 수 있는 작업에 대해 이 토큰 가격을 절반으로 낮춰준다. 더 작은 모델은 토큰당 비용이 다시 더 저렴한데, Claude Haiku 4.5는 $1과 $5로 등재되어 있지만 이번 테스트에서는 다루지 않았으며, 여러분의 페이지에서의 정확도는 가정할 것이 아니라 측정해야 할 사항이다.
지연 시간도 중요하다. 페이지당 중간값 2.4초에 간간이 이상치가 있는 정도는 모니터링과 데이터 보강에는 문제가 없지만, 대량 크롤링에서는 실질적인 제약이 된다. JSON-LD를 파싱하거나 셀렉터를 실행하는 데는 밀리초가 걸리고, 가져오기(fetch) 비용 외에는 사실상 비용이 들지 않는다. 이러한 추출 비용은 수집 비용 위에 추가로 얹어지며, 그래서 클린 레코드당 비용은 둘 다 포함해야 한다.
어떤 방법을 언제 사용할 것인가
| 상황 | 최선의 접근법 |
|---|---|
| 페이지가 JSON-LD나 내장된 JSON 형태로 필드를 게시하는 경우 | 구조화된 데이터를 파싱한다 |
| 하나 또는 소수의 사이트 템플릿에서 나오는 대량 처리 | 검증을 포함한 셀렉터 또는 구조화된 데이터 |
| 각기 다른 템플릿을 가진 여러 사이트 | 검증을 거친 모델, 필요하다면 이후 재사용할 셀렉터를 모델이 생성하도록 함 |
| 약관, 자격 조건, 사양처럼 서술형 텍스트에만 존재하는 필드 | 모델 |
| 페이지의 구조화된 데이터가 검증에 실패하는 경우 | 대체 수단으로서의 모델 |
| 물량은 적지만 가치가 높고 레이아웃이 자주 바뀌는 경우 | 모델 |
가장 강력한 파이프라인은 이 둘을 결합한다. 먼저 구조화된 데이터를 파싱하고, 필수 필드가 없거나 검증에 실패하면 모델로 전환하며, 각 레코드를 만들어낸 방법을 기록한다. 이는 구조화된 데이터에 대해 설명한 것과 동일한 대체 체인(fallback chain) 패턴이다. 하나의 사이트 템플릿이 수천 개의 페이지를 포괄하는 경우, 모델이 한 번 셀렉터를 제안하게 하고 그 셀렉터를 모든 페이지에서 저렴하게 실행하면서, 셀렉터가 깨질 때를 대비해 모델을 대기시켜 두는 방식도 효과적일 수 있다.
모델을 안전하게 사용하기
네 가지 관행이 모델 추출을 인상적인 수준에서 믿을 수 있는 수준으로 바꾼다.
출력을 제한하라. JSON 스키마는 모델이 요청한 필드와 타입을 반환하도록 만들지만, 그래도 정지 이유(stop reason)를 확인해야 한다. 거부되거나 잘린 응답에는 파싱할 것이 없기 때문이다. 페이지에 표시되지 않은 필드는 비워두라고 모델에 지시하라. 우리 테스트에서 모델은 정확히 그렇게 했다.
근거를 확인하라(grounding check). 이름, 헤드라인, 제품명처럼 페이지에 나타나야 하는 모든 추출된 문자열은 페이지 텍스트 안에서 발견되어야 한다. 이는 지어낸 값을 잡아내는 저렴한 검사다. 사진작가 사례가 보여주듯, 실제 이름이 잘못된 필드에 붙은 경우는 잡아내지 못하므로, 이는 필터일 뿐 증명은 아니다.
사람이 검토할 표본을 추출하라. 매주 작은 무작위 표본을 사람이 직접 페이지를 읽은 결과와 대조해 채점하고, 필드별, 사이트별 정확도를 시간에 따라 추적하라.
페이지 텍스트를 신뢰할 수 없는 입력으로 취급하라. 페이지는 다른 누군가가 작성한 것이다. 웹 콘텐츠에 모델을 사용할 때는 오직 추출 목적으로만 사용하고, 페이지 콘텐츠가 어떤 동작을 유발하도록 절대 두지 말며, 모델에게 주는 지시사항은 페이지와 분리해서 유지하라.
다음은 우리가 사용한 추출 호출과, 그 뒤를 잇는 근거 확인(grounding check)이다:
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const SCHEMA = {
type: "object",
properties: {
headline: { type: "string" },
date_published: { type: "string", description: "YYYY-MM-DD, as shown on the page" },
authors: { type: "array", items: { type: "string" } },
},
required: ["headline", "date_published", "authors"],
additionalProperties: false,
};
export async function extractArticle(pageText) {
const response = await client.beta.messages.create({
model: "claude-opus-5",
max_tokens: 2000,
betas: ["server-side-fallback-2026-07-01"],
fallbacks: "default",
output_config: { effort: "low", format: { type: "json_schema", schema: SCHEMA } },
messages: [{
role: "user",
content:
"Below is the visible text of a news article web page, including navigation and other page furniture. " +
"Extract the article's headline exactly as written, its publication date as shown on the page (YYYY-MM-DD), " +
"and the author names. Leave a field empty if the page does not show it.\n\n<page>\n" + pageText + "\n</page>",
}],
});
if (response.stop_reason === "refusal") return null;
const text = response.content.filter((b) => b.type === "text").map((b) => b.text).join("");
return { record: JSON.parse(text), usage: response.usage };
}
// Grounding check: every extracted string must actually appear in the page text.
export function grounded(record, pageText) {
const norm = (s) => s.replace(/[‘’]/g, "'").replace(/[“”]/g, '"').replace(/\s+/g, " ").toLowerCase();
const page = norm(pageText);
return {
headline: page.includes(norm(record.headline)),
authors: record.authors.every((a) => page.includes(norm(a))),
};
}
입력은 모델만큼 중요하다. 모델은 페이지에 실제로 담긴 내용만 추출할 수 있으므로, 차단 페이지, 동의 화면(consent wall), 빈 껍데기 페이지는 확신에 찬 채로 잘못된 콘텐츠를 추출해낸다. 침묵하는 실패율에서 다룬 대로, 추출하기 전에 가져온 것을 검증하고, 필요한 페이지 버전을 가진 마켓에서 가져오라.
이 테스트의 한계
한 발행사에서 가져온 18개 페이지는 작은 표본이며, 결정적이기보다는 솔직하기 위해 선택한 규모다. 뉴스 기사는 또한 쉬운 사례다: 헤드라인이 분명하고, 바이라인이 눈에 보이고, 날짜에 라벨이 잘 붙어 있다. 변형, 가격, 재고 여부가 있는 제품 페이지나 다른 언어로 된 페이지는 다르게 동작할 것이다. 우리는 하나의 모델을 하나의 노력(effort) 수준에서만 테스트했다. 이 방법은 여러분 자신의 페이지에서 반복하기 쉽고, 결국 여러분 자신의 페이지만이 중요한 벤치마크다.
결론
페이지를 읽는 모델은 모든 헤드라인을 정확히 맞혔고, 아무것도 지어내지 않았으며, 여러 경우에는 페이지 자체의 구조화된 데이터보다 더 충실하게 페이지를 보고했다. 동시에 저자를 한 번 잘못 지칭했고, 1센트의 일부가 아니라 몇 센트 단위의 비용이 들었으며, 밀리초가 아니라 몇 초가 걸렸다.
이는 셀렉터가 잘 다루지 못하는 추출 영역, 즉 여러 템플릿, 서술형 텍스트로만 존재하는 필드, 구조화된 데이터가 실패했을 때의 대체 수단에서 강력한 도구가 되게 한다. 구조화된 데이터가 존재하는 곳에서 이를 대체하지는 못한다. 페이지가 게시하는 것은 파싱하고, 그렇지 않은 곳에서는 모델을 활용하며, 둘 다 검증하고, 어느 쪽을 사용했는지 기록하라.
출처 및 참고 자료
- Anthropic, 가격 정책. 테스트 시점의 모델 및 Batch API 가격.
- Schema.org, NewsArticle.
- 위 코드를 사용해 Shifter가 2026년 9월 28일에 공개적으로 접근 가능한 뉴스 기사 18건을 대상으로 실행한 테스트.