現象
あるサイトをスクレイピングしていて、ajaxで取得されるページの内容を取得しようとしたところ
突然 $crawler->html()
したときに日本語が文字化ける現象に遭遇。
文字的には èå /æ±æ¦ä¼å¢å´ã»å¤§å¸«ç·
とかそんな感じ。
原因
「取得しようとしていたページにcharsetがない」
実際はUTF-8なんだけど、headerにもmetaにも明示されてない。
で処理を追っていくと Synfony\ComponetnDomCrawler\Crawler::addContent() にて
/**
* Adds HTML/XML content.
*
* If the charset is not set via the content type, it is assumed
* to be ISO-8859-1, which is the default charset defined by the
* HTTP 1.1 specification.
*
* @param string $content A string to parse as HTML/XML
* @param null|string $type The content type of the string
*/
ということでHTTP 1.1に従ってデフォルトはISO-8859-1にしますよ、と。
対策
単純にレスポンスをmb_convert_encodingしてもダメだったので
以下のような継承クラスを定義してこっちを使う。
<?php namespace MyNameSpace;
use Goutte\Client;
use Psr\Http\Message\ResponseInterface;
use Symfony\Component\BrowserKit\Response;
class GoutteClientCustom extends Client
{
protected function createResponse(ResponseInterface $response)
{
$headers = $response->getHeaders();
if (!isset($headers['Content-Type'])) {
$headers['Content-Type'][0] = 'text/html; charset=UTF-8';
} elseif (stripos($headers['Content-Type'][0], 'charset=') === false) {
if (trim($headers['Content-Type'][0]) && substr(trim($headers['Content-Type'][0]), -1) !== ';') {
$headers['Content-Type'][0] = trim($headers['Content-Type'][0]) . ';';
}
$headers['Content-Type'][0] .= ' charset=UTF-8';
}
return new Response((string) $response->getBody(), $response->getStatusCode(), $headers);
}
}
headerに無理やり charset=UTF-8
をねじ込む。
とりえあずはこれでうまくいった。