欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

请添加图片描述

前言

puppeteer 是一个 Node.js 库的 Dart 移植版,它提供了一套高级 API 来通过 DevTools 协议控制 Chrome 或 Chromium。通常用于爬虫、生成 PDF、截图或自动化测试。

在 OpenHarmony 移动设备上,直接启动一个 Headless Chrome 进程是不现实的(受限于系统权限和架构)。但是,我们可以利用 puppeteer 的远程连接能力,让 OpenHarmony 应用控制部署在服务器或局域网 PC 上的浏览器实例,实现强大的远程自动化功能。本文将介绍如何在 OpenHarmony 环境下使用 puppeteer 连接并控制远程浏览器。

一、puppeteer 简介与适用场景

1.1 核心功能

  • 生成截图与 PDF:将网页转换为图片或文档。
  • 爬取内容:抓取单页应用(SPA)的动态渲染内容。
  • 自动化操作:模拟键盘输入、表单提交、UI 点击。

1.2 OpenHarmony 适配策略

由于移动端无法直接运行 Chrome 二进制文件,我们采用 Client-Server 模式

  1. Server (PC/云端): 运行 Chrome 并开启远程调试端口 (--remote-debugging-port)。
  2. Client (OpenHarmony): 使用 puppeteer.connect 连接至 Server。
