システム開発 - Chrome PHP 覚書

 

参照:chrome-php / headless-chromium-php

※インストール方法などは他サイトなどを参考にしてください。

特徴

  • PHPからChromeまたはChromeブラウザを開くことができる
  • ページを作成し、ページへ移動することができる
  • スクリーンショットを撮ることができる
  • ページ内のJavaScriptの実行結果を評価できる
  • PDFを作成することができる
  • マウスをエミュレートできる
  • キーボードをエミュレートできる
  • 常にIDEフレンドリーである

基本的な使用方法

シンプルかつ理解しやすいAPIにより、Chromeの起動、ページを開く、スクリーンショットを撮る、Webサイトをクロールするなど、Chrome上で人ができることのほとんどを行うことができます。

use HeadlessChromium\BrowserFactory;

$browserFactory = new BrowserFactory();

// headless chromeの開始
$browser = $browserFactory->createBrowser();

try {
    // 新しいページを作成し、指定のURLへ移動する
    $page = $browser->createPage();
    $page->navigate('http://example.com')->waitForNavigation();

    // ページタイトルの取得
    $pageTitle = $page->evaluate('document.title')->getReturnValue();

    // スクリーンショットを撮る
    $page->screenshot()->saveToFile('/foo/bar.png');

    // PDFの作成
    $page->pdf(['printBackground' => false])->saveToFile('/foo/bar.pdf');
} finally {
    // headless chromeの終了
    $browser->close();
}

別のChrome実行ファイルを指定する

ファクトリを開始すると、chromeの実行ファイルとして環境変数"CHROME_PATH"を探します。変数が見つからない場合には、実行ファイルとして"chrome"

(デフォルト)が使用されます。

任意の実行ファイルを指定できます。例では、"chromium-browser"を指定しています。

use HeadlessChromium\BrowserFactory;

// デフォルトの'chrome'から'chromium-browser'に変更
$browserFactory = new BrowserFactory('chromium-browser');

デバッグ

下記の例では、デバッグを容易にするために、ヘッドレスモードを無効にしています。

use HeadlessChromium\BrowserFactory;

$browserFactory = new BrowserFactory();

$browser = $browserFactory->createBrowser([
    'headless' => false, // headlessモードを無効にする
]);

他のデバッグオプション

[
    // chromeに送信される各命令の間に0.8秒の遅延を追加します
    // add 0.8 second of delay between each instruction sent to chrome,
    'connectionDelay' => 0.8,
    // 冗長モードを有効にします
    'debugLogger'     => 'php://stdout',
]
'debugLogger'は、Psr\Log(monologapix/logなど)のLoggerInterfaceを実装するリソース文字列、リソース、またはオブジェクトを指定できます。

API

Browser Factory

use HeadlessChromium\BrowserFactory;

$browserFactory = new BrowserFactory();
$browser = $browserFactory->createBrowser([
    'windowSize'      => [1920, 1000],
    'enableImages'    => false,
]);

オプション

BrowserFactoryで使用できるオプションは次の通りです。

