質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.33%

  • PHP

    21341questions

    PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

  • Eclipse

    1743questions

    Eclipseは、IBM社で開発された統合開発環境のひとつです。2001年11月にオープンソース化されました。 たくさんのプラグインがあり自由に機能を追加をすることができるため、開発ツールにおける共通プラットフォームとして位置づけられています。 Eclipse自体は、Javaで実装されています。

preg_match_all関数を使ってhtmlの中の指定した文字列を取り出したい

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 344

agep31

score 21

//自分が書いたソースコード
<?php
//htmlの取得
$url = "https://e.his-j.com/shop/tour/voyage.aspx?portal=02A&jarea=10&tour=TI-CYC0838"; //取得したいサイトのURL
require_once("./phpQuery-onefile.php");
$html = file_get_contents($url);
$dom = phpQuery::newDocument($html);

//料金の取得
echo "<br>料金:";
preg_match_all("/([0-9]*.[0-9]*)円~([0-9]*.[0-9]*)円/", $dom->text(), $matches_price);
print_r($matches_price);
echo "<br>";
?>

//取得したいhtmlの中身
<html>
<head>
・・・
</head>
<body>
・・・
<div class="infoArea02Wrap clearfix">
        <div class="infoArea02L">
            <div class="infoArea02Top">
                <dl class="clearfix">
                    <dt>旅行代金</dt>
                    <dd>
                        <p class="price01"><em><strong>209,800円~259,800円</strong></em></p>
                        <p class="price02">/大人1名様(2.3名1室利用) <span class="surcharge">(燃油サーチャージ別)</span></p>
                    </dd>
                </dl>
                <script src="//e.his-j.com/static/02A/impresso/js/detail/fuel_another.js"></script>
<span style="color:#FF0000; font-size:12px;">※8/19・8/31出発のみスーパーサマーセールの対象日です。それ以外の出発日は既に販売中です。</span>
            </div>
・・・
</body>
</html>

 前提・実現したいこと

htmlの中にある指定した要素を取り出したいです。

preg_match_all関数を使ってこのようなhtmlの中から、209,800円~259,800円の数値のみを取得したいです。

 試したこと

以上のようなソースコードを作ってみたのですが、$matches_priceの配列に何も要素が入っていなく困っています。出力された結果は以下のようになりました。

料金:Array ( [0] => Array ( ) [1] => Array ( ) [2] => Array ( ) ) 

 補足情報(FW/ツールのバージョンなど)

