🔤
古いレシピサイトの文字化けをJavaScriptで解決する
はじめに
レシピのURLを入力すると自動でレシピ情報を取得するWebアプリを作っています。このアプリの開発にはClaude Code(AIコーディングアシスタント)を活用しています。
開発中に気づいたのですが、古いレシピサイトのURLを入力すると内容が文字化けしてしまう問題が発生しました。この記事ではその原因と解決方法を紹介します。
なぜ文字化けするのか
現在のWebサイトはほぼUTF-8で書かれていますが、2000年代以前に作られた日本語サイトの多くはShift-JISやEUC-JPという文字コードを使っています。
fetch()で取得したデータをそのままUTF-8として読み込もうとすると、文字コードが合わないため文字化けが起きます。
解決策:文字コードを自動判別する
文字コードを判別するために、以下の順番で確認します。
- HTTPレスポンスヘッダの
Content-Typeにcharsetが含まれているか - HTMLの
<meta charset>タグに文字コードが書かれているか - どちらもなければ
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にも対応しているので、判別後はそのまま使える
Discussion