website 컨텐츠 추출 규격 — URL 하나가 어느 칸으로 가나

website 로 분류된 URL 을 ContentInput 으로 옮기는 규칙. 도메인마다 따로 쓰지 않고 패턴 7개로 묶었다.

근거 — 도메인 180개 308 URL 실측 · 124 도메인 배정 · 렌더 2026-08-11 · 분류는 website-kinds.html, 이 문서는 그 다음 단계다

1패턴 7개 — 무엇을 채우고 무엇을 못 채우나

도메인 124개를 일곱 패턴으로 묶었다. 도메인마다 규칙을 쓰면 124개가 되고 사이트가 리뉴얼할 때마다 하나씩 조용히 틀어진다. 패턴으로 묶으면 규칙이 일곱 개이고, 새 도메인이 와도 배정만 하면 된다.

패턴 × 채우는 칸 · 규모

링크 수는 그 패턴에 속한 도메인들이 받은 토큰 링크의 합이다. 절반은 그 패턴 안에서도 도메인마다 갈린다는 뜻이다.

패턴도메인링크비중text.primarytext.bodypublishedAtmediaUrls[0]tags[]
P1 article
제목과 요약이 이 페이지 것이고 발행시각도 있다
4161627.8%채운다채운다채운다채운다절반
P2 entry
제목과 요약이 이 페이지 것인데 발행시각이 없다
3143219.5%채운다채운다채운다
P3 title-only
제목만 쓸 만하고 요약은 없거나 일반 문구다
131717.7%채운다절반
P4 site-wide
값이 있으나 그 페이지가 아니라 사이트 전체를 설명한다
2887139.3%
P5 gated
응답이 로그인 요구 · 페이월 · 봇 차단 안내문이다
4663.0%
P6 spa
클라이언트 렌더라 메타가 구조상 비었다
2120.5%
P7 unfilled
템플릿을 깔고 내용을 안 채웠다
5482.2%

쓸 수 있는 구간은 1,219건, 전체의 55.0% 다. 나머지 997건은 값이 있어도 그 페이지를 말하지 않는다.

가장 큰 손실은 P4 site-wide 로 링크 871건(39.3%)이다. 도메인은 28개뿐인데 링크가 몰려 있다 — facebook.com 186 · coincommunities.org 144 · truthsocial.com 79 · google.com 66 처럼 대형 도메인이 여기 모여 있어서다.

2추출기 — 코드 하나가 어떻게 도나

추출기는 하나다. 도메인을 보고 분기하지 않는다. 받은 값을 보고 패턴을 판정하고, 필드마다 정해진 순서로 표준을 훑는다.

HTML 한 장에서 ContentInput 까지

왼쪽은 값을 모으는 단계이고 오른쪽은 그 값이 쓸 만한지 판정하는 단계다. 판정이 뒤에 오는 것이 중요하다 — 값을 다 모아야 “있는데 사이트 공통 문구” 인지 알 수 있다.

HTML 한 장 GET 1회 · 이미 받고 있다 ① 표준 8종을 전부 읽는다 og · jsonld · article · twitter meta · canonical · rss · oembed 먼저 다 모은다. 하나씩 보고 중간에 끊으면 “있는데 공통 문구” 인지 알 수 없다 ② 캐스케이드로 패턴을 판정한다 R1 로그인벽 → R2 SPA → R3 미기입 → R4 사이트 공통 → R5 제목·요약 못 쓰는 경우를 먼저 걸러낸다. 뒤집으면 로그인 안내문이 “제목과 요약이 있다” 로 읽힌다 ③ 패턴이 P1·P2·P3 인가 아니면 Content 를 만들지 않고 사유만 남긴다 ④ 필드별 체인을 훑는다 칸마다 순서가 다르다 ⑤ text_body 나 published 가 비었나 아니오 → 바로 아래로. P1·P2 는 여기서 끝나 호출이 0회다 예 → 피드를 1회 받아 이 URL 과 대조하고 빈 칸만 메운다 RSS 보정 — 도메인당 1회 dailymail 은 여기서 P3 → P1 이 된다 ContentInput

필드마다 우선 표준이 다르다

