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サイト制作のお問い合わせ、お見積り依頼、ご質問は
こちらのお問い合わせフォームよりお願いいたします

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

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

フライング・ハイ・ワークスは、東京都渋谷区にある2000年3月創業のシステム開発にも対応できるデザインも強いWeb制作・ホームページ制作会社です。東京都及びその近郊(首都圏)を中心として、SEO対策を意識したPC及びスマホのサイトをワンソース(レスポンシブ対応)で制作します。

実績

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

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

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

採用

FHWでWebディレクター、Webデザイナー、Webプログラマーやシステム開発者として働いてみたいと思っていただける方は、下記にて定期的に募集をしておりますので、ぜひ、ご応募ください。

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

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