概要
前回は Laravel Dusk の ChromeDriver を使用して Web スクレイピングしてみましたが、ChromeDriver ですと Chrome ブラウザと ChromeDriver のバージョンをある程度合わせておかないと正常に動作しないなど煩雑です。 Windows などは Chrome ブラウザが自動更新されますのでなおさらです。そこで Chrome DevTool Protocol(CDP) を使用して Web スクレイピングできないか調べてみました。
CDP で代表的なものは本家 Puppeteer や Microsoft の Playwright ですが、いづれも node ライブラリで PHP となると PHP から Node をブリッジして Puppeteer を叩く PuPHPeteer(rialto-php/puphpeteer) ですが、Windows では動作しましたが、残念ながら Linux(CentOS7) では動作しませんでした。
そこでピュア PHP 実装でどちらの環境でも動作する Chrome PHP(chrome-php/chrome) を Laravel11/PHP8.3 環境にて検証してみました。もちろん Laravel でなくても素の PHP でも vendor/autoload.php でロードしてあげれば問題なく動作します。
Laravel プロジェクトの作成
> composer create-project laravel/laravel chrome-php
> cd chrome-php
chrome-php> php artisan -V
Laravel Framework 11.19.0
Chrome PHP のインストール
Composer で chrome-php/chrome をインストールします。
chrome-php> composer require chrome-php/chrome
使い方
Chrome PHP を使い Web スクレイピングしてみます。
Command クラスの雛型を生成します。
chrome-php> php artisan make:command ScrapingCommand
Console command created successfully.
試しにまた Yahoo! Japan トップページの主要ニュースをスクレイピングしてみましょう!
(利用規約に該当する場合は速やかに訂正いたします。見た感じ大丈夫のようですが)
<?php
namespace App\Console\Commands;
use HeadlessChromium\BrowserFactory;
use Illuminate\Console\Command;
class ScrapingCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'command:scraping';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Execute the console command.
*/
public function handle()
{
$targetUrl = "https://www.yahoo.co.jp/";
$browserFactory = new BrowserFactory();
$browserFactory->addOptions([
//'headless' => false,
'noSandbox' => true,
'enableImages' => false,
'sendSyncDefaultTimeout' => 30000,
//'debugLogger' => 'php://stdout'
]);
// starts headless Chrome
$browser = $browserFactory->createBrowser();
try {
// creates a new page
$page = $browser->createPage();
// navigate to an URL
$navigation = $page->navigate($targetUrl);
$navigation->waitForNavigation();
// dom script in the browser
$topics = $page->dom()->querySelectorAll("#tabpanelTopics1 div ul a");
foreach ($topics as $topic) {
$url = $topic->getAttribute("href");
$title = $topic->querySelector('h1 > span')->getText();
var_dump($url . ":" . $title);
}
} catch (Exception $e) {
Log::error($e->getMessage());
} finally {
$browser->close();
}
}
}
Laravel でコマンドを実行をします。
chrome-php> php artisan command:scraping
string(86) "https://news.yahoo.co.jp/pickup/6509848:買い注文殺到 市場の動揺収まらず"
string(86) "https://news.yahoo.co.jp/pickup/6509852:財務省・金融庁・日銀が3者会合へ"
string(86) "https://news.yahoo.co.jp/pickup/6509847:株価乱高下 冷静な判断重要と首相"
string(86) "https://news.yahoo.co.jp/pickup/6509845:蒸し暑さと激しい雷雨 大気不安定"
string(86) "https://news.yahoo.co.jp/pickup/6509851:イラン全土への渡航中止勧告 政府"
string(84) "https://news.yahoo.co.jp/pickup/6509849:排水管から女児の声 気づいた5歳"
string(84) "https://news.yahoo.co.jp/pickup/6509850:Aスイミング日本 抗議で順位変更"
string(86) "https://news.yahoo.co.jp/pickup/6509837:バレー高橋健太郎 代表引退を明言"
おわりに
ピュア PHP な Chrome PHP であるため Windows/Linux 環境でも問題なく動作しました。ただ Windows環境では ChromeDriver と比べ実行時間が長く起動に時間がかかる(引っかかる)印象がありました。
参考程度にですが、以下の動作環境は同一VM環境にての計測になります。
chrome-php> powershell -C Measure-Command { php artisan command:scraping }
Days : 0
Hours : 0
Minutes : 0
Seconds : 11
Milliseconds : 215
Ticks : 112159288
TotalDays : 0.000129813990740741
TotalHours : 0.00311553577777778
TotalMinutes : 0.186932146666667
TotalSeconds : 11.2159288
TotalMilliseconds : 11215.9288
[chrome-php]$ time php artisan command:scraping
real 0m2.338s
user 0m0.415s
sys 0m0.185s
特段速度を要求される環境でなければ Web スクレイピングの実装は Chrome PHP でよいかと思います。実際実務でも Chrome PHP を採用し、現在 Linux 環境にて問題なく動作しています。