🔤

古いレシピサイトの文字化けをJavaScriptで解決する

に公開

はじめに

レシピのURLを入力すると自動でレシピ情報を取得するWebアプリを作っています。このアプリの開発にはClaude Code(AIコーディングアシスタント)を活用しています。

開発中に気づいたのですが、古いレシピサイトのURLを入力すると内容が文字化けしてしまう問題が発生しました。この記事ではその原因と解決方法を紹介します。

なぜ文字化けするのか

現在のWebサイトはほぼUTF-8で書かれていますが、2000年代以前に作られた日本語サイトの多くはShift-JISやEUC-JPという文字コードを使っています。

fetch()で取得したデータをそのままUTF-8として読み込もうとすると、文字コードが合わないため文字化けが起きます。

解決策:文字コードを自動判別する

文字コードを判別するために、以下の順番で確認します。

  1. HTTPレスポンスヘッダの Content-Typecharset が含まれているか
  2. HTMLの <meta charset> タグに文字コードが書かれているか
  3. どちらもなければ UTF-8 をデフォルトとして使う
function detectEncoding(buffer, contentType) {
  const ctCharset = contentType?.match(/charset=([^\s;]+)/i)?.[1];
  if (ctCharset) return ctCharset;

  const preview = new TextDecoder("latin1").decode(buffer.slice(0, 2048));
  const metaCharset = preview.match(/<meta[^>]+charset=["']?([^"'\s;>]+)/i)?.[1]
    || preview.match(/http-equiv=["']?content-type["'][^>]+content=["'][^"']*charset=([^"'\s;]+)/i)?.[1];

  return metaCharset || "utf-8";
}

取得したバイト列はこのように処理します。

const buffer = await res.arrayBuffer();
const encoding = detectEncoding(buffer, res.headers.get("content-type"));
let html;
try {
  html = new TextDecoder(encoding).decode(buffer);
} catch {
  html = new TextDecoder("utf-8").decode(buffer);
}

ポイント:なぜlatin1でプレビューするのか

このコードはClaude Code(AIコーディングアシスタント)に実装してもらったもので、私自身がすべてを完全に理解しているわけではありません。参考程度に読んでいただければ幸いです。

Claude Codeによると、latin1(ISO-8859-1)はバイト値0〜255をそのまま1文字として扱うため、どんなバイト列でも文字化けせずに読み込めるとのことです。

HTMLタグの文字コード宣言はすべてASCII文字(0〜127)で書かれているので、latin1で読んでも正しく解析でき、文字コードがわかってから改めて正しいデコーダーで全体を読み直す、という流れになっています。

まとめ

  • 古い日本語サイトはShift-JIS / EUC-JPを使っているため文字化けする
  • HTTPヘッダとHTMLのmetaタグから文字コードを判別できる
  • metaタグを読むためにlatin1を使うのがポイント
  • TextDecoderはShift-JISやEUC-JPにも対応しているので、判別後はそのまま使える
GitHubで編集を提案

Discussion