前段时间,州的先生为了在觅道文档中实现 markdown 转 pdf 的功能,考察和调研的市面上的一些通行解决方案,详见>>>Python转换HTML为PDF方案合集,你中意哪种?。
在那之后,觅道文档选择了Chromium pyppeteer 的方案作为 HTML 转换 PDF 的技术栈。
但是这个方案并非完美可靠,由于 PyPeeteer 这个第三方库年久失修,很多 Bug 没有修复,导致在觅道文档中调用它经常性地会出现异常。
不得已,州的先生只得另寻它法。由于觅道文档中生成的 PDF 是需要动态渲染一些图形的(比如 Echarts 图表、思维导图、流程图等),所以只能在基于浏览器内核进行渲染的工具中进行选择。因为 whtmltopdf 使用的是老旧的 webkit 作为渲染内核,第一个就将其否决掉。
然后基于对 PyQt5 的熟悉,在 Windows 上使用 PyQt5 的 QWebengine 小部件对 HTML 文件进行 PDF 转换,测试效果还行。结果代码上传到 Linux 服务器上之后,各种系统依赖性的确实和报错。考虑到安装部署的便捷性,只能放弃这个测试了很久的方案。
最后转向了使用 Selenium 调用 Chromium 浏览器的无头模式,将打开的 HTML 打印导出为 PDF,算是比较完美地解决了觅道文档中文集导出 PDF 的问题。下面来看看最核心的实现过程:
依赖库
代码语言:javascript复制from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import base64
配置 chrome 的启动参数
代码语言:javascript复制webdriver_options = Options()
webdriver_prefs = {}
webdriver_options.add_argument('--headless')
webdriver_options.add_argument('--disable-gpu')
webdriver_options.add_argument('--no-sandbox')
webdriver_options.add_argument('--disable-dev-shm-usage')
webdriver_options.experimental_options['prefs'] = webdriver_prefs
webdriver_prefs['profile.default_content_settings'] = {'images': 2}
实例化一个 Chrome
首先在 Selenium 中 实例化一个 Chrome 对象:
代码语言:javascript复制driver = webdriver.Chrome(executable_path=settings.CHROMIUM_DRIVER_PATH,options=webdriver_options)
然后请求 HTML 文件,path
为 HTML 文件路径,也可以为 url:
driver.get(path)
加载及处理
首先等待请求加载的完成:
代码语言:javascript复制WebDriverWait(driver, timeout).until(staleness_of(driver.find_element_by_tag_name('html')))
然后,配置一个用于打印命令的字典:
代码语言:javascript复制calculated_print_options = {
'landscape': False,
'displayHeaderFooter': False,
'printBackground': True,
'preferCSSPageSize': True,
}
接着,获取 selenium 当前 session 的相关信息,使用让 Chrome 执行 Page.printToPDF
这一用于打印页面的命令:
resource = "/session/%s/chromium/send_command_and_get_result" % driver.session_id
url = driver.command_executor._url resource
body = json.dumps({'cmd': 'Page.printToPDF', 'params': calculated_print_options })
response = driver.command_executor._request('POST', url, body)
获取到最后的响应:
代码语言:javascript复制result = response.get('value')
最后将响应写入文件之中:
代码语言:javascript复制with open('report.pdf', 'wb') as file:
file.write(result)
这样,就实现了 HTML 到 PDF 文件的转换。
模块调用
实际上,Pypi 中已经存在第三方模块实现了上述的流程,并且添加了 PDF 文件压缩的功能。通过如下命令即可安装使用:
代码语言:javascript复制pip install pyhtml2pdf
具体的使用方法详见:https://pypi.org/project/pyhtml2pdf/
上述实现的觅道文档代码位于(点击“阅读原文”快捷访问):https://gitee.com/zmister/MrDoc/blob/master/app_doc/report_html2pdf.py
?分享、点赞、在看,给个三连击呗!?