이것이 이번 배정에서 나온 가장 실무적인 결과다. 하나의 폴백 체인을 쓰면 안 된다. text 는 OpenGraph 가 압도하는데 published 는 JSON-LD 가 이긴다.

필드별 폴백 체인

순서는 짐작이 아니라 P1 배정 41개 도메인에서 실제로 그 값을 준 표준의 빈도로 정했다. 오른쪽이 그 빈도다.

출력 칸시도 순서P1 실측 빈도
text.primaryog:title → jsonld:headline → meta:title → twitter:titleog:title 35 · jsonld:headline 5
text.bodyog:description → jsonld:articleBody → meta:description → twitter:descriptionog:description 33 · jsonld:articleBody 4
publishedAtjsonld:datePublished → article:published_timejsonld:datePublished 32 · article:published_time 9
mediaUrls[0]og:image → jsonld:imageog:image 39
tags[]jsonld:keywords → article:tagjsonld:keywords 13

🔴 패턴은 도메인에 고정하지 않는다. 같은 도메인인데 URL 경로에 따라 패턴이 갈리는 경우가 19건 나왔다. fomo.family 는 프로필 페이지가 site-wide 인데 블로그 글은 article 이고, whitehouse.gov 는 기사가 article 인데 PDF 링크는 메타가 아예 없다.

그래서 §4 의 도메인별 배정은 기대값이자 검수용이고, 런타임 판정은 항상 그 URL 에서 실제로 받은 값으로 한다. 도메인 표를 믿고 분기하면 이 19건이 조용히 틀린다.

OG 가 못 채우면 RSS 로 한 번 더 본다

여기까지가 원래 규격이었다. 그런데 dailymail.com 기사 하나를 실제로 열어보고 규격이 바뀌었다. 그 페이지는 OpenGraph 가 통째로 비어 있는데 피드에는 제목과 요약과 발행시각이 다 있었다.

기사 페이지 899KB              RSS 항목
  og:title  없음                 title  Andy Burnham says high streets…
  og:desc   빈 문자열            desc   The Prime Minister has pledged…
  pub_time  없음                 date   Mon, 10 Aug 2026 22:09:40 GMT

dailymail.com 은 배정에서 P3 title-only 였다. OpenGraph 가 비어 제목만 겨우 건졌기 때문이다. 피드를 보면 P1 으로 올라간다.

피드를 실제로 열어본 결과

태그가 있다고 피드인 것이 아니다. 단일 페이지 앱은 어떤 경로를 넣어도 200 과 함께 앱 껍데기를 돌려주므로, 열어서 파싱까지 해봐야 안다. 이득 표시는 OpenGraph 가 약한 패턴이라 피드가 실제로 보탬이 되는 도메인이다.

도메인패턴항목시간창(일)요약 있는 항목
trib.alP1150283150/150
yegge.aiP22027820/20
forbes.comP2119410/11
longdog.lolP71621/1
animamacau.org 이득P3105410/10
dailymail.com 이득P315041150/150
littlepoe.lolP71141/1
pumpga.funP71121/1
sciencedaily.comP260660/60
space.comP150550/50
digitalcameraworld.comP150549/50
tumblr.com 이득P420418/20

태그가 있던 도메인 34개 중 실제 피드는 24개이고, 요약까지 있는 것은 22개다. 나머지는 parse-fail 2 · html-shell 2 · empty-feed 2 · HTTP 404 2 였다.

시간창이 값어치를 정한다. 중앙값은 5일인데 사이트마다 자릿수가 다르다 — trib.al 283일 · dailymail 41일 · knowyourmeme 3일 · foxnews 0일. 피드는 최근 목록이라, 링크를 받은 직후에 부르면 들어 있고 몇 주 뒤에 부르면 없다.

그래서 배치 재수집에서는 거의 쓸모가 없고 실시간 수집에서는 유효하다. 실제 파이프라인은 token.flow 이벤트를 실시간으로 받으므로 토큰이 방금 건 기사는 대개 피드 안에 있다.

그래서 규칙

전량이 아니라 결측 보정이다. 조건을 붙이는 이유는 호출 때문이다.

