Flutter自动化控制插件puppeteer的使用

发布于 1周前 作者 phonegap100 来自 Flutter

Flutter自动化控制插件puppeteer的使用

Puppeteer in Dart简介

Puppeteer in Dart 是一个用于通过 DevTools 协议自动控制 Chrome 浏览器的 Dart 库。它是基于Node.JS版本的Puppeteer库移植到Dart语言上的。

pub package Build Status codecov

Buy Me A Coffee

功能介绍

你可以使用 Puppeteer 实现大多数在浏览器中手动完成的任务,例如:

  • 生成页面的截图和 PDF。
  • 爬取单页应用(SPA)并生成预渲染内容(即“SSR”(服务器端渲染))。
  • 自动化表单提交、UI 测试、键盘输入等操作。
  • 创建一个最新的测试环境。直接在最新版本的 Chrome 中运行测试,利用最新的 JavaScript 和浏览器特性。

API 文档

  • 完整的API文档
  • Dart Doc 包文档
  • Dart 版本的 Puppeteer 与原始的 Javascript 代码非常相似,几乎所有 Node.JS 的 Puppeteer 示例都可以轻松转换为 Dart。

示例代码

以下是几个简单的示例,展示了如何使用 Puppeteer in Dart 来执行各种任务:

启动Chrome

import 'package:puppeteer/puppeteer.dart';

void main() async {
  // 下载Chrome二进制文件,启动它并连接到"DevTools"
  var browser = await puppeteer.launch();

  // 打开一个新标签页
  var myPage = await browser.newPage();

  // 跳转到一个网页并等待完全加载
  await myPage.goto('https://dart.dev', wait: Until.networkIdle);

  // 截图
  await myPage.screenshot();
  
  // 获取PDF
  await myPage.pdf();
  
  // 执行JavaScript获取标题
  await myPage.evaluate<String>('() => document.title');

  // 平滑关闭浏览器进程
  await browser.close();
}

生成页面的PDF

import 'dart:io';
import 'package:puppeteer/puppeteer.dart';

void main() async {
  // 启动浏览器并跳转到一个网页
  var browser = await puppeteer.launch();
  var page = await browser.newPage();
  await page.goto('https://dart.dev', wait: Until.networkAlmostIdle);

  // 强制设置屏幕媒体类型
  await page.emulateMediaType(MediaType.screen);

  // 捕获PDF并保存到文件
  await page.pdf(
    format: PaperFormat.a4,
    printBackground: true,
    pageRanges: '1',
    output: File('example/_dart.pdf').openWrite(),
  );
  await browser.close();
}

截取整个HTML页面的截图

import 'dart:io';
import 'package:puppeteer/puppeteer.dart';

void main() async {
  // 启动浏览器并跳转到一个网页
  var browser = await puppeteer.launch();
  var page = await browser.newPage();

  // 设置特定手机的尺寸和用户代理
  await page.emulate(puppeteer.devices.pixel2XL);

  await page.goto('https://dart.dev', wait: Until.networkIdle);

  // 截取页面的截图
  var screenshot = await page.screenshot();

  // 保存到文件
  await File('example/_github.png').writeAsBytes(screenshot);

  await browser.close();
}

截取页面中特定节点的截图

import 'dart:io';
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/',
    wait: Until.networkIdle,
  );

  // 选择页面中的元素
  var form = await page.$('input[id="search-box"]');

  // 截取元素的截图
  var screenshot = await form.screenshot();

  // 保存到文件
  await File('example/_element.png').writeAsBytes(screenshot);

  await browser.close();
}

交互页面并抓取内容

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,
  );

  // 在搜索框中输入文本
  await page.type('.devsite-search-field', 'Headless Chrome');

  // 等待建议层出现并点击"显示所有结果"
  var allResultsSelector = '.devsite-suggest-all-results';
  await page.waitForSelector(allResultsSelector);
  await page.click(allResultsSelector);

  // 等待结果页面加载并显示结果
  const resultsSelector = '.gsc-results .gsc-thumbnail-inside a.gs-title';
  await page.waitForSelector(resultsSelector);

  // 从页面中提取结果
  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();
}

创建单页应用的静态版本

import 'package:puppeteer/puppeteer.dart';

void main() async {
  var browser = await puppeteer.launch();
  var page = await browser.newPage();
  await page.goto('https://w3c.github.io/');

  // 使用辅助函数获取页面内容
  var pageContent = await page.content;
  print(pageContent);

  // 或者直接执行JavaScript获取页面内容
  var pageContent2 = await page.evaluate<String>(
    'document.documentElement.outerHTML',
  );
  print(pageContent2);

  await browser.close();
}

捕获页面的屏幕录制

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 {
  // 启动本地Web服务器并打开页面
  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');

  // 每一帧解码图像并使用image包创建动画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);
      }
    }
  });

  // 改变CSS动画速度
  await page.devTools.animation.setPlaybackRate(240);

  // 开始屏幕录制
  await page.devTools.page.startScreencast(maxWidth: 150, maxHeight: 150);

  // 等几秒后停止屏幕录制
  await Future.delayed(Duration(seconds: 3));
  await page.devTools.page.stopScreencast();

  // 将所有帧编码为动画GIF文件
  File(
    'example/_rubkis_cube.gif',
  ).writeAsBytesSync(image.GifEncoder().encode(animation!));

  await browser.close();
  await server.close(force: true);
}

