実践ガイド(2026.09.27 更新)

HTMLとJSONの整合検査:実入力3件でURL差分1件を再現

同じ日次レポートからHTMLとraw JSONを作っていても、片方だけURLを直すと、見た目の件数は合っているのに参照先がずれます。2026年9月25日の実入力3件を標準Pythonで照合したところ、日付と件数は一致しましたが、GitHubの一次URL文字列1件だけが不一致でした。

結論は、3件中1件のURL文字列に差分があるです。記事内容の誤りやリンク切れを示す結果ではありません。ツールはURLを開かず、転送やcanonical URLも推測しないため、どちらが正しいかは人が一次情報を確認します。

何を比べ、何を読まないか

検証日は2026年9月25日(日本時間)、環境はWindowsとPython 3.14.2です。追加パッケージ、API、外部送信は使っていません。入力は同日の日次レポートHTMLとraw JSONで、次の項目だけを読みました。

形式 日付 件数 一次URL
HTML title内のYYYY-MM-DD article.newsの数 各記事のp.sourceにある最初のリンク
JSON 調査日 採用件数と項目の配列長 各項目のURL

タイトル、要約、考察、事業案は比較にも出力にも使いません。公開するfixtureもexample.comだけで作り、実レポート本文を含めていません。

入力のSHA-256は実行前後で一致しました。HTMLは EB045D42482FED112CE3B8BE6DD60B16E6CB2668CF24FC9D4164035704E20544、JSONは DBD2D699C149CA085F8953A74475C2890A241F77096979A954823E143B3460A2 です。これは検査中にバイト列を変更しなかった証拠であり、入力内容の正確性を保証するものではありません。

実入力3件では、URLだけ1件が異なった

実行結果は次のとおりです。HTMLの3記事、JSONの宣言3件、JSONの配列3件は一致しました。一次URLはAnthropicとGoogleの2件が完全一致し、GitHubの1件が異なりました。

検査 結果 観測した値
日付 一致 HTML / JSONともに2026-09-25
件数 一致 HTML 3、JSON宣言3、JSON配列3
一次URL 不一致 3件中2件一致、1件差分
全体 FAIL 終了コード1

差分のURLは次の2文字列です。

HTML: https://github.blog/security/application-security/ai-powered-fuzzing-with-the-github-security-lab-taskflow-agent/
JSON: https://github.blog/ai-and-ml/ai-powered-fuzzing-with-the-github-security-lab-taskflow-agent/

GitHub公式の記事は9月25日に開き直し、ページ上の公開日が9月24日であることを確認しました。ただし、この検査ツール自体はHTTP通信をしません。古いURLが転送されるか、2つが同じ内容か、将来も到達できるかは検査外です。

9月27日の週次追試では、2つのURLを個別に取得すると、どちらもHTTP 200で、同じsecurity/application-security側のcanonical URLと同じWordPress REST APIのpost IDを示しました。したがって追試時点では同じ公開記事を指す別URLです。ただし、これは9月25日に観測したURL文字列の差分を取り消すものではなく、オフライン検査へ転送・canonical判定を追加した結果でもありません。将来の到達性や生成工程でURLが変わった原因は、引き続き別の確認事項です。

実入力3件では日付と件数が一致し、一次URL文字列が2件一致・1件差分、人工fixtureと8テストの結果を示す要約図

上図はコマンド出力の件数を再構成した生成図で、生ログの画像ではありません。再現可能なコードと固定入力を次に示します。

標準Pythonで日付・件数・URLを比較する

コード、人工fixture、8件のテスト、READMEをまとめたZIPを展開し、そのフォルダで実行します。

python -m unittest discover -s . -p 'test_*.py' -v
python .\report_consistency.py .\fixtures\match.html .\fixtures\match.json
python .\report_consistency.py .\fixtures\mismatch.html .\fixtures\mismatch.json

一致fixtureはPASS、意図的にURLを変えたfixtureはFAILになります。中心となる比較は次の4条件です。

