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

如何中断Playwright的page.expect_download()下载等待操作?

解决Playwright中主动中断page.expect_download()等待的方案

核心思路

放弃同步API,改用Playwright异步API结合事件循环,搭配简单GUI按钮触发中断信号,通过取消异步任务的方式终止expect_download()的等待,同时捕获异常保证程序不崩溃。

完整代码示例

import asyncio
import tkinter as tk
from tkinter import messagebox
from playwright.async_api import async_playwright

# 全局变量存储下载任务和浏览器上下文
download_task = None
browser_context = None

async def start_download(url):
    global browser_context
    async with async_playwright() as pw:
        browser_context = await pw.chromium.launch_persistent_context(
            r'D:\the_profile', headless=False
        )
        the_page = browser_context.pages[0]
        await the_page.goto(url)
        
        try:
            # 启动下载等待任务
            async with the_page.expect_download(timeout=21600000) as download_info:
                await the_page.click("#dl-button")
                download = await download_info.value
                await download.save_as(f"D:\\{download.suggested_filename}")
                messagebox.showinfo("提示", "下载完成")
        except asyncio.CancelledError:
            # 捕获任务取消异常,优雅终止
            if browser_context:
                # 取消浏览器中所有正在进行的下载
                for page in browser_context.pages:
                    await page.evaluate("""
                        () => {
                            const downloads = window.performance.getEntriesByType('resource');
                            downloads.forEach(item => {
                                if (item.transferSize < item.decodedBodySize) {
                                    const xhr = new XMLHttpRequest();
                                    xhr.open('HEAD', item.name, false);
                                    xhr.abort();
                                }
                            });
                        }
                    """)
                await browser_context.close()
            messagebox.showinfo("提示", "下载已取消")
        except Exception as e:
            messagebox.showerror("错误", f"下载出错:{str(e)}")
            if browser_context:
                await browser_context.close()

def cancel_download():
    global download_task
    if download_task and not download_task.done():
        download_task.cancel()

def run_gui(url):
    global download_task
    root = tk.Tk()
    root.title("下载控制器")
    root.geometry("300x100")
    
    btn_start = tk.Button(root, text="开始下载", command=lambda: asyncio.create_task(start_download(url)))
    btn_start.pack(pady=10)
    
    btn_cancel = tk.Button(root, text="取消下载", command=cancel_download)
    btn_cancel.pack(pady=10)
    
    root.mainloop()

if __name__ == '__main__':
    target_url = "你的目标URL"  # 替换为实际目标网址
    asyncio.run(run_gui(target_url))

关键部分解释

  • 异步API选择:Playwright异步API天生支持任务取消,相比同步API更容易实现中断逻辑,避免多线程导致的崩溃问题。
  • 任务取消机制:点击"取消下载"按钮时,调用download_task.cancel()终止expect_download()的等待任务,触发asyncio.CancelledError异常。
  • 优雅清理:在异常捕获块中,通过页面JS代码取消浏览器中正在进行的下载,关闭浏览器上下文,避免资源泄漏。
  • GUI与异步协同:用asyncio.create_task()启动下载任务,tkinter的GUI线程和异步事件循环协同工作,不会阻塞界面。

对现有尝试的优化

  • 替代多线程方案:异步API无需额外线程即可实现非阻塞等待,规避了Playwright同步API在多线程环境下的稳定性问题。
  • 解决临时方案缺陷:无需等待下载到95%再捕获,expect_download()会在点击下载按钮后立即监听下载事件,不会错过小文件的下载捕获。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 07:06:17