Workers Browser Rendering API とは
Automate an isolated browser instance with just a few lines of code
This is why we’re excited to announce a private beta of the Workers Browser Rendering API, improving the browser automation experience for developers. With browser automation, you can programmatically do anything that a user can do when interacting with a browser.
2022年12月7日時点で Private beta ですが、その様子をお届けできればと思います。
また、「WorkersブラウザレンダリングAPI | Cloudflare」から waitlist に登録可能なので、ぜひ。
環境準備
ディレクトリを準備して、wrangler init
します。TypeScript を使います。
Choose “y” to use TypeScript
mkdir browser-worker && cd $_
npm i @cloudflare/puppeteer
wrangler init
以下のパッケージをインストールすることが必要です。
npm i @cloudflare/puppeteer
スクリーンショット保存用の R2 バケット browser-worker
を作成します。
wrangler r2 bucket create browser-worker
wrangler.toml
に node_compat = true
と R2 バケットのバインディング設定を追加します。
cat << EOS >> wrangler.toml
node_compat = true
[[r2_buckets]]
bucket_name = "browser-worker"
binding = "BROWSER_BUCKET"
EOS
Workers コード
上記 GitHub のコードを使います。
中身は https://www.yahoo.co.jp/
ページのスクリーンショットを R2 バケットに保存する内容です。
import puppeteer from '@cloudflare/puppeteer'
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setViewport({
width: 1920,
height: 1080,
deviceScaleFactor: 1,
});
await page.goto("https://www.yahoo.co.jp/", {
//一定時間ネットワーク通信のないことで完了を判定する
waitUntil: "networkidle2", //コネクション数が2個以下である状態が500ミリ秒続いたとき
timeout: 0 //0を指定するとタイムアウト無し
});
const screenshot = await page.screenshot() as Buffer;
await browser.close();
const jstNow = new Date(Date.now() + ((new Date().getTimezoneOffset() + (9 * 60)) * 60 * 1000));
const yyyymmddhhmm = jstNow.toISOString().replace(/[^0-9]/g, '').slice(0, -5); //yyyymmddhhmm
console.log(yyyymmddhhmm)
try {
//upload to R2
await env.BROWSER_BUCKET.put(`screenshot-${yyyymmddhhmm}.png`, screenshot);
return new Response(`Success!`);
/*
return new Response(screenshot.buffer, {
headers: {
'content-type': 'image/png'
}
})
*/
} catch (e) {
return new Response('', { status: 400 })
}
}
}
デプロイ
以下のコマンドでデプロイします。
wrangler publish src/index.ts
動作確認
Workers
デプロイした Workers の URL にリクエストを送ると動作が確認できます。
% curl https://browser-worker.example.workers.dev
Success!
# 2022年12月7日時点ではレスポンスが返ってくるまでに 1 分弱の時間がかかっていました
# % curl https://browser-worker.example.workers.dev -s -o /dev/null -w "%{time_total}\n"
# 57.886562
Workers のリアルタイムログについて、以下のようになります。
{
"outcome": "ok",
"scriptName": "browser-worker",
"exceptions": [],
"logs": [
{
"message": [
"closing websocket"
],
"level": "log",
"timestamp": 1670386355629
},
{
"message": [
"202212071312"
],
"level": "log",
"timestamp": 1670386355629
},
{
"message": [
"websocket closed"
],
"level": "log",
"timestamp": 1670386355680
}
],
"eventTimestamp": 1670386292182,
"event": {
"request": {
"url": "https://browser-worker.example.workers.dev/",
"method": "GET",
"headers": {
"accept": "*/*",
"accept-encoding": "gzip",
"cf-connecting-ip": "2a09:bac1:3b60:10::16:18a",
"cf-ipcountry": "JP",
"cf-ray": "775a6af618fef625",
"cf-visitor": "{\"scheme\":\"https\"}",
"connection": "Keep-Alive",
"host": "browser-worker.example.workers.dev",
"user-agent": "curl/7.82.0-DEV",
"x-forwarded-proto": "https",
"x-real-ip": "2a09:bac1:3b60:10::16:18a"
},
"cf": {
"longitude": "139.68990",
"latitude": "35.68930",
"tlsCipher": "ECDHE-RSA-AES128-GCM-SHA256",
"continent": "AS",
"asn": 13335,
"country": "JP",
"tlsClientAuth": {
"certIssuerDNLegacy": "",
"certIssuerSKI": "",
"certSubjectDNRFC2253": "",
"certSubjectDNLegacy": "",
"certFingerprintSHA256": "",
"certNotBefore": "",
"certSKI": "",
"certSerial": "",
"certIssuerDN": "",
"certVerified": "NONE",
"certNotAfter": "",
"certSubjectDN": "",
"certPresented": "0",
"certRevoked": "0",
"certIssuerSerial": "",
"certIssuerDNRFC2253": "",
"certFingerprintSHA1": ""
},
"tlsExportedAuthenticator": {
"clientFinished": "e82eec20338be4132fbd52fc177d1b4e50fa3d852a78af97a1447c12295d4ca0",
"clientHandshake": "30df6cfc6441c53105e05505c92280fba9ea3896a280c30047fd55215437b4f6",
"serverHandshake": "fa7de861d2db076438bd812505abbf1338d699a7cff2f409131b6a1143b671cd",
"serverFinished": "1304cc66099f0e019e41701437eae1657b5079821bc4e01c409b4b0d530f26ee"
},
"tlsVersion": "TLSv1.2",
"colo": "NRT",
"timezone": "Asia/Tokyo",
"city": "Tokyo",
"edgeRequestKeepAliveStatus": 1,
"requestPriority": "weight=16;exclusive=0;group=0;group-weight=0",
"httpProtocol": "HTTP/2",
"region": "Tokyo",
"regionCode": "13",
"asOrganization": "Cloudflare",
"postalCode": "xxx-xxxx"
}
},
"response": {
"status": 200
}
},
"id": 0
}
R2
R2 にスクリーンショットが保存されたことが確認できます。
このような画像が保存されたことが確認できました。
感想
以下のようにシンプルな Browser Rendering API を使う場合でも、〜300ms と長めの CPU Time が消費されたことがわかります。
こちらのプランごとの制限に照らし合わせてみても、今回のユースケースでは「Unbound Usage Model」が必要になることがわかります。
(実際に Unbound な Workers を使って実装しました。)
上記のようなポイントを認識しつつも、実際の使用感としては非常に使いやすいと思いました。
これから諸々の自動化が捗りそうです。
また、バインディングについても、これから出てくるようです。
bindings = [
{ name = "my_browser” type = "browser" }
]