① OG 폴백 체인을 먼저 돌린다
② text_body 또는 published 가 비었나?
     아니오 → 끝. RSS 를 부르지 않는다        (P1 · P2 는 여기서 끝난다)
     예   → ③
③ 그 도메인에 rel=alternate rss 링크가 있나?
     아니오 → 끝
     예   → ④
④ 피드를 받아 항목 link 를 이 URL 과 대조한다
     맞는 항목 있음 → title · description · pubDate 로 빈 칸을 메운다
     없음         → 끝. 비운 채로 둔다

“추가 호출 0회” 원칙이 여기서 깨진다. 그래서 조건을 좁혔다.

⚠️ 적중률은 아직 못 쟀다. 지금 데이터는 링크를 받은 지 6주가 지난 것이라 피드에서 이미 밀려났고, 실시간 조건의 적중률은 파이프라인에 붙여야 나온다.

3패턴 카드 — 판정 조건과 매핑

패턴마다 어떻게 판정하고, 무엇이 어디로 가고, 실측 값이 어떻게 생겼는지다.

P1article제목과 요약이 이 페이지 것이고 발행시각도 있다41도메인 · 616건 · 규칙 R5a

무엇이 어느 칸으로

출력 칸이 패턴에서의 출처채워진 도메인
text.primaryog:titlejsonld:headlinemeta:title41/41
text.bodyog:descriptionjsonld:articleBodymeta:description41/41
publishedAtjsonld:datePublishedarticle:published_time41/41
mediaUrls[0]og:imagejsonld:image40/41
tags[]jsonld:keywordsarticle:tag21/41

실측 — 대표 도메인

knowyourmeme.com · 토큰 117건
og:titleHalf Cat / Panorama Fail Cat | Know Your Meme
og:descriptionHalf Cat refers to a digitally altered image of a cat with two legs walking down a street. Since it’s earliest appearanc
jsonld:headlineHalf Cat / Panorama Fail Cat
jsonld:datePublished2018-04-26T16:22:08-04:00
article:published_time2018-04-26T16:22:08-04:00
meta:titleHalf Cat / Panorama Fail Cat | Know Your Meme

밈마다 제목·요약·이미지·키워드가 다르고 jsonld datePublished 에 실제 등록일이 있다.

Content 를 만든다
P2entry제목과 요약이 이 페이지 것인데 발행시각이 없다31도메인 · 432건 · 규칙 R5b

무엇이 어느 칸으로

출력 칸이 패턴에서의 출처채워진 도메인
text.primaryog:titlemeta:title31/31
text.bodyog:descriptionmeta:descriptionjsonld:headline31/31
publishedAtjsonld:datePublished1/31
mediaUrls[0]og:image25/31
tags[]meta:keywords6/31

실측 — 대표 도메인

apple.com · 토큰 47건
og:titlepump.fun - speculate on trends App - App Store
og:descriptionDownload pump.fun - speculate on trends by Maius Imperium Limited on the App Store. See screenshots, ratings and reviews
jsonld:headlineApp Store
jsonld:datePublished
article:published_time
meta:title‎pump.fun - speculate on trends App - App Store

앱마다 제목·설명은 다르지만 발행일은 전혀 없고 이미지는 'Placeholder.mill'이라는 미렌더링 자리표시자다.

Content 를 만든다
P3title-only제목만 쓸 만하고 요약은 없거나 일반 문구다13도메인 · 171건 · 규칙 R6

무엇이 어느 칸으로

출력 칸이 패턴에서의 출처채워진 도메인
text.primaryog:titlemeta:titlejsonld:headline13/13
text.bodyog:descriptionmeta:description4/13
publishedAtjsonld:datePublishedarticle:published_time4/13
mediaUrls[0]og:imagejsonld:image8/13
tags[]meta:keywords1/13

실측 — 대표 도메인

dailymail.com · 토큰 52건
og:title
og:description
jsonld:headlineScientists develop a 'ghost font' that only humans can read - and it leaves even the most advanced AI bots baffled
jsonld:datePublished2026-07-25T08:40:34+0100
article:published_time
meta:titleScientists develop a 'ghost font' that only humans can read - and it leaves even the most advanced AI bots baffled | Dai

og는 완전히 비어 있고 jsonld headline만 기사별로 다르며 본문은 'READ MORE:' 같은 무의미한 값이라 요약이 없다.