Option nameDefaultDescription
connectionDelay0デバッグを目的とした各操作間に適用する遅延時間
customFlagsnoneコマンドラインに渡すフラグの配列。例)['--option1', '--option2=someValue']
debugLoggernullデバッグ用メッセージを出力するための文字列(「php:// stdout」など)、リソース、またはPSR-3ロガーのインスタンス
enableImagestrue画像の読み込み
headlesstrueヘッドレスモード
ignoreCertificateErrorsfalseSSLエラーを無視するかどうか
keepAlivefalseスクリプトの終了時にchromeインスタンスを維持する場合にはtrue
noSandboxfalseDockerコンテナを実行する場合に最適
sendSyncDefaultTimeout5000同期メッセージを送信するためのデフォルトのタイムアウト時間(ミリ秒)
startupTimeout30Chromeが起動するのに待機する最大時間(秒)
userAgentnoneブラウザで使用するユーザーエージェントを指定(置換方法はapiページを参照)
userDataDirnoneChromeユーザのデータディレクトリ(デフォルト:新しい空のディレクトリが一時的に生成されます)
windowSizenoneウインドウサイズ。使用法:$width, $height - Page::setViewportも参照

ブラウザAPI

新しいページの作成(タブ)

$page = $browser->createPage();

// 目的のページを指定
$uri = 'http://example.com';
$page = $browser->createPage($uri);

ブラウザを閉じる

$browser->close();

ブラウザで作成された全ページで遷移する前に実行されるスクリプトの設定

$browser->setPagePreScript('// ナビゲーターとしての権限を装う
const originalQuery = window.navigator.permissions.query;
window.navigator.permissions.query = (parameters) => (
    parameters.name === 'notifications' ?
        Promise.resolve({ state: Notification.permission }) :
        originalQuery(parameters)
);');

Page API

URLに移動

// ページ遷移
$navigation = $page->navigate('http://example.com');

// ページが読み込まれるのを待つ
$navigation->waitForNavigation();

$navigation->waitForNavigation()を使用する場合、ページイベント の"loaded"が発動するまで30秒待機します。待ち受けるタイムアウト時間やイベントは変更できます。

// "DOMContentLoaded"イベントが発動するまで10秒待つ
$navigation->waitForNavigation(Page::DOM_CONTENT_LOADED, 10000);

利用可能なイベント(発動順):

  • Page::DOM_CONTENT_LOADED:domが完全に読み込まれる
  • Page::LOAD:(デフォルト)ページとすべてのリソースが読み込まれる
  • Page::NETWORK_IDLE:ページが読み込まれ、少なくとも500ミリ秒間ネットワーク上の通信が発生していない

ページを移動するのを待ちたい場合、発生する可能性のある2つの主な問題があります。一つは、ページが長すぎて読み込み終わらず、次に読み込まれたページで置き換えられてしまうことです。良いニュースは、古き良きtry catchを使用してこれらの問題を処理できることです。

use HeadlessChromium\Exception\OperationTimedOut;
use HeadlessChromium\Exception\NavigationExpired;

try {
    $navigation->waitForNavigation()
} catch (OperationTimedOut $e) {
    // 読み込むのに長すぎた場合
} catch (NavigationExpired $e) {
    // 他のページが読み込まれた場合
}

ページ上のスクリプトの評価

ページ遷移が完了すると、そのページで任意のスクリプトを評価できます。

// ページ遷移
$navigation = $page->navigate('http://example.com');
    
// ページが読み込まれるのを待つ
$navigation->waitForNavigation();

// ブラウザでスクリプトを評価
$evaluation = $page->evaluate('document.documentElement.innerHTML');

// 値が返るのを待って値を取得
$value = $evaluation->getReturnValue();

評価するスクリプトがリンクをクリックしたり、フォームを送信したりする場合があります。この場合、ページは再読み込みされるので、新しいページが読み込まれるのを待つ必要があります。

$page->evaluate('ページを再読み込みするjs')->waitForPageReload()を使用することで実現可能です。例はform-submit.phpで入手できます。

関数を呼び出す

この方法は、ページのコンテキスト内で指定された引数を使用して、指定された関数を呼び出すことができるevaluateの代替手段です。

$evaluation = $page->callFunction(
    "function(a, b) {\n    window.foo = a + b;\n}", 
    [1, 2]
);

$value = $evaluation->getReturnValue();

scriptタグの追加

下記は、jQuery(またはその他のもの)をページに追加する場合に使用します。

$page->addScriptTag([
    'content' => file_get_contents('path/to/jquery.js')
])->waitForResponse();

$page->evaluate('$(".my.element").html()');

src属性にURLを設定することも可能です。

$page->addScriptTag([
    'url' => 'https://code.jquery.com/jquery-3.3.1.min.js'
])->waitForResponse();

$page->evaluate('$(".my.element").html()');

ページのHTMLを取得

getHtmlメソッドを使用して、ページのHTMLを文字列として取得できます。

$html = $page->getHtml();

ページナビゲーション時に評価するスクリプトの追加

$page->addPreScript('// ナビゲーターとしての権限を装う
const originalQuery = window.navigator.permissions.query;
window.navigator.permissions.query = (parameters) => (
    parameters.name === 'notifications' ?
        Promise.resolve({ state: Notification.permission }) :
        originalQuery(parameters)
);');

スクリプトを実行する前にDOMを完全に構成させる必要がある場合は、オプション「onLoad」を使用します。

$page->addPreScript($script, ['onLoad' => true]);

ビューポートサイズの設定

この機能は、ブラウザの他のページの大きさに影響を与えずに、現在のページのビューポート(エミュレーション)のサイズを変更することができます(BrowserFactory::createBrowserの"windowSize"のオプションも参照)。

$width = 600;
$height = 300;
$page->setViewport($width, $height)
    ->await(); // 設定の完了を待つ

スクリーンショットの作成

// ページ遷移
$navigation = $page->navigate('http://example.com');
    
// ページが読み込まれるのを待つ
$navigation->waitForNavigation();

// スクリーンショットを撮る
$screenshot = $page->screenshot([
    // デフォルト:'png' / 他:'png', 'jpeg',
    'format'  => 'jpeg',
    // 'jpeg'の場合 / デフォルト:100 
    'quality' => 80,
]);

// スクリーンショットの保存
$screenshot->saveToFile('/some/place/file.jpg');

エリアの選択

スクリーンショットの領域を選択するために、"clip"オプションを使用します(例 TODO)

ページ全体のスクリーンショットを撮る

$page->getFullPageClipを使用して、完全なレイアウト(レイアウトだけでなく)のスクリーンショットを撮ることもできます(例 TODO)。

TODO Page.getFullPageClip();

use HeadlessChromium\Clip;

// ページ遷移
$navigation = $page->navigate('http://example.com');
    
// ページが読み込まれるのを待つ
$navigation->waitForNavigation();

// left及びcenterの座標+幅及び高さを指定して長方形を作成
// create a rectangle by specifying to left corner coordinates + width and height
$x = 10;
$y = 10;
$width = 100;
$height = 100;
$clip = new Clip($x, $y, $width, $height);

// スクリーンショットを撮る(メモリバイナリ内)
$screenshot = $page->screenshot([
    'clip'  => $clip,
]);

// スクリーンショットを保存
$screenshot->saveToFile('/some/place/file.jpg');

PDFの作成

// ページ遷移
$navigation = $page->navigate('http://example.com');
    
// ページが読み込まれるのを待つ
$navigation->waitForNavigation();

$options = [
    // デフォルト:false
    'landscape' => true,
    'printBackground' => true,
    'displayHeaderFooter' => true,
    // デフォルト:false(@pageから直接パラメータを読み取る)
    'preferCSSPageSize' => true,
    // デフォルト:~0.4(浮動小数点、値はインチ)
    'marginTop' => 0.0,
    'marginBottom' => 1.4,
    'marginLeft' => 5.0,
    'marginRight' => 1.0,
    // デフォルト:8.5(浮動小数点、値はインチ)
    'paperWidth' => 6.0,
    'paperHeight' => 6.0,
    // 詳細は下記参照
    'headerTemplate' => '<div>foo</div>',
    'footerTemplate' => '<div>foo</div>',
    // デフォルト:1.0(浮動小数点)
    'scale' => 1.2,
];

// PDFの作成(メモリバイナリ内)
$pdf = $page->pdf($options);

// PDFの保存
$pdf->saveToFile('/some/place/file.pdf');

// 又は保存せずに直接出力
header('Content-Description: File Transfer');
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename=filename.pdf');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');

echo base64_decode($pdf->getBase64());

オプションのheaderTempaltefooterTempalteは、値を挿入するために使用される次のクラスを持つ有効なHTMLの記述が必要です。

  • date:フォーマットされた制作日
  • title:ドキュメントのタイトル
  • url:ドキュメントの場所
  • pageNumber:現在のページ番号
  • totalPages:ドキュメントの合計ページ

マウスAPI

マウスAPIは、ページインスタンスに依存しており、マウスの移動、クリック、スクロールを制御できます。

$page->mouse()
    // マウスの位置をx=10/y=20に移動
    ->move(10, 20)
    // 上記位置に移動し、左クリック
    ->click()
    // 5pxずつマウスをx=100/y=200に移動
    ->move(100, 200, ['steps' => 5])
    // 上記位置に移動し、右クリック
    ->click(['button' => Mouse::BUTTON_RIGHT];

// 最後のクリックがリンク上であった場合
// リンクのクリック後、ページが読み込まれるのを待つ
$page->waitForReload();

マウスホイールをエミュレートして、ページ、フレーム、または要素を上下にスクロールできます。

$page->mouse()
    // 下方向に100pxスクロール
    ->scrollDown(100)
    // 上方向に50pxスクロール
    ->scrollUp(50);

キーボードAPI

キーボードAPIは、ページインスタンスに依存しており、実際のユーザーのように入力できます。

$page->keyboard()
    // 「Tab」のような実際のキーを入力
    ->typeRawKey('Tab')
    // テキスト"bar"の入力
    ->typeText('bar');

実際のユーザーになりすますには、setKeyIntervalメソッドを使用して、各キーストロークの間に遅延を追加します。

// キーストロークに10ミリ秒の遅延を設定
$page->keyboard()->setKeyInterval(10);

Cookie API

ページのCookieを設定および取得できます。

クッキーの設定

use HeadlessChromium\Cookies\Cookie;

$page = $browser->createPage();

// 例1)特定のドメインにクッキーを設定

$page->setCookies([
    Cookie::create('name', 'value', [
        'domain' => 'example.com',
        // 期限1日
        'expires' => time() + 3600
    ])
])->await();


// 例2)現在のページにクッキーを設定

$page->navigate('http://example.com')->waitForNavigation();

$page->setCookies([
    Cookie::create('name', 'value', ['expires'])
])->await();

クッキーの取得

use HeadlessChromium\Cookies\Cookie;

$page = $browser->createPage();

// 例1)ブラウザの全てのクッキーを取得

$cookies = $page->getAllCookies();

// 例2)現在のページのクッキーを取得

$page->navigate('http://example.com')->waitForNavigation();
$cookies = $page->getCookies();

// name == 'foo'のクッキーにフィルタリング
$cookiesFoo = $cookies->filterBy('name', 'foo'); 

// name == 'bar'のクッキーを探す
$cookieBar = $cookies->findOneBy('name', 'bar');
if ($cookieBar) {
    // do something
}

ユーザーエージェントの設定

ページごとにユーザーエージェントを設定できます。

$page->setUserAgent('my user agent');

ブラウザ全体でuserAgentを設定するには、BrowserFactoryのオプションも参照してください。

高度な使用法

ライブラリには、すべての通信ロジックを非表示にするツールが付属していますが、内部で使用されるツールを使用して、Chromeデバッグプロトコルと直接通信できます。

例:

use HeadlessChromium\Communication\Connection;
use HeadlessChromium\Communication\Message;

// chrome devtools uri
$webSocketUri = 'ws://127.0.0.1:9222/devtools/browser/xxx';

// 接続を作成
$connection = new Connection($webSocketUri);
$connection->connect();

// "Target.activateTarget"メソッドの送信
$responseReader = $connection->sendMessage(new Message('Target.activateTarget', ['targetId' => 'xxx']));

// 応答を1000ミリ秒待つ
$response = $responseReader->waitForResponse(1000);

セッションを作成し、ターゲットにメッセージを送信

// ターゲットIDを設定
$targetId = 'yyy';

// ターゲットのセッションを作成(attachToTarget)
$session = $connection->createSession($targetId);

// ターゲットにメッセージを送信(Target.sendMessageToTarget)
$response = $session->sendMessageSync(new Message('Page.reload'));

デバッグ

各操作が行われる前に遅延を設定することで、容易にデバッグを行えます。

// デバッグのために各操作間で500ミリ秒待つ
$connection->setConnectionDelay(500);

ブラウザ(スタンドアロン)

use HeadlessChromium\Communication\Connection;
use HeadlessChromium\Browser;

// chrome devtools uri
$webSocketUri = 'ws://127.0.0.1:9222/devtools/browser/xxx';

// WebソケットのURIを指定して接続を作成
$connection = new Connection($webSocketUri);
$connection->connect();

// ブラウザを作成
$browser = new Browser($connection);

WEBサイト制作のお問い合わせ、お見積り依頼、ご質問は
こちらのお問い合わせフォームよりお願いいたします

メールお問い合わせはこちら

フライング・ハイ・ワークスの紹介

フライング・ハイ・ワークスは、東京のホームページ制作・Web制作会社・システム開発会社です。東京都及びその近郊(首都圏)を中心として、SEO対策を意識したPC及びスマホのサイトをワンソース(レスポンシブ対応)で制作します。

実績

デザイナーチームは、グラフィックデザインやイラストの制作も得意としており、著作権を意識しない素材の提供が可能です。システム・コーディングチームでは、Laravelなどを使用したスクラッチからのオリジナルシステム開発を始め、WordPressのカスタマイズを得意としております。

また、SEOやランディングページ(LP)、広告向けバナーなどを他社様でやっていた作業の引継ぎでも問題ありません。制作実績は多数ございますので、お客様に合わせたご提案が可能です。

500点以上のフライング・ハイ・ワークスの制作実績ページをご覧ください!

東京のホームページ制作・Web制作のお問い合わせ、お見積り依頼、相談、質問は
こちらのお問い合わせフォームよりお願いいたします

メールお問い合わせはこちら