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

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

新規登録して質問してみよう
ただいま回答率
85.50%
PHP

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

Eclipse

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

Q&A

解決済

1回答

2523閲覧

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

agep31

総合スコア29

PHP

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

Eclipse

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

0グッド

0クリップ

投稿2018/06/09 03:46

編集2018/06/09 06:08
//自分が書いたソースコード <?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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

m.ts10806

2018/06/09 04:04

プログラムコード(およびエラーメッセージ)は質問内容としては最も重要な部分であるため、見やすくしていただけると助かります。<code>ボタン押下→「コード」部分にコードを貼り付け→「ここに言語を入力」に対象言語名記入(エラーメッセージの場合は不要)の手順で「コードハイライト化」してください。(質問編集画面ではリアルタイムでプレビューが表示されるので見ながら調整してください)
agep31

2018/06/09 04:14

ご指摘ありがとうございます。手順の通りソースコードを載せました。よろしくお願いします。
m.ts10806

2018/06/09 04:24

bodyの中にただ置かれているのでしょうか?何か他のタグに囲われているとかclassが指定されているとかありませんか?確実に半角数字でしょうか?○~○のような表現ではなく実際の表記に近いhtmlの提示は可能でしょうか
agep31

2018/06/09 04:32

ご指摘ありがとうございます。実際のhtmlを載せました。よろしくお願いします。
m.ts10806

2018/06/09 05:20

htmlもコードなので同じように対応していただけると見やすくなり、助かります。
agep31

2018/06/09 05:27

ご指摘ありがとうございます。htmlもソースコードのほうに載せました。よろしくお願いします。
m.ts10806

2018/06/09 05:29

HTMLは正しく取得できている・・という前提での回答があっても良いですか?
agep31

2018/06/09 05:31

はい。htmlは取得できています。そのhtmlの中から「~」で範囲指定されている値段のみを抽出したいです。
退会済みユーザー

退会済みユーザー

2018/06/09 05:45

htmlが正しく取得できているということはどのように確認しましたか?
agep31

2018/06/09 05:50

print_r($dom)で確認しましたが、抽出できているので問題ないかと思います。
agep31

2018/06/09 06:04

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

退会済みユーザー

2018/06/09 06:04

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

2018/06/09 06:11

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

回答1

0

ベストアンサー

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

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

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

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

php

1<?php 2 3$html = file_get_contents('https://e.his-j.com/shop/tour/voyage.aspx?portal=02A&jarea=10&tour=TI-CYC0838'); 4 5$dom = new DOMDocument; 6@$dom->loadHTML( 7 mb_convert_encoding($html, 'HTML-ENTITIES', 'ASCII,JIS,UTF-8,eucJP-win,SJIS-win'), // 文字化け対応 8 LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD 9); 10$xpath = new DOMXPath($dom); 11$price = $xpath->evaluate('string(//*[@class="price01"])'); 12 13var_dump($price); 14// string(23) "209,800円〜259,800円"

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

php

1list($min, $max) = array_map('intval', explode('〜', str_replace(',', '', $price))); 2 3var_dump($min, $max); 4// int(209800) 5// int(259800)

でどうぞ。

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

php

1<?php 2 3$html = file_get_contents('https://e.his-j.com/shop/tour/voyage.aspx?portal=02A&jarea=10&tour=TI-CYC0838'); 4 5// 文字化け対応とテキスト部分のみの抽出 6$dom = new DOMDocument; 7@$dom->loadHTML( 8 mb_convert_encoding($html, 'HTML-ENTITIES', 'ASCII,JIS,UTF-8,eucJP-win,SJIS-win'), 9 LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD 10); 11$text = $dom->documentElement->nodeValue; 12 13if (preg_match('/([\d,]+)\s*円\s*~\s*([\d,]+)\s*円/', $text, $match)) { 14 list(, $min, $max) = array_map('intval', str_replace(',', '', $match)); 15 var_dump($min, $max); 16 // int(209800) 17 // int(259800) 18}

投稿2018/06/09 06:08

編集2018/06/09 08:01
mpyw

総合スコア5223

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

agep31

2018/06/09 06:20

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

2018/06/09 06:27

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

2018/06/09 06:31

例えばサイトによっては左側にある検索メニューに ☐ 10,000円〜20,000円 ☑ 20,000円〜30,000円 のような,ページで表示している情報とは直接無関係な絞り込み条件入力欄があったりしますよね?こういうノイズを拾ってしまうリスクはないか?というところです。 今回は @class="price01" という条件で絞り込みましたが,サイト内を見た感じ他に同様の属性が全く見当たらなかったため,一意性を担保するためには十分な情報量である,と判断したまでですね。このあたりは慣れないと判断しづらいかもしれません。
mpyw

2018/06/09 06:34

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

2018/06/09 07:33

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

2018/06/09 07:36

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

2018/06/09 07:41

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

2018/06/09 08:02

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

2018/06/10 05:00

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

2018/06/10 05:07

0番目をスキップするためです。 0番目…全体 1番目…ひとつめのカッコ 2番目…ふたつめのカッコ
agep31

2018/06/10 05:08

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問