LoginSignup
12
7

More than 1 year has passed since last update.

PHPでも動的サイトをスクレイピングしたい夜

Last updated at Posted at 2020-12-29

 はじめに

これはジーズアカデミーAdvent Calendar 2020の記事です!

序章 -俺はスクレイピングやるぜ、PHPで。-

だいたいスクレイピングって言うと、相場はpythonって決まってます。
けど、普段PHPを触っているボクは、PHPでスクレイピングしたいんです。

で、PHPでスクレイピングって言うとだいたいphpQueryというものが言及されがちです。
確かに、jQueryみたいに操作できるのでかんたんです。
けど、これ動的サイトにはうまく利用できないんですね。

だから、他の方法が必要になってきます。

今回の対象。

某海外の買い物サイト(一応名前は伏せます。)
以下の様に
1. 検索
2. 一覧表示(一部だけ)
3. 下にスクロールするとスクロールした分だけ情報を取得する
という動きです。
scraping.gif

最初に、すべての情報が取得されて一覧表示されたらphpQueryでもいけるのですが、
コレだとできません。

対処方法

以下のツール利用で行きます。

  • selenium-server-standalone
    • seleniumはのWebブラウザの操作を自動化してくれるやつ。
  • Selenium Google Chrome Driver
    • seleniumで、Chromを動かせるようにするやつ。
  • facebook-webdriver
    • facebook-webdriverで上記の自動化をPHPを用いてできる様にするやつ

という感じです。

で、今回やることのイメージは、
1. seleniumのブラウザで、スクレイピングしたいサイトにアクセス。
2. 情報を取得したいページに行って、一旦一番下までスクロールさせる(スクロールすれば、全部の情報が表示されている)
3. 一番下までスクロールされた = スクレイピングしたい情報がすべて表示されているので、それをスクレイピング!!!!

なんというハック!

ツールの準備

今回の作業用ディレクトリに移動

$ mkdir selenium
$ cd selenium

selenium-server-standaloneをインストール。

homebrewを利用。

brew install selenium-server-standalone

Selenium Google Chrome Driverをダウンロード

リンク
多分ここにあるかも↓

ダウンロードしたら、seleniumディレクトリにぶち込む。

facebook-webdriverダウンロード

$ php composer.phar require facebook/webdriver

もし、Could not open input file: composer.pharとかの場合は、以下の参考になるかも。

サーバー起動

$ selenium-server -port 4444 &

$ selenium-server -port 4444 &
[1] 33415
C02SY1XFGTFJ:selenium username$ 18:04:14.512 INFO [GridLauncherV3.launch] - Selenium build info: version: '3.11.0', revision: 'e59cfb3'
18:04:14.513 INFO [GridLauncherV3$1.launch] - Launching a standalone Selenium Server on port 4444
2018-05-24 18:04:14.624:INFO::main: Logging initialized @441ms to org.seleniumhq.jetty9.util.log.StdErrLog
18:04:14.882 INFO [SeleniumServer.boot] - Welcome to Selenium for Workgroups....
18:04:14.882 INFO [SeleniumServer.boot] - Selenium Server is up and running on port 4444

で、下のようになればおk

$ jobs
[1]+  Running                 selenium-server -port 4444 &

PHPファイルの用意

試しに、test.phpを用意。

<?php
require './vendor/autoload.php';

use Facebook\WebDriver\Chrome\ChromeOptions;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\WebDriverBy;
use Facebook\WebDriver\WebDriverExpectedCondition;

// 今回の買い物サイト用に準備する変数。
// 検索したいワードを$wordに入れる。この例だとamazonと検索した場合
$word = 'amazon';
$title = " ********** "; // ここはhtmlのtitleに表示される文言を記述。そのサイトによって異なるので、一旦ブラウザでアクセスして確認してください。

// chrome利用用意
$options = new ChromeOptions();
// headlessモードで対応
$options->addArguments(['--headless']);
// ブラウザのサイズを指定
$options->addArguments(["window-size=1024,2048"]);

$host = 'http://localhost:4444/wd/hub';
$capabilities = Facebook\WebDriver\Remote\DesiredCapabilities::chrome();
$capabilities->setCapability(ChromeOptions::CAPABILITY, $options);
$driver = Facebook\WebDriver\Remote\RemoteWebDriver::create($host, $capabilities);
//スクレイピングしたいアドレスを入力
$driver->get('https://www.******/');
$browserLogs = $driver->manage()->getLog('browser');
// 検索したい語をsendKeys()に入れて、submitする。ここの'q'はサイトによって異なる。
$element = $driver->findElement(WebDriverBy::name('q'));
$element->sendKeys($word);
$element->submit();

// このサイトでは、商品画像を全部表示するため、画面を少し右に移動。場合によっては不要。Y軸を動かしたい場合は、第2引数の数字を変える。
$driver->executeScript("window.scrollTo(300, 0);");
$driver->wait(15)->until(
    WebDriverExpectedCondition::titleIs($title)
);

// もし、$title(htmlの<title>が想定どおりじゃない)がなければ、なにかした失敗していると判断
if ($driver->getTitle() !== "$title") {
    throw new Exception('fail');
}

// 以下取得したい要素を指定してあげる。
// 今回は、検索結果のURL、写真、名前を取得したいので以下の用になっている。
// cssSelectorはサイトに寄って異なる。cssSelectorの中身を変えてね。
$itemUrls = $driver->findElements(WebDriverBy::cssSelector('div.c2iYAv > div.cRjKsc > a'));
$photos = $driver->findElements(WebDriverBy::cssSelector('.c5TXIP .c2iYAv .cRjKsc .c1ZEkM'));
$productNames = $driver->findElements(WebDriverBy::cssSelector('.c5TXIP .c3KeDq .c16H9d'));

$items = [];
// 商品あるかチェック
if (count($photos) < 0) {
    throw new Exception('no item.');
}

//。今回は上から10件だけ取得して、情報を$item配列に格納する。
foreach ($itemUrls as $k => $v) {
    if ($k === 10) {
        break;
    }
    $items[$k]['$itemUrl'] = $v->getAttribute('href');
}
foreach ($photos as $k => $v) {
    if ($k === 10) {
        break;
    }
    $items[$k]['photoUrl'] = $v->getAttribute('src');
}
foreach ($productNames as $k => $v) {
    if ($k === 10) {
        break;
    }
    $items[$k]['titleName'] = $v->getText();
}

print_r($items);
echo "\n";

// ↓コメントアウト外せば、どこをスクレイピングしているかとスクリーンショットで確認できる↓
// $file = "sumple_chrome.png";
// $driver->takeScreenshot($file);
$driver->close();

※selenium-webdriverの使い方はこちら。
リンク

かけたら、
$ php test.php
で実行。

いい感じにできました。

スクリーンショット 2020-12-30 0.06.31.png

終了したらkillする。

$ jobs
[1]+  Running                 selenium-server -port 4444 &  (wd: ~/selenium)
$ kill %1
$ jobs
[1]+  Exit 143                selenium-server -port 4444  (wd: ~/selenium)

まとめ

PHPでも動的サイトをスクレイピングできるんだよ!

12
7
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
7