php5.6.34

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • agep31

    2018/06/09 15:04

    検索パターンを、preg_match_all("/([0-9]*.[0-9]*)円./"として抽出したところ、209,800円�となり「~」の部分が文字化けしてしまっているのですが、何か関係ありますでしょうか?

    キャンセル

  • 退会済みユーザー

    退会済みユーザー

    2018/06/09 15:04

    よっぱらってるので内容見てないけど、<head/>タグと<body/>タグが破綻してるのは直しといてくれるかな?

    キャンセル

  • agep31

    2018/06/09 15:11

    ご指摘ありがとうございます。修正しました。

    キャンセル

回答 1

checkベストアンサー

+4

PHPネイティブのDOMによるスクレイピング入門 - Qiita

「HTMLタグを削ぎ落とすためだけにサードパーティライブラリを使って,情報量が削ぎ落とされた中から正規表現で抜き出す」というのはアプローチとして非常にナンセンスです。

  • $dom->text() を呼ぶためだけに phpQuery を入れる意味はありません。 phpQuery は本来PHPネイティブの DOMXPath のような機能を実現するためのものです。
  • (CSSクエリではなくXPathクエリを理解できるのであれば) phpQuery よりも DOMXPath のほうが速い上に余分なコードを書かなくていいので優れています。
  • HTMLのクラス属性などはスクレイピングのための貴重な情報源なのに,削ぎ落としてしまってテキストしか残っていない状態から正規表現で探し出す,というのは本末転倒です。

今回の目的であれば DOMXPath が適任です。

<?php

$html = file_get_contents('https://e.his-j.com/shop/tour/voyage.aspx?portal=02A&jarea=10&tour=TI-CYC0838');

$dom = new DOMDocument;
@$dom->loadHTML(
    mb_convert_encoding($html, 'HTML-ENTITIES', 'ASCII,JIS,UTF-8,eucJP-win,SJIS-win'), // 文字化け対応
    LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD
);
$xpath = new DOMXPath($dom);
$price = $xpath->evaluate('string(//*[@class="price01"])');

var_dump($price);
// string(23) "209,800円〜259,800円"

これだけ。サードパーティライブラリは一切不要です。もし価格レンジを数値で取得したいのであれば更に

list($min, $max) = array_map('intval', explode('〜', str_replace(',', '', $price)));

var_dump($min, $max);
// int(209800)
// int(259800)

でどうぞ。

 (追記) 正規表現で対応する場合

<?php

$html = file_get_contents('https://e.his-j.com/shop/tour/voyage.aspx?portal=02A&jarea=10&tour=TI-CYC0838');

// 文字化け対応とテキスト部分のみの抽出
$dom = new DOMDocument;
@$dom->loadHTML(
    mb_convert_encoding($html, 'HTML-ENTITIES', 'ASCII,JIS,UTF-8,eucJP-win,SJIS-win'), 
    LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD
);
$text = $dom->documentElement->nodeValue;

if (preg_match('/([\d,]+)\s*円\s*~\s*([\d,]+)\s*円/', $text, $match)) {
    list(, $min, $max) = array_map('intval', str_replace(',', '', $match));
    var_dump($min, $max);
    // int(209800)
    // int(259800)
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/06/09 15:20

    ご回答ありがとうございます。ご提示していただいたコードで実行したところ、数値を取得することができました。このサイトのみの抽出であれば、この結果でよいのですが、ほかのサイトのhtmlからも「円~円」で範囲指定されている値段を取得したいのですが、どうすればよいのでしょうか?後述となって申し訳ありません。

    キャンセル

  • 2018/06/09 15:27

    基本的にはサイトごとにHTMLの構造は違うので,サイトごとに ->evaluate() の部分を適切に書く必要があります。

    「最初に出現する /[\d,]+円〜[\d,]+円/ にマッチするテキスト」 という正規表現で必ず意図したものが取得できる保証がある,もしくは必ず正しいという保証がなくてもいいからあっている可能性が高いものを抜き出したい。スクレイピング対象サイトが多くていちいち1個1個対応するためのコードを書いていられない。
    → strip_tags ですべての HTML をドロップしてから正規表現で抽出

    できる限り正確に抽出したい
    → サイトごとに ->evaluate() の部分を適切に書く

    キャンセル

  • 2018/06/09 15:31

    例えばサイトによっては左側にある検索メニューに

    ☐ 10,000円〜20,000円
    ☑ 20,000円〜30,000円

    のような,ページで表示している情報とは直接無関係な絞り込み条件入力欄があったりしますよね?こういうノイズを拾ってしまうリスクはないか?というところです。

    今回は @class="price01" という条件で絞り込みましたが,サイト内を見た感じ他に同様の属性が全く見当たらなかったため,一意性を担保するためには十分な情報量である,と判断したまでですね。このあたりは慣れないと判断しづらいかもしれません。

    キャンセル

  • 2018/06/09 15:34

    また商品情報などに任意の文字列を入力できる場合も問題です。もしサイトが形式的に出力する価格とは別に,ユーザが自由入力欄に書いた価格に関するテキストが存在し,サイトが形式的に出力している価格よりもHTML定義順序で前にきてしまっていたら,preg_matchはそちらを抽出してしまいます。

    HTMLタグは通常ユーザは自由に入力できないことが多いので,HTMLタグの属性などから判断するほうが正確なのです。

    キャンセル

  • 2018/06/09 16:33

    一つ一つのサイトに対応するのは困難なので、正規表現を使って抽出したいと考えています。strip_tags($html)として抽出してみたのですが、209,800�~�`259,800�~と文字化けが起こってしまい、困っています。

    キャンセル

  • 2018/06/09 16:36

    php.iniファイルのdefault_charsetはutf-8に指定しています。また、phpファイルの最初でも<meta>タグの中でutf-8にしているのですが、文字化けが起こってしまいます。

    キャンセル

  • 2018/06/09 16:41

    あっごめんなさい,strip_tags は要らなかったですね,すいません
    少々回答修正します

    キャンセル

  • 2018/06/09 17:02

    strip_tags を使うとタグの中身まで全部削ぎ落とされてしまうので,DOMDocument でテキストのみを抽出する方法を使いました。

    キャンセル

  • 2018/06/10 14:00

    ありがとうございます。とても理想の形になりました。最後に一つ質問なのですが、list(, $min, $max)のところで、最初に","を入れるのはなぜなのでしょうか?

    キャンセル

  • 2018/06/10 14:07

    0番目をスキップするためです。

    0番目…全体
    1番目…ひとつめのカッコ
    2番目…ふたつめのカッコ

    キャンセル

  • 2018/06/10 14:08

    わかりました。ありがとうございます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.33%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • PHP

    21341questions

    PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

  • Eclipse

    1743questions

    Eclipseは、IBM社で開発された統合開発環境のひとつです。2001年11月にオープンソース化されました。 たくさんのプラグインがあり自由に機能を追加をすることができるため、開発ツールにおける共通プラットフォームとして位置づけられています。 Eclipse自体は、Javaで実装されています。