23024


PHP 5.3のWebスクレーパーとして`Goutte 1.0.6`を使っています。

一度訪問したサイトのHTMLをファイルキャッシュなどに格納しておき、2回目からはファイルキャッシュからHTMLを文字列として変数に読み込み、その内容をスクレイピングするような動作をさせています。

[code]

    $client = new Goutte\Client;
    $is_cached = false;
    if ($cache_data = $cache->get($url)) {
      $crawler = $client->request('GET', '');
      $crawler->clear();
      $crawler->addHtmlContent($cache_data, 'cp932');
      $is_cached = true;
    }
    else {
      $crawler = $client->request('GET', $url);
      $status = $client->getResponse()->getStatus();
      if (($status != 200) && ($status != 304 )) {
        return array(status => $status);
      }
    }
    
[/code]

のようなコードにしていますが、キャッシュがヒットした場合、空の`GET`をさせることで`Crawler`オブジェクトを得て、いったんスクレイプ対象のHTMLを`$crawler->clear()`で捨ててから改めて`$crawler->addHtmlContent($cache_data,
'cp932');`でHTMLを食べさせるというアドホックなコードになっています。

実際には無駄なHTTPリクエストが発生しておりますので、改善できないかと考えています。

`Goutte`を使ったスクレイピングで、HTTPリクエストなしでスクレイピングできる`crawler`オブジェクトを生成させる方法をご存知の方は、ご教示いただけないでしょうか。

コード全文：  
<https://github.com/CLCL/akizukidenshi-ogp-
injector/blob/8c0869e411ac9898fd8e9cba9bfebb4d93a22314/index.php>

* * *

（投稿後すぐに自己解決したので一時ここに解決法を載せていましたが、回答の方に転記してこちらの記述を削除しました。）


CC BY-SA 3.0
2015-10-07T11:11:32.387
17366
2016-06-06T07:30:02.120
2015-10-07T11:38:44.427
12633
12633
question
php
GoutteでHTTPリクエストなしに文字列からスクレイプする方法


自己解決しました。

[code]

    if ($cache_data = $cache->get($url)) {
      //$crawler = $client->request('GET', '');
      //$crawler->clear();
      $crawler = $client->getClient();
      $crawler->addHtmlContent($cache_data, 'cp932');
      $is_cached = true;
    }
    
[/code]

で解決しました。

解決方法の見つけ方として、

GoutteのGitHub

<https://github.com/FriendsOfPHP/Goutte/blob/master/Goutte/Client.php>

を参照したところ、

[code]

    public function getClient()
    {
        if (!$this->client) {
            $this->client = new GuzzleClient(array('defaults' => array('allow_redirects' => false, 'cookies' => true)));
        }
        return $this->client;
    }
    
[/code]

という箇所があり、これで空の`Crawler`オブジェクトを生成しているようだったので、試してみたところ、うまく動作しました。


CC BY-SA 3.0
2015-10-07T11:34:26.370
17370
2015-10-07T11:34:26.370
12633
17366
answer


「自己解決した」としましたが、その後、間違っていることがわかり、下記コードに修正しました。

今回は、ちゃんと動かせるテストコードを提示します。

[code]

    <?php
    require_once './vendor/autoload.php';
    
    // モジュールインストール
    // php composer.phar require fabpot/goutte:~1.0 pear/Cache_Lite:~1.7
    
    $cache_dir = __DIR__ . '/cache_cache_lite/';
    @mkdir($cache_dir, 0775);
    
    $cache  = new Cache_Lite( array (
      'cacheDir' => $cache_dir,
      'lifeTime' => 8,// 8秒間キャッシュ有効
      'automaticCleaningFactor' => 20, 
      'hashedDirectoryLevel'    => 1,
      'hashedDirectoryUmask'    => 02775,
    ));
    
    $url='http://akizukidenshi-ogp-injector.dtpwiki.jp/';
    
    $client = new Goutte\Client;
    $is_cached = false;
    if ($cache_data = $cache->get($url)) {
      echo "cache zumi\n";
    
      // a. 質問文での手法 無駄なGETリクエストが走るがHTMLをスクレイプできるオブジェクトができる
      //$crawler = $client->request('GET', '');
      //$crawler->clear();
      //$crawler->addHtmlContent($cache_data, 'cp932');
    
      // b. 一度解決したとした時の手法 実際にできるオブジェクトはHTMLをスクレイプできない
      //$crawler = $client->getClient();
      //$crawler->addHtmlContent($cache_data, 'cp932');
    
      // c. 今回の手法    
      $crawler = $client->request('HEAD', null);
      $crawler->clear();
      $crawler->addHtmlContent($cache_data, 'cp932');
    
      $is_cached = true;
    }
    else {
      echo "torini iku\n";
      $crawler = $client->request('GET', $url);
      $status = $client->getResponse()->getStatus();
      if (($status != 200) && ($status != 304 )) {
        return array(status => $status);
      }
    }
    
    if (!$is_cached) {
      $cache->save($client->getResponse()->getContent(), $url);
    }
    
[/code]

一度「解決した」として提示したb.のコードでは、実際にはオブジェクトに`addHtmlContent`は生えておらず、エラーとなります。

今回のc.のコードでは、どうしてもHTTPリクエストを発生させずにHTMLスクレイピングができる`clawler`オブジェクトの生成はできなかったため、最初質問で提示したものから、GETリクエストをHEADリクエストに変更することで、`localhost`にはアクセスするが最小の負荷になるようにしました。

この場合、稼働している自サーバのport 80/TCPのドキュメントルートにアクセスが来ることになり、アクセスログには、

[code]

    ::1 - - [11/Mar/2016:15:51:21 +0900] "HEAD / HTTP/1.1" 403 - "-" "Symfony2 BrowserKit"
    
[/code]

が記録されることになります。

しっくりこない結果ですが、現状のベストとしてこのコードを上げさせていただきます。


CC BY-SA 3.0
2016-03-11T07:32:05.497
23024
2016-03-11T07:32:05.497
12633
17366
answer

[code]

    use Symfony\Component\DomCrawler\Crawler;
    $crawler = new Crawler($html);
    
[/code]

これでいけると思います。


CC BY-SA 3.0
2016-06-06T04:29:11.637
26494
2016-06-06T07:30:02.120
2016-06-06T07:30:02.120
5008
16770
17366
answer