PHPによる文字コードが違うページのスクレイピングで気をつけること


 

Webスクレイピングを行う際、対象ページの文字コードが異なることはよくあります。特に日本語のサイトでは、Shift-JISやEUC-JPが使われているケースもあり、単純にUTF-8へ変換すると文字化けや解析エラーの原因になります。本記事では、PHPを使って異なる文字コードのページを適切にスクレイピングする方法について解説します。

1. エンティティとは?

HTMLやXMLなどのマークアップ言語では、一部の文字(たとえば特殊文字や言語固有の文字)を、そのまま記述すると誤解や衝突を招く場合があります。 そこで、文字を「エンティティ」と呼ばれる別の形式に変換する方法が使用されます。

主なエンティティの例

名前付きエンティティ

例:

& → &(アンパサンド)
&lt; → <(小なり記号)

数値エンティティ

例:

&#12345; のような形式で、文字のコード番号(Unicodeコードポイントなど)を使って表現する。
例えば、「あ」は `&#12354;` のように表現される場合があります。

エンティティに変換することで、マークアップ内で正しく文字を表示したり、パース処理を行う際にトラブルが起こりにくくなります。

2. mb_convert_encoding()'HTML-ENTITIES' へ変換する理由

通常、mb_convert_encoding() を使ってエンコーディングを変換するときは、例えば Shift-JIS から UTF-8 へ変換する、といった形が一般的です。 しかし、直接 'UTF-8' に変換してしまうと、HTML内の特殊文字や日本語がそのままのバイト列として変換されるため、DOMDocument がそのまま読み込んだときに正しく解釈できない場合があります。

そこで、mb_convert_encoding() の第2引数に 'HTML-ENTITIES' を指定します。これにより、以下のような効果があります:

数値エンティティへの変換

たとえば、Shift-JIS などの文字コードで表現された日本語の文字は、&#NNNN;(ここで NNNN はその文字のUnicodeコードポイント)という形の数値エンティティに置き換えられます。

例: こんにちわ!&#12371;&#12435;&#12395;&#12385;&#12399;!

DOMDocument の正確な解釈

DOMDocument は HTML を読み込む際、内部でエンティティに置き換えられた部分を「正しいUTF-8文字列」として復元して解釈します。 そのため、getAttribute('value')textContent で取り出すと、本来意図したUTF-8の日本語文字列が得られるようになります。

3. この仕組みを使ったコードの流れ

1. HTMLソース取得

相手フォームから得た HTML(Shift-JIS 等の文字コードで送られてくる文書)を $response に取得します。

2. mb_convert_encoding() の使用

$utf8_response = mb_convert_encoding($response, 'HTML-ENTITIES', $target_encoding);

これにより、$response 内の日本語文字などはすべて数値エンティティに変換されます。

例: こんにちわ!&#12371;&#12435;&#12395;&#12385;&#12399;!

3. meta タグの置換

その後、正規表現を用いて、HTML内の meta タグに記述された charsetUTF-8 に書き換えます。 これにより、DOMDocument に読み込ませる際に文字コードの整合性が保たれます。

4. DOMDocument による解析

// HTMLを取得(Shift-JISで送られてくることを想定)
$response = file_get_contents('http://example.com');
$target_encoding = mb_detect_encoding($response, ['UTF-8', 'SJIS', 'EUC-JP', 'ISO-2022-JP'], true) ?: 'Shift_JIS';

// 文字コード変換(エンティティ化)
$utf8_response = mb_convert_encoding($response, 'HTML-ENTITIES', $target_encoding);

// meta タグの文字コードを UTF-8 に書き換え
$utf8_response = preg_replace('/<meta[^>]+charset=[^>]+>/i', '<meta charset="UTF-8">', $utf8_response);

// DOMDocument による解析
$dom = new DOMDocument();
libxml_use_internal_errors(true); // 読み込み時のエラーを抑制
$dom->loadHTML($utf8_response);
libxml_clear_errors();

// 特定の要素を取得して値を表示
$xpath = new DOMXPath($dom);

4. DOMDocument による解析後の処理

上記のように$utf8_response を DOMDocument に読み込むと、内部ではエンティティを正しく解釈し、元々意図したUTF-8の文字列が復元されます。 その結果、例えばフォームの要素から、$field->getAttribute('value')textContent などのように値を取り出すと、元の文字列が正しく得られます。

まとめ

エンティティの基本

HTMLでは、特殊文字や日本語を安全に扱うため、文字を数値エンティティ(例: &#NNNN;)に変換することができます。

mb_convert_encoding()'HTML-ENTITIES'

これを使うと、対象文字列内の Shift-JIS などの文字が数値エンティティに変換され、DOMDocument がそれを正しくUTF-8文字列として解釈できるようになります。

DOMDocument 解析での効果

DOMDocument に読み込んだ後、getAttribute('value')textContent によって得られる文字列は、数値エンティティが元のUTF-8文字列に復元されるため、正しく保持・利用できます。

この仕組みにより、本来の日本語文字列が DOMDocument の解析を経ても正しいUTF-8として得られ、最終的な送信データが意図通りの文字コードとなります。

WEBプログム、WEBデザインなどの制作については、以下を御覧ください。

WEBプログム、WEBデザインなどの制作