写真 コラム 開発室
HOME開発室[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 写真データ販売中! STOCKPHOTO 写真のデータ販売について 写真 PHOTO
湘南ひらつか花火大会
深山唐松
衣笠草
天空の花
車百合
白山石楠花
写真の一覧へ
エリア
ネパール 北海道 屋久島 沖縄 北アルプス 石鎚山系 剣山地 鳥取大山 くじゅう連山 丹沢・大山 富士山
被写体
河川・湖沼 森林 雲・霧 石・岩 雪・氷 生物 植物 街・集落 鉄道 神社 寺院 人物 生活
季節
時間
夕方 マジックアワー
オレンジ・黄 ピンク・紫 茶色 虹色 錦繍
キーワード
石鎚神社
販売
Aflo PIXTA imagemart
タグ
トップ画像 傑作選
コラム PHOTO BLOG
コラムの一覧へ
カテゴリ
撮影 道具
開発室 DEVELOPMENT BLOG
開発室の一覧へ
カテゴリ
ASP.NET Core C# WordPress PHP TypeScript JavaScript
サイトマップ SITEMAP
スタジオ旅路
https://tabiji.gallery
渡邊 佑
tabiji.gallery (c) 2020 Yu Watanabe サイトマップ SITEMAP 写真データ販売中! STOCKPHOTO 写真のデータ販売について 写真 PHOTO
湘南ひらつか花火大会
深山唐松
衣笠草
天空の花
車百合
白山石楠花
写真の一覧へ
エリア
ネパール 北海道 屋久島 沖縄 北アルプス 石鎚山系 剣山地 鳥取大山 くじゅう連山 丹沢・大山 富士山
被写体
河川・湖沼 森林 雲・霧 石・岩 雪・氷 生物 植物 街・集落 鉄道 神社 寺院 人物 生活
季節
時間
夕方 マジックアワー
オレンジ・黄 ピンク・紫 茶色 虹色 錦繍
キーワード
石鎚神社
販売
Aflo PIXTA imagemart
タグ
トップ画像 傑作選
コラム PHOTO BLOG
コラムの一覧へ
カテゴリ
撮影 道具
開発室 DEVELOPMENT BLOG
開発室の一覧へ
カテゴリ
ASP.NET Core C# WordPress PHP TypeScript JavaScript