启动带有可见窗口的浏览器

import 'package:puppeteer/puppeteer.dart';

void main() async {
  var browser = await puppeteer.launch(headless: false);
  // 进行某些操作...
  await browser.newPage();
  await browser.close();
}

执行JavaScript代码

import 'package:puppeteer/puppeteer.dart';

void main() async {
  var browser = await puppeteer.launch();
  var page = await browser.newPage();

  // 函数声明语法
  await page.evaluate('function(x) { return x > 0; }', args: [7]);

  // 简写语法
  await page.evaluate('(x) => x > 0', args: [7]);

  // 多行简写语法
  await page.evaluate(
    '''(x) => {  
    return x > 0;
  }''',
    args: [7],
  );

  // 带有异步的简写语法
  await page.evaluate(
    '''async (x) => {
    return await x;
  }''',
    args: [7],
  );

  // 表达式
  await page.evaluate('document.body');

  await browser.close();
}

Flutter中的限制

此库的功能分为两部分:

  1. 下载 Chrome 二进制文件并启动 Chrome 进程。
  2. 通过 Websocket 连接到此进程,并发送 JSON 命令来控制浏览器。

由于移动平台(iOS 和 Android)的限制,在 iOS 和 Android 上无法启动外部 Chrome 进程。因此,第一步在移动设备上不可用。

你仍然可以在以下环境中使用 puppeteer-dart

  • Flutter 桌面版(macOS, Windows, Linux)
  • Flutter 移动端,但实际的 Chrome 实例需要在服务器上运行,并通过 puppeteer.connect 从移动端应用程序访问

相关工作

以上就是关于Flutter中使用Puppeteer进行自动化控制的相关内容,希望对你有所帮助!


更多关于Flutter自动化控制插件puppeteer的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter自动化控制插件puppeteer的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter项目中,虽然直接使用Puppeteer(一个Node库,用于自动化控制Chrome或Chromium浏览器)进行自动化测试或控制并不常见,因为Puppeteer本质上是针对Web应用的,而Flutter主要是用于构建跨平台的移动和桌面应用。然而,你可以通过一些间接的方法将Puppeteer集成到你的Flutter开发流程中,例如,使用Puppeteer进行Web端的自动化测试,同时Flutter应用可能包含一个WebView组件来加载和展示Web内容。

以下是一个简化的示例,展示了如何在Flutter项目中调用Node.js脚本(该脚本使用Puppeteer),以便对某个Web页面进行自动化控制。这只是一个概念验证,实际项目中可能需要更复杂的设置。

1. 设置Node.js和Puppeteer环境

首先,确保你的开发环境中已经安装了Node.js和npm。然后,你可以通过npm安装Puppeteer:

npm install puppeteer

2. 编写Node.js脚本(使用Puppeteer)

创建一个名为script.js的文件,并添加以下代码:

const puppeteer = require('puppeteer');

(async () => {
  // 启动浏览器
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  
  // 访问网页
  await page.goto('https://example.com');
  
  // 执行一些自动化操作,比如截图
  await page.screenshot({ path: 'example.png' });
  
  // 关闭浏览器
  await browser.close();
})();

这个脚本将启动一个浏览器实例,访问https://example.com,并截取屏幕截图保存为example.png

3. 在Flutter中调用Node.js脚本

为了在Flutter中调用这个Node.js脚本,你可以使用process_run包来执行命令行命令。首先,在你的pubspec.yaml文件中添加依赖:

dependencies:
  flutter:
    sdk: flutter
  process_run: ^0.12.0  # 请检查最新版本号

然后,在你的Flutter项目中,例如在一个按钮点击事件中,执行以下代码:

import 'package:flutter/material.dart';
import 'package:process_run/cmd_run.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Puppeteer Example'),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: () async {
              // 执行Node.js脚本
              var result = await runCmd('node script.js');
              print(result.stdout);
              print(result.stderr);
            },
            child: Text('Run Puppeteer Script'),
          ),
        ),
      ),
    );
  }
}

注意:runCmd函数是一个简化的示例,实际使用中你可能需要根据process_run包的API进行调整。process_run包提供了更丰富的功能来处理进程执行和结果处理。

注意事项

  1. 跨平台兼容性:直接在Flutter应用中调用Node.js脚本在iOS上可能不可行,因为iOS设备通常不运行Node.js。这种方法更适合在开发机器或服务器上进行自动化测试。

  2. 安全性:确保你的Node.js脚本和Flutter应用之间的交互是安全的,特别是当处理敏感信息时。

  3. 性能:在Flutter应用中启动一个完整的浏览器实例可能会消耗较多资源,影响应用性能。

通过上述方法,你可以在Flutter项目中间接地使用Puppeteer进行Web端的自动化控制。然而,对于Flutter应用本身的自动化测试,建议使用专为Flutter设计的工具,如Flutter Driver或Integration Testing。

回到顶部