远程浏览器 (PC/Server) OpenHarmony 应用 远程浏览器 (PC/Server) OpenHarmony 应用 启动时开启远程调试端口 (--remote-debugging-port) puppeteer.connect(ws://ip:port) - 建立 Websocket 连接 newPage() - 新建页面 page.goto(url) - 打开目标网页 页面加载完成 page.screenshot() - 执行自动化截图 返回图片字节流 (bytes) 在应用内显示图片内容

二、环境准备与配置

2.1 添加依赖

pubspec.yaml 中:

dependencies:
  puppeteer: ^3.20.0

2.2 服务端配置(PC端)

你需要在一台电脑或服务器上启动 Chrome,允许远程调试。

# macOS 示例
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
  --remote-debugging-port=9222 \
  --headless \
  --disable-gpu

确保 OpenHarmony 设备能 ping 通这台机器的 IP(例如 192.168.1.100)。

在这里插入图片描述

三、核心 API 详解与示例

3.1 示例一:连接远程浏览器

在 OpenHarmony 应用中连接到远程 Chrome 实例。

import 'package:puppeteer/puppeteer.dart';

Future<void> connectToBrowser() async {
  // 替换为你 PC 的 IP 地址
  final browser = await puppeteer.connect(
    browserUrl: 'http://192.168.1.100:9222',
  );
  
  print('已连接到浏览器版本: ${await browser.version}');
  
  // 记得关闭连接,但通常不关闭远程浏览器本身
  await browser.disconnect();
}

在这里插入图片描述

3.2 示例二:远程截图

让远程浏览器打开网页截图,并将图片数据返回给 OpenHarmony 显示。

import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:puppeteer/puppeteer.dart';

Future<Uint8List> takeScreenshot(String url) async {
  final browser = await puppeteer.connect(
    browserUrl: 'http://192.168.1.100:9222',
  );
  
  final page = await browser.newPage();
  await page.goto(url, wait: Until.networkIdle);
  
  // 截取全屏
  final screenshot = await page.screenshot(fullPage: true);
  
  await page.close();
  await browser.disconnect();
  
  return screenshot; // 返回二进制图片数据
}

在这里插入图片描述

3.3 示例三:生成 PDF

类似截图,生成 PDF 并保存(需配合文件权限)。

Future<void> generatePdf(String url) async {
  final browser = await puppeteer.connect(
    browserUrl: 'http://192.168.1.100:9222',
  );
  
  final page = await browser.newPage();
  await page.goto(url);
  
  final pdfData = await page.pdf(format: PaperFormat.a4);
  print('PDF 生成成功,大小: ${pdfData.length} 字节');
  
  await page.close();
  await browser.disconnect();
}

四、OpenHarmony 平台适配

4.1 网络权限

连接远程调试端口需要 Internet 权限。在 module.json5 中配置:

"requestPermissions": [
  {
    "name": "ohos.permission.INTERNET"
  }
]

4.2 异常处理

移动网络不稳定,连接远程实例时极易超时。务必添加 try-catch 和超时设置。

try {
  await puppeteer.connect(..., timeout: Duration(seconds: 10));
} catch (e) {
  // 提示用户检查网络或服务状态
}

五、完整实战示例:网页截图查看器

本示例是一个 OpenHarmony 应用,用户输入网址,点击按钮后,应用控制局域网内的 Chrome 截图并显示在屏幕上。

5.1 示例代码

import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:puppeteer/puppeteer.dart';

void main() {
  runApp(const MaterialApp(home: PuppeteerDemoPage()));
}

class PuppeteerDemoPage extends StatefulWidget {
  const PuppeteerDemoPage({super.key});

  
  State<PuppeteerDemoPage> createState() => _PuppeteerDemoPageState();
}

class _PuppeteerDemoPageState extends State<PuppeteerDemoPage> {
  final TextEditingController _urlController = 
      TextEditingController(text: 'https://www.baidu.com');
  // ⚠️ 请根据实际情况修改调试地址
  final String _debugUrl = 'http://192.168.1.5:9222'; 
  
  Uint8List? _imageData;
  bool _isLoading = false;
  String _status = '准备就绪';

  Future<void> _captureScreen() async {
    setState(() {
      _isLoading = true;
      _status = '正在连接远程浏览器...';
      _imageData = null;
    });

    try {
      // 1. 连接
      final browser = await puppeteer.connect(
        browserUrl: _debugUrl,
        timeout: const Duration(seconds: 5), // 设置超时
      );

      setState(() => _status = '正在加载页面...');
      
      // 2. 打开页面
      final page = await browser.newPage();
      // 设置视口大小以获得更好的截图效果
      await page.setViewport(const DeviceViewport(width: 375, height: 812)); 
      
      await page.goto(_urlController.text, wait: Until.networkIdle);

      setState(() => _status = '正在截图...');
      
      // 3. 截图
      final screenshot = await page.screenshot();
      
      // 4. 清理
      await page.close();
      await browser.disconnect();

      if (mounted) {
        setState(() {
          _imageData = screenshot;
          _status = '截图成功';
        });
      }
    } catch (e) {
      if (mounted) {
        setState(() => _status = '错误: $e');
      }
    } finally {
      if (mounted) {
        setState(() => _isLoading = false);
      }
    }
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Puppeteer 远程截图')),
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: _urlController,
                    decoration: const InputDecoration(
                      labelText: '输入网址',
                      border: OutlineInputBorder(),
                    ),
                  ),
                ),
                const SizedBox(width: 16),
                ElevatedButton(
                  onPressed: _isLoading ? null : _captureScreen,
                  child: _isLoading 
                      ? const SizedBox(
                          width: 20, 
                          height: 20, 
                          child: CircularProgressIndicator(strokeWidth: 2),
                        )
                      : const Text('截图'),
                ),
              ],
            ),
          ),
          Text(_status, style: const TextStyle(color: Colors.grey)),
          const Divider(),
          Expanded(
            child: _imageData != null
                ? SingleChildScrollView(
                    child: Image.memory(
                      _imageData!,
                      fit: BoxFit.contain,
                    ),
                  )
                : const Center(
                    child: Icon(Icons.image, size: 64, color: Colors.grey),
                  ),
          ),
        ],
      ),
    );
  }
}

在这里插入图片描述

六、总结

虽然 OpenHarmony 移动设备无法直接运行 puppeteer 驱动的本地 Chrome,但通过远程连接模式,我们可以将繁重的网页渲染和处理任务卸载到服务端,让 OpenHarmony 应用轻松拥有“超级浏览器”的能力。

最佳实践

  1. 仅在受信任的网络环境中使用远程调试,注意安全。
  2. 对于简单的网页展示,优先使用 webview_flutter
  3. puppeteer 适合用于需要服务端生成复杂内容(如报表 PDF)并回传给客户端的场景。
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