使用Selenium实现HTML转PDF

2021-03-19 15:05:00 浏览数 (1)

前段时间,州的先生为了在觅道文档中实现 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:

代码语言:javascript复制
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 这一用于打印页面的命令:

代码语言:javascript复制
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

?分享、点赞、在看,给个三连击呗!?

0 人点赞