Python批量保存网页为PDF问题求助(PyQt5/wkhtmltopdf)
批量将网页保存为PDF的解决方案
问题分析
你的代码核心问题在于:
- 每次调用
save_webpage_as_pdf时,pdfPrintingFinished信号直接绑定了QApplication.exit(),导致第一个网页转换完成后程序立即退出,后续任务完全无法执行。 - 循环中一次性创建多个
QWebEngineView实例,Qt事件循环无法正确处理多异步加载/打印任务,容易出现资源冲突或任务中断。
改进后的PyQt5方案
改用任务队列串行处理,完成一个网页转换后再启动下一个,避免提前退出程序:
import requests from bs4 import BeautifulSoup import sys from PyQt5 import QtWidgets, QtWebEngineWidgets from PyQt5.QtCore import QUrl, QObject from PyQt5.QtGui import QPageLayout, QPageSize from pathlib import Path class PDFConverter(QObject): def __init__(self, url_queue): super().__init__() self.url_queue = url_queue self.loader = QtWebEngineWidgets.QWebEngineView() self.loader.setZoomFactor(1) self.layout = QPageLayout() self.layout.setPageSize(QPageSize(QPageSize.A4Extra)) self.layout.setOrientation(QPageLayout.Portrait) # 打印完成后触发下一个任务 self.loader.page().pdfPrintingFinished.connect(self.process_next_task) self.loader.loadFinished.connect(self.print_pdf) def start(self): # 启动首个任务 self.process_next_task() def process_next_task(self): if self.url_queue: url, output_file = self.url_queue.pop(0) if not Path(output_file).is_file(): self.current_output = output_file self.loader.load(QUrl(url)) else: # 文件已存在,直接跳过 self.process_next_task() else: # 所有任务完成后再退出 QtWidgets.QApplication.exit() def print_pdf(self, finished): if finished: self.loader.page().printToPdf(self.current_output, pageLayout=self.layout) if __name__ == "__main__": app = QtWidgets.QApplication(sys.argv) # 爬取目标网页列表 catalogue_url = "https://debis.deu.edu.tr/ders-katalog/2023-2024/tr/" department_url = "bolum_1129_tr.html" reqs = requests.get(catalogue_url + department_url) content = reqs.content.decode('utf-8') soup = BeautifulSoup(content, 'html.parser') urls = soup.find_all('a') start_index = [url.get('href') for url in urls].index("../havuz/1129tr.pdf") end_index = [url.get('href') for url in urls].index("bolum_1129_tr.html") # 构建任务队列:[(目标URL, 输出文件名), ...] task_queue = [] for i in range(start_index + 1, end_index): course_url = catalogue_url + urls[i].get('href') output_file = f"File({i}).pdf" task_queue.append((course_url, output_file)) print(f"待处理:{course_url} -> {output_file}") # 启动转换器 converter = PDFConverter(task_queue) converter.start() sys.exit(app.exec_())
改进点说明
- 用
PDFConverter类统一管理任务队列,串行处理每个网页,避免多实例资源冲突。 - 仅在所有任务完成后才退出应用,保证所有转换任务执行完毕。
- 自动跳过已存在的PDF文件,避免重复转换。
替代方案:使用Playwright(更简单稳定)
如果PyQt5的配置仍有问题,推荐使用Playwright,它对网页渲染和PDF转换的支持更友好,逻辑更直观:
步骤1:安装依赖
pip install playwright playwright install chromium
步骤2:批量转换代码
import requests from bs4 import BeautifulSoup from playwright.sync_api import sync_playwright from pathlib import Path # 爬取目标网页列表 catalogue_url = "https://debis.deu.edu.tr/ders-katalog/2023-2024/tr/" department_url = "bolum_1129_tr.html" reqs = requests.get(catalogue_url + department_url) content = reqs.content.decode('utf-8') soup = BeautifulSoup(content, 'html.parser') urls = soup.find_all('a') start_index = [url.get('href') for url in urls].index("../havuz/1129tr.pdf") end_index = [url.get('href') for url in urls].index("bolum_1129_tr.html") # 批量转换 with sync_playwright() as p: browser = p.chromium.launch(headless=True) page = browser.new_page() page.set_viewport_size({"width": 1280, "height": 720}) for i in range(start_index + 1, end_index): course_url = catalogue_url + urls[i].get('href') output_file = f"File({i}).pdf" if not Path(output_file).is_file(): print(f"转换中:{course_url}") # 等待网页完全加载后再生成PDF page.goto(course_url, wait_until="networkidle") page.pdf( path=output_file, format="A4", print_background=True ) else: print(f"文件已存在,跳过:{output_file}") browser.close()
优势
- 无需处理Qt事件循环的异步问题,代码逻辑更简洁。
- 支持等待网页完全加载(
wait_until="networkidle"),避免内容未加载完全就生成PDF。 - 自动处理页面样式、图片等资源加载,转换效果更稳定。
内容的提问来源于stack exchange,提问作者bkarpuz
相关产品推荐
相关产品推荐

