写真 ブログ 開発室
HOME開発室PHP[PHP] cURLで読み込んだウェブサイトの文字コードを取得する
キーワード
PHP

[PHP] cURLで読み込んだウェブサイトの文字コードを取得する

[PHP] cURLで読み込んだウェブサイトの文字コードを取得する

cURLで読み込んだウェブサイトの文字コードを知りたい

外部のウェブサイトからOGPを取得したい時など、cURL等を使用してウェブサイトのソースコードを読み込む場合がある。 それを解析する際に適切な文字コードで処理しなければ文字化けを起こす可能性がある。 ではその文字コードはどこから取得すればよいのかという点について考えていきたい。

文字コードを取得する流れ

PHPにはmb_detect_encoding()という文字コードを判別する関数があるが、これは癖が強く正直あまり役に立たない。

それ以外で文字コードを取得する手段だが、知る限りでは以下の3つがある。

  • cURL実行時に取得できるレスポンスヘッダ内のContent-Type
  • HTML内の<meta charset="..." />
  • HTML内の<meta http-equiv="Content-Type" content="..." />

レスポンスヘッダのContentTypeには文字コードが指定されている場合とされていない場合があるので、指定されていない場合はソースコード内の文字コード指定から取得を試みる。 これらがすべて未指定だった場合はSJIS-winと見なす。

レスポンスヘッダからContent-Typeを取得する

PHP
$url = 'https://...'; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HEADER, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 30); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // 指定したURLからソースコードを読み込む $result = curl_exec($ch); // レスポンスヘッダからContent-Typeを取得する $contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);

curl_exec()を呼び出した後で、そのリクエストに使用したハンドラ(ここでは$ch)を使いcurl_getinfo($ch, CURLINFO_CONTENT_TYPE)と呼び出すとレスポンスヘッダのContent-Typeを取得できる。 例えば以下のような値が取得できる。

text/html; charset=UTF-8

このようにcharsetが指定されていればそこから文字コードを取得することができる。 多くの場合ここにきちんとcharsetが指定されているが、たまにtext/htmlだけが指定されていてcharsetが指定されていない場合がある。

その場合はソースコード内から取得を試みる必要がある。

ソースコード内に指定された文字コードを取得する

前述したように、HTMLのソースコード内には次のような形で文字コードを指定してある場合が多い。

  • <meta charset="UTF-8" />
  • <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

<meta charset="..." />があればそこから取得し、無ければ<meta http-equiv="Content-Type" content="..." />を探す。 HTMLの文字列から正規表現で検索する手もあるが、HTMLは書式のばらつきが多いので正規表現で網羅するのは正直かなりめんどくさい。 なので少々回りくどいがXPathで検索する形を取ってみた。

PHP
function GetEncoding(string $html): string { $internalErrors = libxml_use_internal_errors(true); // XPathを初期化する $dom = new DOMDocument(); $dom->loadHTML($html); $xpath = new DOMXPath($dom); $charset = 'SJIS-win'; // <meta charset="..." />を検索 $node = $xpath->query('//meta/@charset'); if ($node->length > 0) { // 見つかればその値を取得 $charset = $node->item(0)->nodeValue; } else { // 見つからなければ<meta http-equiv="Content-Type" content="..." />を検索 $node = $xpath->query('//meta[@http-equiv="Content-Type"]/@content'); if ($node->length > 0) { // contentには"text/html; charset=UTF8"のような値が入る $contentType = $node->item(0)->nodeValue; // そのパターンにマッチしていればcharsetの値を取得 if (preg_match('/.+; ?charset=(.+)/', $contentType, $matches) === 1) $charset = $matches[1]; } } libxml_use_internal_errors($internalErrors); libxml_clear_errors(); // いずれも指定されていなければSJIS-winとみなす return $charset; }

<meta charset="..." /><meta http-equiv="Content-Type" content="..." />がいずれも指定されていない場合はSJIS-winと見なしている。

実際のところ「文字コードがどこにも指定されていないが実際はUTF-8が使われている」という可能性もある。 だがそこまで考慮していてはキリがないのでどこかで妥協することが必要なのではないだろうか。

ひとこと

正直あまりスマートな形ではないと思うが取り合えずこれでかなりの範囲をカバーできたのではないかと思う。 もっと簡潔に取得する方法があれば良いのだが、その辺りは追い追い調べていきたい。

キーワード
PHP
シェアする
サイトマップ SITEMAP 広告 写真 PHOTO
年度
2024 2023 2022 2021 2020 2019 2018 2017 ~2016
アルバム
傑作選 北海道 石鎚山系 ネパール 屋久島 北アルプス 中央アルプス 離島 巨樹
写真の一覧へ
ストックフォトで作品を探す
JOURNEY
2020年 冬の屋久島歩き旅
2019年 厳冬期北海道徒歩横断
旅の一覧へ
ブログ BLOG
カテゴリ
写真 よもやま話
タグ
スポット 道具 心得
ブログの一覧へ
開発室 DEVELOPMENT
カテゴリ
ASP.NET Core C# WordPress PHP TypeScript JavaScript Web
開発室の一覧へ
広告
サイトマップ SITEMAP
スタジオ旅路
https://tabiji.gallery
渡邊 佑
tabiji.gallery (c) 2020 Yu Watanabe サイトマップ SITEMAP 写真 PHOTO
年度
2024 2023 2022 2021 2020 2019 2018 2017 ~2016
アルバム
傑作選 北海道 石鎚山系 ネパール 屋久島 北アルプス 中央アルプス 離島 巨樹
写真の一覧へ
ストックフォトで作品を探す
JOURNEY
2020年 冬の屋久島歩き旅
2019年 厳冬期北海道徒歩横断
旅の一覧へ
ブログ BLOG
カテゴリ
写真 よもやま話
タグ
スポット 道具 心得
ブログの一覧へ
開発室 DEVELOPMENT
カテゴリ
ASP.NET Core C# WordPress PHP TypeScript JavaScript Web
開発室の一覧へ