LLMにMarkdown形式の記事を生成させ、HTMLへ変換してWordPressに投稿する運用では、本文にもタイトルが残ったり、h2の次にh4が出たりすることがあります。見出しを確認する処理を投稿前に置くと、こうした構造を検出・補正できます。

まずテーマが出す見出しを確認する

見出しの正規化は構造で行う。投稿先の規則を確認:タイトルと本文の役割を分ける、どの見出し階層が必要か決める。HTMLを扱う:解析した構造から対象を変更、引用やコード中の文字列は変更しない。結果を確認:階層と目次を確認、h1の数だけでアクセシビリティを判断しない
HTMLの構造と、ページ全体の見出しの関係を確認する。

以下の例は、WordPressテーマが記事タイトルをh1として表示し、本文の主な節をh2から始める構成を想定しています。テーマによって出力は異なるので、実際のページ全体を確認してください。H1の個数が検索順位を直接決めるという説明にはしません。

見出しは文字の大きさではなく、内容の関係を示すために使います。支援技術を使う読者が節の構造を理解できるように、W3C WAIの見出し解説に沿って意味を確認します。階層の飛びだけでWCAG違反と即断することも避けます。

HTMLへの変換後に解析する

処理順は「MarkdownからHTMLへ変換 → 本文の見出しを検査・補正 → 安全検査 → 下書きとして保存 → 人が確認」です。下の関数はHTMLを受け取ります。Markdownの # タイトル を正規表現で除去する処理は含みません。

Node.jsでCheerioを使う例です。対象Node.js版と互換性のあるCheerioを依存に追加し、lockfileで固定します。記事内の例はこのプロジェクトの実行依存には追加しておらず、ここでCheerioを使った実行検証は行っていません。

<h1>記事タイトル</h1>

<article>
  <h1>記事タイトル</h1>        
  <p>導入...</p>
  <h2>最初のセクション</h2>
</article>

この関数が変更すること

  • 最初の見出しがh1で、空白を正規化した文字列が記事タイトルと一致する場合、その見出しだけ除去します。
  • 残る本文見出しはh2以上にし、直前の見出しから二段以上深くなる場合は一段下までに補正します。
  • 見出しの属性と内部要素を引き継ぎます。pre/code内の見出しは処理対象から除外します。

一致しないh1は内容を捨てずにh2へ変更します。タイトルを動的な正規表現へ渡さないため、タイトルに括弧や記号があっても正規表現用のエスケープは不要です。

ただし機械的な補正は、節の意味上の親子関係を判断できません。h2→h4をh3へ変えてよいかは、人のレビューで確かめます。HTMLパーサーはシリアライズ時に表記を変えることがあるため、入力と出力の差分も確認してください。

投稿前に確認したい例

  • タイトルと同じh1、異なるh1、見出しのない本文。
  • h2→h4、h3→h5、浅い階層へ戻る見出し、属性やリンクを含む見出し。
  • pre/code内のサンプル、HTMLエンティティを含むタイトル、同じ関数を二度通した場合。
  • テーマのタイトル出力と組み合わせた最終ページ、目次、既存アンカーへのリンク。

これらは採用前に追加したいテスト項目です。コード例をそのまま本番の自動投稿へ接続する前に、隔離したサンプル記事で確かめてください。

安全検査と承認は別に設ける

この関数はscript要素、イベント属性、危険なURLを除去するサニタイザーではありません。生成HTMLを信頼せず、許可する要素・属性・URLを定めた安全検査を別途行います。見出し補正に成功しても、本文の事実確認や公開承認が済んだことにはなりません。

よくある質問

本文のh1は必ず削除すべきですか?
テーマとページ全体の構造によります。この例では、テーマがタイトルをh1として出す前提で、重複する同じタイトルのみ削除します。

Markdownへ直接適用できますか?
できません。HTMLへ変換してから渡します。Markdown段階で直すなら、別のMarkdown解析処理を設けてください。

これで投稿を完全自動化できますか?
見出し補正だけの例です。HTMLの安全検査、事実確認、認証、投稿先への権限確認、公開承認は別に必要です。