You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Playwright下载无.pdf后缀的生成式PDF文件?

问题分析

你的代码卡在等待下载事件,核心原因是无头模式下Chromium的下载行为与非无头模式存在差异,同时弹窗页面的元素加载等待、事件监听逻辑可能存在时序问题。手动操作能成功说明业务流程没问题,只需调整自动化环境配置和等待逻辑即可解决。

解决方案

1. 配置无头模式的下载权限与路径

在创建浏览器上下文时,明确指定下载路径并授予下载权限,避免无头模式下的默认行为限制:

const context = await browser.newContext({
  acceptDownloads: true,
  downloadsPath: downloadPath, // 直接指定下载目录
  permissions: ['downloads']   // 授予下载权限
});

2. 禁用Chromium内置PDF查看器

无头模式下Chromium默认会用内置查看器打开PDF,而非触发下载,需通过启动参数强制下载行为:

const browser = await chromium.launch({
  headless: true,
  args: [
    '--disable-pdf-viewer',                  // 禁用内置PDF查看器
    '--allow-downloads-without-user-activation', // 允许自动化触发下载
    '--download-restrictions=0'              // 解除下载限制
  ]
});

3. 优化事件监听与页面等待逻辑

通过Promise.all并行监听弹窗和下载事件,避免时序偏差;同时添加页面加载等待,确保弹窗内元素可交互:

// 并行监听弹窗和弹窗内的下载事件
const [page1, download] = await Promise.all([
  page.waitForEvent('popup'),
  (async () => {
    const popup = await page.waitForEvent('popup');
    await popup.waitForLoadState('networkidle'); // 等待弹窗页面完全加载
    return popup.waitForEvent('download');
  })()
]);

// 触发PDF导出弹窗
await page.frameLocator('frame[name="link"]').getByRole('link', { name: 'Export PDF...' }).click();

// 等待下载按钮可见后再点击
await page1.getByRole('button', { name: 'Submit' }).waitFor({ state: 'visible' });
await page1.getByRole('button', { name: 'Submit' }).click();

完整优化代码

const { chromium } = require('playwright');
const path = require('path');

(async () => {
  const downloadPath = path.join(__dirname, 'edt');
  
  const browser = await chromium.launch({
    headless: true,
    args: [
      '--disable-pdf-viewer',
      '--allow-downloads-without-user-activation',
      '--download-restrictions=0'
    ]
  });
  
  const context = await browser.newContext({
    acceptDownloads: true,
    downloadsPath: downloadPath,
    permissions: ['downloads']
  });
  
  const page = await context.newPage();

  // 登录流程
  await page.goto('https://planif.eppe.ui/jsp/custom/eppee/easyMyPlanning.jsp');
  await page.getByLabel('Identifiant :').fill('myusername');
  await page.getByLabel('Mot de passe :').fill('mysecretpassword');
  await page.getByLabel('Mot de passe :').press('Enter');
  
  // 选择选项并提交
  await page.getByRole('combobox').selectOption('10');
  await page.getByRole('button', { name: 'Submit' }).click();
  
  await page.locator('.footer > a').click();

  // 并行监听弹窗和下载事件
  const [page1, download] = await Promise.all([
    page.waitForEvent('popup'),
    (async () => {
      const popup = await page.waitForEvent('popup');
      await popup.waitForLoadState('networkidle');
      return popup.waitForEvent('download');
    })()
  ]);

  // 触发PDF导出弹窗
  await page.frameLocator('frame[name="link"]').getByRole('link', { name: 'Export PDF...' }).click();

  // 点击下载按钮
  await page1.getByRole('button', { name: 'Submit' }).waitFor({ state: 'visible' });
  await page1.getByRole('button', { name: 'Submit' }).click();

  // 保存下载文件
  await download.saveAs(path.join(downloadPath, 'edt.pdf'));
  
  await browser.close();
})();
关键说明
  • 无头模式参数直接改变了Chromium对PDF的处理逻辑,强制触发下载而非内置查看;
  • 并行事件监听避免了先等待弹窗再监听下载的时序漏洞;
  • 页面加载等待确保弹窗内的下载按钮完全就绪,不会出现点击无效的情况。

内容的提问来源于stack exchange,提问作者lolozen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 19:30:55