puppeteer-dart

xvrhBSD-3-Clause活跃维护

用 Dart 通过 DevTools 协议控制 Chrome 浏览器的库,移植了 Puppeteer 的 API,方便在 Dart 项目里做网页自动化、截图和生成 PDF。

A Dart library to automate the Chrome browser over the DevTools Protocol. This is a port of the Puppeteer API

v3.27.0发布于 2026-10-29 15:34 UTC260star65fork同步于 2026-11-11 05:58 UTC

怎么上手

由 README 提炼,执行前请确认
dart pub add puppeteer
硬件要求按 README 说明

README 未说明

下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

Dart 版 Puppeteer,用 DevTools 协议在 Dart 里自动化 Chrome 浏览器

用 Dart 通过 DevTools 协议控制 Chrome 浏览器的库,移植了 Puppeteer 的 API,方便在 Dart 项目里做网页自动化、截图和生成 PDF。

核心能力

  • 用 puppeteer.launch 自动下载并启动 Chrome
  • 支持页面截图、生成 PDF 和抓取页面内容
  • 提供 Locator 自动等待元素可见、稳定、可点击并重试
  • 支持 ::-p-text、::-p-xpath 和 >>> 穿透 shadow DOM 选择器
  • 内置完整类型化的 Chrome DevTools 协议 API

容易踩的坑

默认以 headless 无头模式启动,调试时需传 headless: false;首次 launch 会下载 Chrome 二进制,需保证网络可用;Flutter 环境存在已知限制,需查看 README 的 Flutter 章节

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

