Open2

Cloudflare Browser Rendering (Puppeteer)

kazuphkazuph

https://developers.cloudflare.com/browser-rendering/get-started/screenshots/

基本的にこの通りでできます。CFに要課金です。

setup

まずCloudflareに月額$5ドル払います。これをしないとローカルで試しても利用できません(どちらにせよ--remote付けないとだけど)。

雛形作成。

npm create cloudflare@latest

インストール

npm install @cloudflare/puppeteer --save-dev

KVの追加

npx wrangler kv:namespace create BROWSER_KV_DEMO
npx wrangler kv:namespace create BROWSER_KV_DEMO --preview
#:schema node_modules/wrangler/config-schema.json
name = "jmobile-puppeteer"
main = "src/index.ts"
compatibility_date = "2024-04-23"
compatibility_flags = ["nodejs_compat"]

browser = { binding = "MY_BROWSER" }

kv_namespaces = [
  { binding = "BROWSER_KV_DEMO", id = "",preview_id = "" }
]

ソースコード

src/index.ts
import puppeteer from "@cloudflare/puppeteer";

interface Env {
  MY_BROWSER: Fetcher;
  BROWSER_KV_DEMO: KVNamespace;
}

export default {
  async fetch(request, env): Promise<Response> {
    const { searchParams } = new URL(request.url);
    let url = searchParams.get("url");
    let img: Buffer;
    if (url) {
      url = new URL(url).toString(); // normalize
      img = await env.BROWSER_KV_DEMO.get(url, { type: "arrayBuffer" });
      if (img === null) {
        const browser = await puppeteer.launch(env.MY_BROWSER);
        const page = await browser.newPage();
        await page.goto(url);
        img = (await page.screenshot()) as Buffer;
        await env.BROWSER_KV_DEMO.put(url, img, {
          expirationTtl: 60 * 60 * 24,
        });
        await browser.close();
      }
      return new Response(img, {
        headers: {
          "content-type": "image/jpeg",
        },
      });
      // biome-ignore lint/style/noUselessElse: <explanation>
    } else {
      return new Response("Please add an ?url=https://example.com/ parameter");
    }
  },
} satisfies ExportedHandler<Env>;

実行

npx wrangler dev --remote

<LOCAL_HOST_URL>/?url=https://zenn.dev/

にアクセスするとスクショが表示されます。

いやめっちゃ長!?

kazuphkazuph

普通にKV邪魔なので外してスクショだけ返すようにしてますが、それでも全然余裕で楽にスクレイピングできます。簡単なサイトであれば手元でPuppeteerを起動せずにこのスクショの撮影だけでいけるということがわかりました。

import puppeteer from "@cloudflare/puppeteer";

interface Env {
  MY_BROWSER: Fetcher;
}

export default {
  async fetch(request, env): Promise<Response> {
    const { searchParams } = new URL(request.url);
    const url = "https://....";
    const browser = await puppeteer.launch(env.MY_BROWSER);
    const page = await browser.newPage();
    await page.goto(url);

    // ユーザーIDとパスワードを入力してログイン
    await page.type("#user_id", "");
    await page.type("#password", "");
    await page.click("#login");
    await page.waitForNavigation();

    await page.goto("https://....");

    const img = (await page.screenshot()) as Buffer;
    await browser.close();
    return new Response(img, {
      headers: {
        "content-type": "image/jpeg",
      },
    });
  },
} satisfies ExportedHandler<Env>;