checks = {
    "date": html.date == raw.date,
    "html_json_count": html.observed_count == raw.declared_count,
    "json_count": raw.declared_count == raw.observed_count,
    "url_sequence": html.urls == raw.urls,
}

Python公式のjson資料に沿ってJSONを読み、Python公式のHTMLParser資料のイベントでHTML要素と属性を取り出します。正規表現だけでHTML全体を解析する構成にはしていません。

終了コード0・1・2を混ぜない

終了コード 意味 次の行動
0 定義した4検査がすべて一致 内容確認やリンク確認を別に続ける
1 入力は読めたが差分あり どちらが正しいか一次情報と生成工程を確認する
2 UTF-8で読めない、必須項目がない、JSON不正など 入力形式や文字コードを直して再実行する

8件の単体テストは、人工PASS、人工FAIL、3種類の終了コード、HTMLとJSONの件数差分、JSON宣言件数の差分、一次URL欠落、補足リンクを一次URLへ数えないことを確認し、すべて成功しました。有限のfixtureであり、任意のHTMLを正しく解釈する形式証明ではありません。

詰まった点と限界

HTMLには1記事あたり複数の出典リンクがありました。JSONの各項目には代表のURLが1つなので、HTML側もp.source内の最初のリンクだけを一次URLとして比較しました。2番目以降の技術資料やリポジトリは、今回の契約では対象外です。サイト構造が変わったら、黙って0件にせず入力エラーで止める必要があります。

URLは完全一致で比較します。末尾スラッシュ、大小文字、追跡引数、URLエンコード、HTTPからHTTPSへの転送を同一視しません。反対に、同じURL文字列でも本文が更新されたことは検出できません。URLの存在、安全性、内容の正しさ、公開日も保証しません。

また、HTMLの表示件数を本文中の自然言語から拾うのではなく、article.newsを数えます。画面上に別のカードが追加された場合やJavaScriptが後から要素を作る場合は、入力契約の見直しが必要です。

実務への影響:生成直後に止める場所を作る

この検査は、記事の正しさを判定する仕組みではなく、HTML生成とJSON保存の間に生じた差分を公開前に止める小さなゲートです。日付と件数だけが合っていても、今回のように代表URLだけが変わることがあります。URLを内容確認の入口にしている運用では、別の日の再検証時に違うページを開く原因になります。

終了コード1をCIや日次処理の停止条件にすれば、「差分があるのに成功扱い」を避けられます。ただし自動で片方へ上書きしないことが重要です。HTML側が新しいとは限らず、JSON側が正しい可能性もあるため、差分を人へ渡し、一次情報と生成元を確認してから直します。終了コード2は差分ではなく入力契約の破損なので、同じ修正手順へまとめません。

運用では、生成前の原本、JSON、HTMLの順にどこでURLが変わったかを確認し、修正後に再実行します。PASSになっても、リンクの到達性、主張、公開日、料金、仕様は別の一次情報確認が必要です。機械検査の対象と編集判断を分けることで、PASSを公開承認と誤解しにくくなります。

既存記事との使い分けと次の確認

ニュース候補の48時間判定は、JSON内の公開日時と重複候補を仕分ける記事です。本稿は、同じレポートを2形式へ出した後の日付・件数・一次URLを比較します。また、静的サイトの内部リンク検査は、ビルド済みサイト内のページと見出しの行き先を調べます。本稿はサイト内部リンクではなく、内部レポート2形式の代表URL文字列が対象です。

9月27日の追試で2つのURLが同じ公開記事を指すことまでは確認しました。次は生成工程のどの段階でURL文字列が変わったかを確認し、原本側で採用するURLを決めてから再実行します。さらに公開前にはURLを実際に開き、一次情報の公開日と重要な主張を別に確認します。表紙は生成イラストで検証証拠ではなく、独自証拠は実入力の比較、入力ハッシュ、人工fixture、単体テスト、URL到達先の追試です。

一次情報・出典

  1. Python公式:json
  2. Python公式:HTMLParser
  3. GitHub公式:AI-powered fuzzing(2026-09-24)