Content 를 만든다 — text.body 는 비운다
P4site-wide값이 있으나 그 페이지가 아니라 사이트 전체를 설명한다28도메인 · 871건 · 규칙 R4 · R7

무엇이 어느 칸으로

출력 칸이 패턴에서의 출처채워진 도메인
text.primaryog:titlemeta:title27/28
text.bodyog:descriptionmeta:description25/28
publishedAtjsonld:datePublished3/28
mediaUrls[0]og:image22/28
tags[]meta:keywords2/28

실측 — 대표 도메인

coincommunities.org · 토큰 144건
og:titlePump.fun — Launch and trade memecoins on Solana
og:descriptionPump.fun is the fairest way to launch and trade memecoins on Solana. Coins are instantly tradable on a transparent bondi
jsonld:headlinePump
jsonld:datePublished
article:published_time
meta:titlePump.fun — Launch and trade memecoins on Solana

두 토큰 페이지 모두 og/jsonld/meta 값이 완전히 동일하며 해당 토큰이 아니라 Pump.fun 플랫폼 전체를 설명한다.

만들지 않는다
P5gated응답이 로그인 요구 · 페이월 · 봇 차단 안내문이다4도메인 · 66건 · 규칙 R1

무엇이 어느 칸으로

출력 칸이 패턴에서의 출처채워진 도메인
text.primaryog:titlemeta:title4/4
text.bodymeta:descriptionog:description2/4
publishedAt0/4
mediaUrls[0]og:image2/4
tags[]0/4

실측 — 대표 도메인

t.co · 토큰 35건
og:titleInstagram
og:description
jsonld:headline
jsonld:datePublished
article:published_time
meta:titleInstagram

단축 URL이 인스타그램 로그인 요구 페이지로 리다이렉트되어 값이 전부 로그인 유도 문구다.

만들지 않는다 — unseen 으로 기록
P6spa클라이언트 렌더라 메타가 구조상 비었다2도메인 · 12건 · 규칙 R2

무엇이 어느 칸으로

출력 칸이 패턴에서의 출처채워진 도메인
text.primarymeta:title1/2
text.bodymeta:description1/2
publishedAt0/2
mediaUrls[0]0/2
tags[]0/2

실측 — 대표 도메인

bilibili.com · 토큰 6건
og:title
og:description
jsonld:headline
jsonld:datePublished
article:published_time
meta:title牛奶是只客服猫的个人空间-牛奶是只客服猫个人主页-哔哩哔哩视频

spa=true 이고 og:title/og:description 이 둘 다 비어 있어 R2 에 해당하며 meta 필드도 사용자명만 바뀌는 프로필 템플릿이라 실질 정보가 없다

만들지 않는다 — unseen 으로 기록
P7unfilled템플릿을 깔고 내용을 안 채웠다5도메인 · 48건 · 규칙 R3

무엇이 어느 칸으로

출력 칸이 패턴에서의 출처채워진 도메인
text.primarymeta:title4/5
text.bodymeta:description1/5
publishedAt0/5
mediaUrls[0]0/5
tags[]0/5

실측 — 대표 도메인

longdog.lol · 토큰 9건
og:title
og:description
jsonld:headline
jsonld:datePublished
article:published_time
meta:titleLong Dog

워드프레스 기본 설치 그대로라 meta 제목만 사이트명이고 og/jsonld/설명/이미지가 전부 비어 있다

만들지 않는다 — placeholder 로 기록. 결측 자체가 신호다

4결측을 어떻게 기록하나

G-6 이 “계약된 필드를 채우는 것을 보장하거나 아예 만들지 않는다” 로 껍데기 객체를 금지한다. 그런데 못 채우는 이유가 넷이고, 넷의 뜻이 서로 다르다.

못 채울 때 무엇을 남기나

셋은 “정보가 없다” 이고 하나는 “정보가 있다” 다. 넷을 한 값으로 접으면 마지막 하나를 잃는다.