5 个版本v3.27.0
  1. v3.27.0最新正式版2026-10-29 · 2026-10-29 15:34 UTC
    • Update to Chrome 156.0.8078.73.
    • Regenerating the protocol against Chrome 156 drops some low-level page.devTools members that Chrome itself removed. The high-level API is unaffected (in particular page.setRequestInterception, which goes through the Fetch domain):
      • Network: the deprecated request-interception flow (onRequestIntercepted, continueInterceptedRequest, getResponseBodyForInterception, takeResponseBodyForInterceptionAsStream, the protocol-level setRequestInterception and RequestPattern), plus setAcceptedEncodings and clearAcceptedEncodingsOverride.
      • Storage: the Shared Storage commands, events and types, and getRelatedWebsiteSets.
    • Add the FindInPage domain (page.devTools.findInPage) with findFirst, findNext, findPrev and stop.
  2. v3.26.02026-08-13 · 2026-08-13 10:29 UTC
    查看更新说明
    • Update to Chrome 152.0.7977.42.
  3. v3.25.12026-06-20 · 2026-06-20 22:39 UTC
    查看更新说明
    • Complete WASM compatibility: route dart:isolate through the same shim.
  4. v3.25.02026-06-20 · 2026-06-20 20:21 UTC
    查看更新说明
    • The package is now WASM-compatible, so web keeps full marks for platform support. puppeteer.connect() and page interaction over the DevTools websocket can be compiled to WebAssembly (dart compile wasm). Internally, dart:io is now reached through a conditional shim that re-exports the real dart:io on native platforms (so the public API is unchanged there) and a stub on web/WASM. On web/WASM, puppeteer.launch(), downloadChrome and the File/IOSink convenience overloads throw UnsupportedError — only connect() and CDP-based page interaction are supported.
  5. v3.24.12026-06-20 · 2026-06-20 12:26 UTC
    查看更新说明
    • Fix downloadChrome on Windows: download coordination is now keyed per platform, so fetching multiple platforms of the same Chrome version (e.g. windows32 and windows64) no longer collides on the .downloading rename.

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    Puppeteer in Dart

    A Dart library to automate the Chrome browser over the DevTools Protocol.

    This is a port of the Puppeteer Node.JS library in the Dart language.

    pub package Build Status codecov

    What can I do?

    Most things that you can do manually in the browser can be done using Puppeteer! Here are a few examples to get you started:

    • Generate screenshots and PDFs of pages.
    • Crawl a SPA (Single-Page Application) and generate pre-rendered content (i.e. "SSR" (Server-Side Rendering)).
    • Automate form submission, UI testing, keyboard input, etc.
    • Create an up-to-date, automated testing environment. Run your tests directly in the latest version of Chrome using the latest JavaScript and browser features.
    Flutter

    See limitations with Flutter section.

    API

    • See the full API in a single-page document: doc/api.md
    • See the Dart Doc for this package: API reference
    • The Dart version of Puppeteer is very similar to the original Javascript code. Every sample available for Puppeteer Node.JS could be converted in Dart very easily.

    Examples

    Launch Chrome

    Download the last revision of chrome and launch it.

    import 'package:puppeteer/puppeteer.dart';
    
    void main() async {
      // Download the Chrome binaries, launch it and connect to the "DevTools"
      var browser = await puppeteer.launch();
    
      // Open a new tab
      var myPage = await browser.newPage();
    
      // Go to a page and wait to be fully loaded
      await myPage.goto(
        'https://pub.dev/documentation/puppeteer/latest',
        wait: Until.networkIdle,
      );
    
      // Do something... See other examples
      await myPage.screenshot();
      await myPage.pdf();
      await myPage.evaluate<String>('() => document.title');
    
      // Gracefully close the browser's process
      await browser.close();
    }
    

    Generate a PDF from a page

    import 'dart:io';
    import 'package:puppeteer/puppeteer.dart';
    
    void main() async {
      // Start the browser and go to a web page
      var browser = await puppeteer.launch();
      var page = await browser.newPage();
      await page.goto(
        'https://pub.dev/documentation/puppeteer/latest/',
        wait: Until.networkAlmostIdle,
      );
    
      // For this example, we force the "screen" media-type because sometime
      // CSS rules with "@media print" can change the look of the page.
      await page.emulateMediaType(MediaType.screen);
    
      // Capture the PDF and save it to a file.
      await page.pdf(
        format: PaperFormat.a4,
        printBackground: true,
        pageRanges: '1',
        output: File('example/_dart.pdf').openWrite(),
      );
      await browser.close();
    }
    

    Take a screenshot of a complete HTML page

    import 'dart:io';
    import 'package:puppeteer/puppeteer.dart';
    
    void main() async {
      // Start the browser and go to a web page
      var browser = await puppeteer.launch();
      var page = await browser.newPage();
    
      // Setup the dimensions and user-agent of a particular phone
      await page.emulate(puppeteer.devices.pixel2XL);
    
      await page.goto(
        'https://pub.dev/documentation/puppeteer/latest',
        wait: Until.networkIdle,
      );
    
      // Take a screenshot of the page
      var screenshot = await page.screenshot();
    
      // Save it to a file
      await File('example/_github.png').writeAsBytes(screenshot);
    
      await browser.close();
    }
    

    Take a screenshot of a specific node in the page

    import 'dart:io';
    import 'package:puppeteer/puppeteer.dart';
    
    void main() async {
      // Start the browser and go to a web page
      var browser = await puppeteer.launch();
      var page = await browser.newPage();
      await page.goto(
        'https://pub.dev/documentation/puppeteer/latest/',
        wait: Until.networkIdle,
      );
    
      // Select an element on the page
      var form = await page.$('input[id="search-box"]');
    
      // Take a screenshot of the element
      var screenshot = await form.screenshot();
    
      // Save it to a file
      await File('example/_element.png').writeAsBytes(screenshot);
    
      await browser.close();
    }
    

    Interact with the page and scrap content

    import 'package:puppeteer/puppeteer.dart';
    
    void main() async {
      var browser = await puppeteer.launch();
      var page = await browser.newPage();
    
      await page.goto(
        'https://developers.google.com/web/',
        wait: Until.networkIdle,
      );
    
      // Type into search box.
      await page.type('.devsite-search-field', 'Headless Chrome');
    
      // Wait for suggest overlay to appear and click "show all results".
      var allResultsSelector = '.devsite-suggest-all-results';
      await page.waitForSelector(allResultsSelector);
      await page.click(allResultsSelector);
    
      // Wait for the results page to load and display the results.
      const resultsSelector = '.gsc-results .gsc-thumbnail-inside a.gs-title';
      await page.waitForSelector(resultsSelector);
    
      // Extract the results from the page.
      var links = await page.evaluate<List<dynamic>>(
        r'''resultsSelector => {
      const anchors = Array.from(document.querySelectorAll(resultsSelector));
      return anchors.map(anchor => {
        const title = anchor.textContent.split('|')[0].trim();
        return `${title} - ${anchor.href}`;
      });
    }''',
        args: [resultsSelector],
      );
      print(links.join('\n'));
    
      await browser.close();
    }
    

    Use locators to interact with the page

    A Locator auto-waits for the element to be visible, stable and enabled before acting, and retries the whole action on failure. It also supports Puppeteer-specific selectors such as ::-p-text(...), ::-p-xpath(...) and the deep >>> combinator that pierces shadow DOM.

    import 'package:puppeteer/puppeteer.dart';
    
    void main() async {
      var browser = await puppeteer.launch();
      var page = await browser.newPage();
    
      await page.setContent('''
        <input name="q" />
        <button type="button" onclick="this.textContent = 'Clicked!'">Search</button>
      ''');
    
      // A locator auto-waits for the element to be visible, stable and enabled,
      // then retries the whole action until it succeeds or the timeout elapses.
      await page.locator('input[name="q"]').fill('Headless Chrome');
      await page.locator('button').click();
    
      // Puppeteer-specific selectors work anywhere a selector is accepted, e.g.
      // matching by visible text.
      var button = await page.$('::-p-text(Clicked!)');
      print(await button.evaluate('e => e.textContent'));
    
      await browser.close();
    }
    

    Create a static version of a Single Page Application

    import 'package:puppeteer/puppeteer.dart';
    
    void main() async {
      var browser = await puppeteer.launch();
      var page = await browser.newPage();
      await page.goto('https://pub.dev/documentation/puppeteer/latest/');
    
      // Either use the helper to get the content
      var pageContent = await page.content;
      print(pageContent);
    
      // Or get the content directly by executing some Javascript
      var pageContent2 = await page.evaluate<String>(
        'document.documentElement.outerHTML',
      );
      print(pageContent2);
    
      await browser.close();
    }
    

    Capture a screencast of the page

    The screencast feature is not part of the Puppeteer API. This example uses the low-level protocol API to send the commands to the browser.

    import 'dart:convert';
    import 'dart:io';
    import 'package:image/image.dart' as image;
    import 'package:puppeteer/puppeteer.dart';
    import 'package:shelf/shelf_io.dart' as io;
    import 'package:shelf_static/shelf_static.dart';
    
    void main() async {
      // Start a local web server and open the page
      var server = await io.serve(
        createStaticHandler('example/html'),
        'localhost',
        0,
      );
      var browser = await puppeteer.launch();
      var page = await browser.newPage();
      await page.goto('http://localhost:${server.port}/rubiks_cube/index.html');
    
      // On each frame, decode the image and use the "image" package to create an
      // animated gif.
      image.Image? animation;
      page.devTools.page.onScreencastFrame.listen((event) {
        var frame = image.decodePng(base64.decode(event.data));
        if (frame != null) {
          if (animation == null) {
            animation = frame;
          } else {
            animation!.addFrame(frame);
          }
        }
      });
    
      // For this example, we change the CSS animations speed.
      await page.devTools.animation.setPlaybackRate(240);
    
      // Start the screencast
      await page.devTools.page.startScreencast(maxWidth: 150, maxHeight: 150);
    
      // Wait a few seconds and stop the screencast.
      await Future.delayed(Duration(seconds: 3));
      await page.devTools.page.stopScreencast();
    
      // Encode all the frames in an animated Gif file.
      File(
        'example/_rubkis_cube.gif',
      ).writeAsBytesSync(image.GifEncoder().encode(animation!));
    
      // Alternatively, we can save all the frames on disk and use ffmpeg to convert
      // it to a video file. (for example: ffmpeg -i frames/%3d.png -r 10 output.mp4)
    
      await browser.close();
      await server.close(force: true);
    }
    

    Launch with visible window

    By default, puppeteer launch Chrome in "headless" mode, the browser is invisible. For better development & debugging, you can force "headful" mode with parameter headless: false

    import 'package:puppeteer/puppeteer.dart';
    
    void main() async {
      var browser = await puppeteer.launch(headless: false);
      // Do something...
      await browser.newPage();
      await browser.close();
    }
    

    Test a Chrome extension

    Pass the paths of unpacked extensions to puppeteer.launch(extensions: [...]), or install them later with browser.installExtension(path) on a browser launched with enableExtensions: true.

    Extensions are installed through the DevTools protocol, so this works in headless mode and with branded Google Chrome, which ignores the --load-extension flag since Chrome 137.

    import 'package:puppeteer/puppeteer.dart';
    
    void main() async {
      var browser = await puppeteer.launch(extensions: ['example/extension']);
      var extension = (await browser.extensions).values.single;
      var origin = 'chrome-extension://${extension.id}';
    
      // The extension's service worker
      var serviceWorker = await browser.waitForTarget(
        (t) => t.type == 'service_worker' && t.url.startsWith(origin),
      );
      print(serviceWorker.url);
    
      // Click the extension's toolbar icon and get its popup
      var page = await browser.newPage();
      var popupTarget = browser.waitForTarget((t) => t.url == '$origin/popup.html');
      await page.triggerExtensionAction(extension);
      var popup = await (await popupTarget).page;
      var title = await popup.waitForSelector('h1');
      print(await title!.evaluate('e => e.textContent'));
      print(await popup.evaluate('() => chrome.runtime.getManifest().name'));
    
      await browser.close();
    }
    

    Low-level DevTools protocol

    This package contains a fully typed API of the Chrome DevTools protocol. The code is generated from the JSON Schema provided by Chrome.

    With this API you have access to the entire capabilities of Chrome DevTools.

    The code is in lib/protocol

    import 'package:puppeteer/puppeteer.dart';
    
    void main() async {
      var browser = await puppeteer.launch();
      // Create a chrome's tab
      var page = await browser.newPage();
    
      // You can access the entire Chrome DevTools protocol.
      // This is useful to access information not exposed by the Puppeteer API
      // Be aware that this is a low-level, complex API.
      // Documentation of the protocol: https://chromedevtools.github.io/devtools-protocol/
    
      // Examples:
    
      // Start a screencast
      await page.devTools.page.startScreencast();
    
      // Change the animation speed for the document
      await page.devTools.animation.setPlaybackRate(10);
    
      // Access the memory information for the page
      await page.devTools.memory.getDOMCounters();
    
      // Go to https://chromedevtools.github.io/devtools-protocol/ to read more about
      // the protocol and use the code in `lib/protocol`.
    
      await browser.close();
    }
    

    Execute JavaScript code

    The Puppeteer API exposes several functions to run some Javascript code in the browser.

    Like in this example from the official Node.JS library:

    test(async () => {
      const result = await page.evaluate(x => {
        return Promise.resolve(8 * x);
      }, 7);
    });
    

    In the Dart port, you have to pass the JavaScript code as a string. The example above will be written as:

    main() async {
      var result = await page.evaluate('''x => {
        return Promise.resolve(8 * x);
      }''', args: [7]);
    }
    

    The javascript code can be:

    • A function declaration (in the classical form with the function keyword or with the shorthand format (() => ))
    • An expression. In which case you cannot pass any arguments to the evaluate method.
    import 'package:puppeteer/puppeteer.dart';
    
    void main() async {
      var browser = await puppeteer.launch();
      var page = await browser.newPage();
    
      // function declaration syntax
      await page.evaluate('function(x) { return x > 0; }', args: [7]);
    
      // shorthand syntax
      await page.evaluate('(x) => x > 0', args: [7]);
    
      // Multi line shorthand syntax
      await page.evaluate(
        '''(x) => {  
        return x > 0;
      }''',
        args: [7],
      );
    
      // shorthand syntax with async
      await page.evaluate(
        '''async (x) => {
        return await x;
      }''',
        args: [7],
      );
    
      // An expression.
      await page.evaluate('document.body');
    
      await browser.close();
    }
    

    If you are using IntellJ (or Webstorm), you can enable the syntax highlighter and the code-analyzer for the Javascript snippet with a comment like // language=js before the string.

    main() {
      page.evaluate(
      //language=js
      '''function _(x) {
        return x > 0;
      }''', args: [7]);
    }
    

    Limitations with Flutter

    This library does 2 things:

    1. Download the Chrome binaries and launch a Chrome process.
    2. Connect to this process with Websocket and send json commands to control the browser.

    Due to limitations on mobile platforms (iOS and Android), it is not possible to launch an external Chrome process on iOS and Android. So, step 1) does not work on mobile.

    You can still use puppeteer-dart on Flutter either with:

    • Flutter on Desktop (macOS, Windows, Linux)
    • Flutter on Mobile BUT with the actual Chrome instance running on a server and accessed from the mobile app using puppeteer.connect

    Related work