koulab

技術系のメモ

ChromeDriverを使ってPHPでヘッドレスで安定にスクレイピングする

この記事のゴール:Google検索をして最初の検索結果のページをクリックしてアクセスを自動化する

環境構築

以下のライブラリを使用します。

https://github.com/php-webdriver/php-webdriver

composerパッケージ名はphp-webdriver/webdriverです。

※facebook/webdriverは非推奨(deprecated)となっており、今後はアップデートは行われません

https://github.com/php-webdriver/php-webdriver#upgrade-from-version-180

composer require php-webdriver/webdriver

ChromeDriverのダウンロード

以下から現在使用しているChromeのバージョンchromedriverをダウンロードしてください。 https://sites.google.com/a/chromium.org/chromedriver/downloads

chrome80.PNG

Windowsの場合
ダウンロードしたchromedriver.exeをPowerShellなどで以下のコマンドを実行

.\chromedriver.exe --port=4444
chromedriver --port=4444

Linux/Macの場合
chmod +x chromedriver
./chromedriver --port=4444

ソース

<?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;

$host = 'http://127.0.0.1:4444';
$driver = null;

try {
    $options = new ChromeOptions();
    $options->addArguments([
        '--headless',
        '--window-size=1920,1080',
        '--ignore-certificate-errors',
        '--disable-popup-blocking',
        '--disable-web-security',
        '--disable-javascript',
        '--start-maximized',
        '--incognito',
        '--no-sandbox',
        '--disable-infobars',
        '--disable-dev-shm-usage',
        '--disable-browser-side-navigation',
        '--disable-gpu',
        '--disable-features=VizDisplayCompositor'
    ]);

    $options->setExperimentalOption('excludeSwitches', ['enable-automation']);
    $caps = DesiredCapabilities::chrome();

    $caps->setCapability('pageLoadStrategy', 'none');
    $caps->setCapability(ChromeOptions::CAPABILITY, $options);
    $driver = RemoteWebDriver::create($host, $caps);
    $driver->get('http://google.co.jp');

    $driver->wait(10, 500)->until(
        WebDriverExpectedCondition::visibilityOfElementLocated(WebDriverBy::cssSelector("form input[type=text]"))
    );

    $driver
        ->findElement(WebDriverBy::name('q'))
        ->sendKeys('ブラックハットSEO 方法')
        ->submit();

    //フッター要素が出るまで待機する
    $driver->wait(10, 500)->until(
        WebDriverExpectedCondition::elementToBeClickable(WebDriverBy::id("foot"))
    );
    $searchResults = $driver->findElements(WebDriverBy::cssSelector("#search .srg .g"));
    foreach ($searchResults as $remoteWebElement){
        //検索結果のサイトのURLたち
        $href = $remoteWebElement->findElement(WebDriverBy::cssSelector('.r a'))->getAttribute("href");
        var_dump($href);
    }
    //ページのHTMLソース
    $source = $driver->getPageSource();
    file_put_contents("google_search_result.html",$source);
    $driver->takeScreenshot('google_search.png');

    //最初の検索結果1件をクリック
    $driver->findElement(WebDriverBy::cssSelector("#search h3"))->click();
    $driver->wait(10, 500)->until(
        WebDriverExpectedCondition::visibilityOfElementLocated(WebDriverBy::tagName("body"))
    );
    $driver->takeScreenshot('google_search_first_page.png');

}catch (\Facebook\WebDriver\Exception\WebDriverException $e){
    var_dump($e);
}

try{
    //確実に終了する
    if($driver != null && $driver instanceof RemoteWebDriver){
        $driver->close();
    }
}catch (\Facebook\WebDriver\Exception\WebDriverException $e){
    var_dump($e);
}

ハマりポイント

pageLoadStrategyを指定しない場合でも取得できるが、取得できないページが出てくる。(タイムアウトする)ので、pageLoadStrategyをnoneにしてWebDriverExpectedConditionで表示されているべき要素を指定することでタイムアウト回避できる。

SEO系な企業はこういうアレなことやってんだろうなーと思いつつ書いてた。 他のサンプルとかはgithubに挙げました

https://github.com/39ff/php-chromedriver-headless-scrape-example

JSの挙動も考慮するの難しいですね。

参考 https://qiita.com/Rasukarusan/items/0ca204d5b0f0fb876252

よさそう→ https://github.com/shimabox/screru