実践ガイド

日次ニュースの48時間判定を修正:人工14例と日本語JSON 3件の追試

日次ニュースの整理ツールは、ニュースの内容以前に入力形式の変化で止まることがあります。2026年9月5日に作った判定ツールも、従来の items / url / published_at では動いた一方、9月11日と15日の日本語キーを持つJSONでは終了コード2になりました。

そこで9月15日、失敗を再現してから、項目 / URL / 公開日時 だけを内部形式へ写す処理を追加しました。修正版では9月15日の3件を読み取れましたが、3件とも日付だけで時刻がなかったため review です。結論は「3件が新しい」でも「3件が誤り」でもありません。形式を読めたことと、48時間内だと確定できたことを分けるのが今回の判断です。

9月5日の成功が、9月11日に通用しなかった

初回の9月5日は、Windows、PowerShell、Python 3.14.2、標準ライブラリだけで検証しました。両端を含む48時間を対象に、人工入力14件を4区分へ分け、10個のテストメソッドを通しました。当時の実素材5件は英語キー形式で読み取れ、公開日が年月日だけだったため5件とも review になりました。

しかし、9月11日の入力はトップ階層が 項目、各行が URL と 公開日時 になっていました。旧版は items がないと入力なしとして扱うため、9月11日の4件、9月15日の3件のどちらも次の結果で停止しました。

Input rejected: items must be a list
exit code: 2

これはニュース7件の内容や鮮度を否定した結果ではなく、入力schemaを読めなかっただけです。日次レポートは題材発見用の内部素材なので、本稿にも配布ファイルにもニュース本文、要約、内部の事業案を転載していません。

検証条件と、変えてよいキーを固定した

追試日は2026年9月15日(日本時間)、環境はWindows、PowerShell、Python 3.14.2です。追加ライブラリ、API、URLへのアクセス、外部送信は使っていません。実素材では各レポートの調査期間終了時刻を --as-of に指定し、そこから48時間を閉区間として判定しました。

入力読み取る階層行から写すキー欠損・混在時
従来のオブジェクトitemsurl, published_at欠損はreview
従来の配列配列自体url, published_at欠損はreview
現行の日本語オブジェクト項目URL, 公開日時欠損はreview
両schemaが混在読み取らない推測変換しないexit 2
未知のトップ階層読み取らない似たキーを探さないexit 2

タイトル、要約、カテゴリ、活用案などは無視し、出力もしません。URLや日時のない行は消さず、missing_date や invalid_url の理由付きでreviewへ残します。

許可した2項目だけを新しい辞書へ写す

修正の中心は、判定前の小さな正規化処理です。以下は公開コードからの抜粋です。

LEGACY_ROW_KEYS = ("url", "published_at")
JAPANESE_ROW_KEYS = ("URL", "公開日時")

row = {}
if source_keys[0] in item:
    row["url"] = item[source_keys[0]]
if source_keys[1] in item:
    row["published_at"] = item[source_keys[1]]
normalized.append(row)

元の辞書を書き換えず、新しい辞書を作ります。混在検知はこのコピーより先です。たとえばトップに 項目 があるのに行へ published_at が入っていたら、都合のよい方を採用せず入力全体を拒否します。柔軟性より、schema変更を見落とさないことを優先した設計です。

人工14例は同じ内訳、テストは10から17へ増えた

9月5日の人工14例を修正版へ再入力した結果は変わりませんでした。

区分 件数 意味
keep 4 URLと日時が今回の期間条件を満たす候補
review 8 日時不足、未来、矛盾、不正形式などで確認待ち
outside_window 1 指定した48時間より前
duplicate 1 完全一致URLかつ同じ公開時点の先行行がある

合計は14件です。keep は内容の正しさや公開承認、outside_window は情報の無価値、duplicate は削除許可を意味しません。

テストメソッドは初回の10個から17個へ増やしました。追加した7つは、日本語schemaで許可キーだけを写す、従来形式を維持する、入力を変更しない、トップ階層と行の混在を拒否する、未知schemaを拒否する、URLや日時の欠損をreviewに残す、という回帰確認です。17テストはすべて通過しました。「17」はニュース件数ではありません。

9月11日の4件と9月15日の3件を追試した

入力日項目数旧版修正版修正版の内訳
2026-09-114exit 2、schema未対応exit 0review 4、全件 date_only
2026-09-153exit 2、schema未対応exit 0review 3、全件 date_only

タイトルの「日本語JSON 3件」は最新の9月15日分です。9月11日分は4件であり、合計3件とは数えていません。修正版のexit 0は集計完了の意味で、一次情報確認済みや公開可能の意味ではありません。

入力のSHA-256は処理前後で一致しました。9月11日は 1C2FB2E5CCA78631B326732EBD6077A5EACD015B25C5BA62F3A5439FC274C667、9月15日は DCAEC18B4A7712AE38C3209792C7E473537A6A6A18C6EC5F6524F0BB15938024 です。ハッシュ一致は同じバイト列だった証拠で、内容の正確性は保証しません。

手元で人工入力を再実行する

コード・人工入力・テスト・説明書の4ファイルを展開し、PowerShellで次を実行します。

python -m unittest discover -s . -p 'test_*.py' -v
python .\report_gate.py .\cases.json --as-of "2026-09-05T05:30:00+09:00" --summary

2つ目は keep=4, review=8, outside_window=1, duplicate=1 を返します。実素材や秘密情報をZIPへ追加する必要はありません。

日時はタイムゾーン付きの時刻だけを48時間の比較対象にします。Python公式のdatetime資料は、awareな日時と、時点を一意に置けないnaiveな日時を区別しています。日付だけをreviewにするのは、それを踏まえた本ツールの保守的な編集ルールです。

URLは最低限の構文だけを確認します。Python公式のurllib.parse資料にも、urlsplit() 自体は入力検証を行わないという注意があります。存在、信頼性、安全なアクセス先かは判定外です。両資料は9月15日に開き直しました。

実務への影響と、次に人が確認すること

完全一致URLと同じ公開時点しか重複候補にしません。追跡引数だけ違うURL、別URLで同じ出来事を扱う記事、本文が更新された同一URLは判別できません。日付だけの項目へレポート全体のタイムゾーンと午前0時を補うこともしないため、今回のようにreviewが多く残ります。

次はreview行の一次情報を開き、発表日と更新日、時刻とタイムゾーンを確認します。確認できなければ48時間内という表現を外すか、判断を保留します。keep でも本文の裏取りと独自検証は別に必要です。

Lyriaの公式資料を6項目で照合した記事は、一つの主張を一次資料へ戻す方法を扱います。本稿はその前段で、JSONの形式・URL・日時を壊さず仕分ける方法です。メタデータを通過した候補に次の照合票を使う関係で、検索意図は重なりません。

表紙は工程を表す生成イラストで、検証証拠ではありません。独自証拠は失敗と修正後のコマンド結果、人工入力、テスト、入力ハッシュです。

一次情報・出典

  1. Python公式:datetimeのawareとnaive
  2. Python公式:urllib.parseとURL検証の限界