【PHP】スクレイピングの文字化けを全力で治す

製作 プログラム

最終更新日:2024/07/18

ラムネグから一言:寝る前に読むとくだらなすぎて逆に寝れると好評なすごい適当なブログをこっちではじめてます.

いやぁ…、2日ハマりました。手ごわかったです。今回の文字化け。本当になんで文字コードなんてあるんだろうって思っちゃいますよね、UTF8以外すべて殺してしまえっ!と織田信長ばりに思わずにいられません。

ここではPHPでスクレイピングしている時、文字化けがどうしても治らない時の治し方を書きます。ぼくの場合は素のPHPではなく、laravelというフレームワークでの文字化けでしたが素のPHPでも同じだと思うので参考にしてみてください。

  1. 場合分けが大事!
    1. ①PHPにあらかじめ入っているloadHTMLで文字化けしているのか
    2. ②何かしらのスクレイピングライブラリ(もしくはHTMLパーサー)を使っていて文字化けが起こるのか
    3. ③特定のサイトのみで文字化けが起こるのか
  2. まとめ

場合分けが大事!

文字化けが治らない時、一番大事なのが場合分けです。もうやられていると思いますが、

  1. PHPにあらかじめ入っているloadHTMLで文字化けしているのか
  2. 何かしらのスクレイピングライブラリ(もしくはHTMLパーサー)を使っていて文字化けが起こるのか
  3. 特定のサイトのみで文字化けが起こるのか

ここらへんを場合分けしていくと文字化けも直しやすくなります。

①PHPにあらかじめ入っているloadHTMLで文字化けしているのか

PHPはデフォルトでHTMLパーサー「loadHTML」が入っていて、これを使うとライブラリを入れなくてもPHP単体でスクレイピングができます。

ただ、このloadHTMLは少々使いづらく、日本語が文字化けしやすい…みたいです。調べればわんさか文字化けの事例が出てきます。

loadHTMLでの文字化けの治し方は2つあって、

$dom = new \DOMDocument('1.0', 'UTF-8');
libxml_use_internal_errors( true );
$html = file_get_contents('https://google.com');
libxml_clear_errors();
$dom->loadHTML( mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8') );
$dom = new \DOMDocument('1.0', 'UTF-8');
libxml_use_internal_errors( true );
$html = file_get_contents('https://google.com');
libxml_clear_errors();
dom->loadHTML('<?xml encoding="UTF-8">' . $html);

どちらも4行目まで全く同じで5行目だけ違います。5行目で「UTF-8」の文字コードを設定してます。もし対象のウェブサイトがシフトJISなら「shift_jis」としてください。「Shift_JIS」や「SJIS」みたいな大文字表記でも行けましたが、一応ぼくの環境では小文字で安定して動いています。

ちなみに両方のコードで共通の2行目と4行目でHTMLタグ読み込み時のエラーを無効化している(これがないとエラーで実行できない)ので、loadHTMLを使う場合はこの2行もつけてくださいね。

②何かしらのスクレイピングライブラリ(もしくはHTMLパーサー)を使っていて文字化けが起こるのか

phpにはいくつかスクレイピング用の便利なライブラリがありますよね。

「Goutte」が更新されなくなったんで、今だと「Guzzle」+Symfonyの「DomCrawler」とかでスクレイピングしてる方が多いのかもしれません。

で、こういうスクレイピングライブラリを使っていて文字化けが発生したなら、いったん①のようなライブラリを使わない方法、それから他のスクレイピングライブラリでも同じく文字化けが発生しちゃうのか見てみるのがいいです。

発生するのであればPHPのかなり深い所で文字化けが発生している(例えばcurlとか)か、もしくは次項の話になりますが、そのスクレイピングしたいサイト特有のどデカい原因があるのか、という事になります。

③特定のサイトのみで文字化けが起こるのか

僕の場合、今回このケースでした。なぜか特定の1サイトのみ文字化けが発生し続ける、という。

僕も最初、②で書いたようにGuzzleとDomCrawlerを組み合わせてスクレイピングしててそれで文字化け、場合分けするために①のloadHTMLを使ってみても文字化け…、でcurlか対象サイト自体の問題を疑うに至る、というながーいハマりでした(さらにいうと他言語でも試していてPythonでやってみたらちゃんとスクレイピングできちゃうというのもモヤモヤ)。

で、特定サイトのみで文字化けが起こる時、そのサイトの「<meta charset="utf-8">」を信じない、というのが大事だと思います。結局、今回僕がハマった文字化けの場合、タグで宣言されてる文字コードとそのファイルをサーバーにアップロードした際の保存形式としての文字コードが違ったのが原因で文字化けしてました。

めっちゃわかりやすく書くと「WEBデザイナーのミス」という事です。

なので①の方法で「UTF-8」になってる所を他の文字コード「shift_jis」とか「EUC-JP」に変更してみてください。

結局、プログラムの方はパースするHTMLのタグとかヘッダーの「content type」とかからそのサイトの文字コードを自動取得するんですが、それと実際のファイルの保存形式が違ったらそりゃ文字化けしちゃうよね、という事でした。

「PHP君:スクレイピングしちゃうぞっ!よしっ、このサイトはメタタグにUTF-8って書いてあるからこれで解析していくぞっ!」「実際のファイル君:ふっふーん、ぼくタグではUTF-8って言ってるけれど実際の保存形式はSJISなんですよねぇ…」という罠。

まとめ

PHPでスクレイピングをしている時に文字化けしちゃうときの治し方をケースごとに書きました。

loadHTMLは文字化けしやすいんで、PHPでスクレイピングするならGuzzleとDomCrawlerのライブラリを使うのがいいんだけれど、サイトの制作者の保存ミス?によってはライブラリを使っていても普通に文字化けするんで、メタタグを信じないようにしよう!という感じ。

参考にしてみてくださいね。

【おしらせ、というか完全なる宣伝】

文体がもうぜんぜん適当すぎてあれだけどものすごい自由に書いてるブログ「檸檬だくだく」もよろしく.寝る前に読める恐ろしくくだらないやつです.

こんなにも一ミリも目を引かれないタイトルを取り扱ってます: ココア20g / ハイチュウとかってさ / なぜ米と小麦を食べようと思ったのかの謎 /