💨

Browser Rendering APIでウェブサイトを単一のPDFファイルにしてダウンロードする

2024/04/15に公開

Cloudflare WorkersのBrowser Rendering APIのpuppeteer互換APIを使って与えられたURLの下位リンクを辿り複数ページを含む単一のPDFファイルを生成します

PDFファイルにすることで各種チャットAIサービスに入力してQ&Aできるようになります

例: Cloudflare Workersのドキュメントと対話する

実装

PDFの連結にはpdf-libを使います

ローカルで実行するだけならpuppeteerとNode.jsでもおそらく実現できます

import puppeteer from "@cloudflare/puppeteer";
import { PDFDocument } from "pdf-lib";

interface Env {
	MY_BROWSER: Fetcher;
}

export default {
	async fetch(request: Request, env: Env) {
		const url = new URL(request.url).searchParams.get('url');
		if (!url) {
			return new Response('Missing URL parameter', { status: 400 });
		}

		const browser = await puppeteer.launch(env.MY_BROWSER);
		const page = await browser.newPage();

		await page.goto(url);

		const subLinks = await page.evaluate((currentURL) => {
			const links = Array.from(document.querySelectorAll('a'));
			return links
				.map((link) => link.href)
				.filter((href) => href.startsWith(currentURL))
				.map((link) => link.split('#')[0]);
		}, url);

		// Remove duplicate pages from the sub-links list
		const uniqueSubLinks = Array.from(new Set([url, ...subLinks]));

		const pdfDoc = await PDFDocument.create();

		// Navigate to each sub-link, generate PDF, and merge into the main PDF document
		for (const link of uniqueSubLinks) {
			console.log(`Generating PDF for: ${link}`);
			await page.goto(link);
			const pdfBytes = await page.pdf({ format: 'A4' });
			const subPdfDoc = await PDFDocument.load(pdfBytes);
			const copiedPages = await pdfDoc.copyPages(subPdfDoc, subPdfDoc.getPageIndices());
			copiedPages.forEach((page) => pdfDoc.addPage(page));
		}

		const pdfBytes = await pdfDoc.save();

		return new Response(pdfBytes, {
			headers: {
				'Content-Type': 'application/pdf',
			},
		});
	}
}

テスト

https://developers.cloudflare.com/workers/platform/ を与えたところ1.4MBのPDFファイルがダウンロードできました

61ページあります

Discussion