15
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Cloudflare Workers Browser Rendering API を使ってスクリーンショットを R2 に保存する

Posted at

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.

Rendering API architecture diagram

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

以下のパッケージをインストールすることが必要です。

@cloudflare/puppeteer - npm

npm i @cloudflare/puppeteer

スクリーンショット保存用の R2 バケット browser-worker を作成します。

wrangler r2 bucket create browser-worker

wrangler.tomlnode_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 バケットに保存する内容です。

src/index.ts
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 にスクリーンショットが保存されたことが確認できます。

image-20221207115949169

このような画像が保存されたことが確認できました。

screenshot-202212071138

感想

以下のようにシンプルな Browser Rendering API を使う場合でも、〜300ms と長めの CPU Time が消費されたことがわかります。

image-20221207132256995

こちらのプランごとの制限に照らし合わせてみても、今回のユースケースでは「Unbound Usage Model」が必要になることがわかります。

(実際に Unbound な Workers を使って実装しました。)

Worker limits · Cloudflare Workers docs

image-20221207132513608

上記のようなポイントを認識しつつも、実際の使用感としては非常に使いやすいと思いました。

これから諸々の自動化が捗りそうです。

また、バインディングについても、これから出てくるようです。

bindings = [
 { name = "my_browser” type = "browser" }
]

参考

15
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?