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

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

ただいまの
回答率

87.78%

PHPのスクレイピングで幅広い構造のサイトに対応したい

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 1,922

score 9

 前提・実現したいこと

PHPでスクレイピングをしています。アンテナサイトのフィードから配信元のurlを取得し、更にその配信元の記事で使用されている画像を取得しています。
htmlの構造が違うサイトでも確実に画像が取得できるようにしたいです。

 発生している問題・エラーメッセージ

rssであればlinkやtitleといった値はどのサイトにも共通してありますが、DOMで値を指定するタグやidは各サイトによって名前が異なる場合があります。
対応できるようにするためには何が必要でしょうか。

 該当のソースコード

<?php
    $rssdata = simplexml_load_file("http://2chnavi.net/headline/?rss=1");

    // 読み込み件数を決定する
    $num_of_data = 5;

    //出力内容の初期化
    $outdata = "";


    //設定した読み込み件数分だけ取得を繰り返す
    for ($i=0; $i<$num_of_data; $i++){
        $entry = $rssdata->channel->item[$i]; //記事1個取得
        $date = date("Y/m/d", strtotime($entry->pubDate));
        $title = $entry->title; //タイトル取得
        $link = $entry->link; //リンクURL取得

        //出力内容に日付けを入れる
        $outdata .= '<li><a href="' . $link . '" target="_blank">' ;

        //出力内容にリンク付きでタイトルを入れる
        $outdata .= '<span>' . $title . '</span></a>'.' '. $date .'</li>';


        require_once("phpQuery-onefile.php");
        $html = file_get_contents($link);
        $doc = phpQuery::newDocument($html)->find("td")->find(".entry_title:eq(0)")->attr("href");


        $html3 = file_get_contents($doc);

        if( parse_url($doc)['host']=="summary.livedoor.biz" ) $html3 = "<html>".$html3;

        $html2 = phpQuery::newDocument($html3)->find("article")->find(".article-body")->find("img:eq(0)")->attr("src");


        $um .= '<li><img class="um" src="'. $html2 .'">'. '<p class="rt">'. $title . '</p>';      
    }

    echo'<ul class="io">' . $um . '</ul>';  
    ?>
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

+1

記事ごとに OGP タグが設定されているサイトが多いので、<meta property="og:image" content="画像URL" /> から画像URLを取得し、無いものに関しては例外としてセレクターを手作業で入れる方法が確実じゃないかなと思います。

完全自動化したいのなら、OGP イメージが見つからなかった場合に、適当なライブラリなり API なりで HTML の本文抽出を行って、最初に出てくる画像を取得する、とか。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/08/05 08:35

    ありがとうございます。OGPタグを知らなかったので大変勉強になりました。
    条件分岐でカバーしていきたいと思います。

    キャンセル

+1

引数を渡すと、phpQuery::newDocument($html)->find("td")->find(".entry_title:eq(0)")->attr("href");のような構文を実行したのと同じ結果を返してくれる関数があればいいのかなと思います。

概念的には(APIをみてないので)

function Detector($doc /*phpQuery::newDocument($html)*/, $find = ["td",".entry_title:eq(0"], $targetAttr = "href") {
  foreach($find => $sel) {
    $doc = findDoc($doc, $sel);
  }
  return $doc->attr($targetAttr);
}
function findDoc($doc /*phpQuery::newDocument($html)*/, $selector = "td") {
  return $doc->find($selector);
}


とか。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/08/04 08:02

    ありがとうございます。
    記事部分が「articleタグ」でなかったり「.article-body」というクラス名でないサイトもあると思いますが、この関数に渡す引数の値を各サイト毎に取得する処理も必要なんですよね?
    サイトのhtml情報渡せばこの条件で対応できるのでしょうか。

    キャンセル

  • 2018/08/05 01:05

    プログラム的にhtmlの構造を解析することは可能だと思いますが、ページ中のどこに"ユーザが欲しいデータ"があるかは、人間が意味を判断しないといけないので、それを"取得"するというのは変ではないでしょうか(例えば2chのrssのようですが、掲示板のレスが欲しいという局面と、どのような広告が出ているか調べたいという局面と、画像だけ集めたいという局面とでは、欲しいタグは異なります。もちろん「幅広く」を単純に2chのログを色々なまとめブログサイト別に集積したい、という程度にとても狭く考えておられるなら話は違ってくるのかもしれません)

    回答した時点で考えていたのは、設定ファイル(やデータベース)に、urlと追跡すべきDOM構造をあらかじめ調査して設定しておくということです。
    (例えば、{url:"http://2chnavi.net/headline/?rss=1", find:["td",".entry_title:eq(0"]:target:"href"}みたいな設定を別途持っておく。あるいは、このrssからだと、{domain:"livedoor", find = ["article"]}みたいな設定がいいのかもしれません。そこは用途次第です)
    もし、webアプリというなら、urlと探査タグをユーザが入力するフォームインターフェースを入れてもいいかもしれないですが、いずれにしても、どういうurlだったらどういうタグなのか、はプログラムに人間が与えるデータです。

    なので、「この関数に渡す引数の値を各サイト毎に取得する処理」が必要なのではなく「各サイト毎に保存したり読み出したりする処理」が必要だと思います。

    キャンセル

  • 2018/08/05 08:42

    返信ありがとうございます。
    目的の値を自動的に取得するのは無理に近い話ですよね。
    記事内の画像といっても、記事のタグがサイト毎に違ったり画像位置もバラバラですし。
    他サイト「http://ki-topo-ka.com/」見ると2行4列で外部rss画像取得完璧にされていたので、簡単にできるのかと考えましたが甘かったです。
    幅広いサイトに対応するというより、1つ1つのサイトに対応した処理を地道に揃えていくのが現実的ですね。

    キャンセル

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

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

関連した質問

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