如何中断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
相关产品推荐
相关产品推荐