상황Content무엇을 남기나
P4 사이트 공통 문구안 만든다site_wide값을 넣으면 같은 문구가 수백 행에 복제된다. coincommunities.org 144개 토큰이 전부 같은 Pump.fun 소개문을 갖게 된다
P5 로그인벽 · 봇 차단안 만든다gated대상 쪽 사정이라 재시도로 풀릴 수 있다. 영구 실패와 구분해야 한다
P6 SPA안 만든다unseen구조상 못 본 것이지 없는 것이 아니다. server 필드를 남긴 이유가 이 구분이다
P7 템플릿 미기입안 만든다placeholder 🔴이것만 신호다. 채울 자리를 만들어 놓고 안 채웠다는 뜻이라, 소유권 조사에서 CA 를 안 채운 도메인 집단과 정확히 겹쳤다

결측을 하나로 접으면 안 되는 이유가 실측으로 확인됐다. 컨텐츠 조사에서 text 가 “있지만 쓸모없음” 으로 판정된 토큰사이트가 longdog.lol · pumpga.fun · littlepoe.lol · crypto.grok.me · darkarena.app 인데, 이것은 앞서 소유권 조사에서 계약 주소를 안 채운 것으로 나온 바로 그 도메인들이다.

두 조사는 서로를 몰랐다. 그런데 같은 집단을 가리켰다. 템플릿을 깔고 내용을 안 채운 사이트는 계약 주소도 메타데이터도 안 채운다. 이 결측을 absent 로 뭉개면 그 신호가 사라진다.

그 밖에 정한 것 둘

5루브릭 — 배정 절차

병렬 작업자 아홉이 같은 결론에 도달해야 해서, 판정을 순서가 있는 캐스케이드로 고정했다. 먼저 걸리는 규칙에서 멈춘다.

판정 캐스케이드와 실제 적용 횟수

못 쓰는 경우를 먼저 걸러내고 쓸 수 있는 것만 등급을 매긴다. 순서를 뒤집으면 로그인 안내문이 “제목과 요약이 다 있다” 로 읽혀 P1 에 들어간다.

순서조건결과적용
R1og · twitter · meta 값이 로그인 요구 · 페이월 · 봇 차단 · 쿠키 동의 문구인가P54
R2spa 가 true 이고 og 제목·설명이 둘 다 비었나P62
R3미기입 표식이 남았거나, 토큰사이트인데 설명이 홈 카피 그대로인가P75
R4값이 이 페이지가 아니라 사이트 전체를 설명하는가P422
R5a제목과 요약이 둘 다 이 페이지 것이고 발행시각도 있다P141
R5b제목과 요약이 둘 다 이 페이지 것인데 발행시각이 없다P231
R6제목만 이 페이지 것이고 요약은 없거나 일반 문구다P313
R7어디에도 안 맞는다 — 가장 보수적인 쪽으로P46

샘플이 두 개인데 서로 다른 패턴으로 보이면 더 나쁜 쪽을 택하게 했다. 낙관 쪽으로 기울면 구현할 때 빈 필드가 나온다. 그렇게 갈린 것이 19건이다.

확신도는 high 86 · medium 28 · low 10 였다. low 10건은 대부분 한 도메인에 성격이 다른 URL 이 섞인 경우이고, §2 의 “패턴을 도메인에 고정하지 않는다” 가 그 대응이다.

이 배정의 한계 셋.

첫째, 도메인당 URL 을 1~2개만 봤다. 그 도메인의 나머지 URL 도 같은 모양일 것이라는 추정이다. 뉴스사처럼 기사마다 다른 곳은 샘플 하나가 대표할 뿐이다.

둘째, 124 도메인만 배정했다. 조사 대상 180개 중 56개는 403 봇 차단으로 못 열었고, 공유 도메인 797개 전체로 보면 더 적다.

셋째, 판정 시점이 2026-08-11 이다. 사이트는 리뉴얼되고 메타는 바뀐다. 다만 표준 8종은 사이트가 유지할 유인이 따로 있어(검색 리치결과 · SNS 공유 미리보기) CSS 셀렉터보다 훨씬 오래 간다 — 그것이 표준만 보기로 한 이유다.

배정 데이터는 scaffold/website-patterns.json, 실측 원문은 scaffold/website-extract-samples.json, 이 페이지를 만드는 것은 scaffold/render-website-content-mapping.py 다.