python实现长文HTML自动转换为pdf

本文将介绍如何使用python将HTML超长页面自动分页打印为pdf的几种方法,主要解决了长文中图片加载不全的问题,部分css样式可能无法体现。

项目地址:Github

1. HTML模板

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Long HTML Page</title>
    <style>
        body {
            /* text-align: center; */
            width: 80%;
            margin: 20px auto;
            font-family: Arial, sans-serif;
        }
        p {
            margin-bottom: 20px;
        }
    </style>
</head>
<body>

<h1>Super Long HTML Page</h1>

<p>
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum euismod massa ut quam dapibus, vel accumsan nulla iaculis.
    Curabitur vel leo in dolor tincidunt laoreet. Fusce nec ligula eu nisl facilisis facilisis. Quisque vel elit vel purus bibendum
    ultrices. Nulla facilisi. Aliquam erat volutpat. Nunc accumsan dui vel sapien tincidunt, et bibendum velit tempus.
</p>

<img src="http://wxapp.tc.qq.com/262/20304/stodownload?m=fe710280d3694acff34d47cd254d8c1b&filekey=30350201010421301f02020106040253480410fe710280d3694acff34d47cd254d8c1b02034613b0040d00000004627466730000000132&hy=SH&storeid=2632486bb00047401000000000000010600004f50534803cd2a00b10d983bf&bizid=1023" alt="" width="200" height="300">

<img src="./img/img1.jpg" alt="">
<br>
<img src="./img/img2.jpg" alt="">

<p>
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum euismod massa ut quam dapibus, vel accumsan nulla iaculis.
    Curabitur vel leo in dolor tincidunt laoreet. Fusce nec ligula eu nisl facilisis facilisis. Quisque vel elit vel purus bibendum
    ultrices. Nulla facilisi. Aliquam erat volutpat. Nunc accumsan dui vel sapien tincidunt, et bibendum velit tempus.
</p>

<img src="http://wxapp.tc.qq.com/262/20304/stodownload?m=fe710280d3694acff34d47cd254d8c1b&filekey=30350201010421301f02020106040253480410fe710280d3694acff34d47cd254d8c1b02034613b0040d00000004627466730000000132&hy=SH&storeid=2632486bb00047401000000000000010600004f50534803cd2a00b10d983bf&bizid=1023" alt="" width="200" height="300">

<img src="./img/img1.jpg" alt="">
<br>
<img src="./img/img2.jpg" alt="">

<p>
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum euismod massa ut quam dapibus, vel accumsan nulla iaculis.
    Curabitur vel leo in dolor tincidunt laoreet. Fusce nec ligula eu nisl facilisis facilisis. Quisque vel elit vel purus bibendum
    ultrices. Nulla facilisi. Aliquam erat volutpat. Nunc accumsan dui vel sapien tincidunt, et bibendum velit tempus.
</p>

<img src="http://wxapp.tc.qq.com/262/20304/stodownload?m=fe710280d3694acff34d47cd254d8c1b&filekey=30350201010421301f02020106040253480410fe710280d3694acff34d47cd254d8c1b02034613b0040d00000004627466730000000132&hy=SH&storeid=2632486bb00047401000000000000010600004f50534803cd2a00b10d983bf&bizid=1023" alt="" width="200" height="300">

<img src="./img/img1.jpg" alt="">
<br>
<img src="./img/img2.jpg" alt="">

<p>
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum euismod massa ut quam dapibus, vel accumsan nulla iaculis.
    Curabitur vel leo in dolor tincidunt laoreet. Fusce nec ligula eu nisl facilisis facilisis. Quisque vel elit vel purus bibendum
    ultrices. Nulla facilisi. Aliquam erat volutpat. Nunc accumsan dui vel sapien tincidunt, et bibendum velit tempus.
</p>

<img src="http://wxapp.tc.qq.com/262/20304/stodownload?m=fe710280d3694acff34d47cd254d8c1b&filekey=30350201010421301f02020106040253480410fe710280d3694acff34d47cd254d8c1b02034613b0040d00000004627466730000000132&hy=SH&storeid=2632486bb00047401000000000000010600004f50534803cd2a00b10d983bf&bizid=1023" alt="" width="200" height="300">

<img src="./img/img1.jpg" alt="">
<br>
<img src="./img/img2.jpg" alt="">

<p>
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum euismod massa ut quam dapibus, vel accumsan nulla iaculis.
    Curabitur vel leo in dolor tincidunt laoreet. Fusce nec ligula eu nisl facilisis facilisis. Quisque vel elit vel purus bibendum
    ultrices. Nulla facilisi. Aliquam erat volutpat. Nunc accumsan dui vel sapien tincidunt, et bibendum velit tempus.
</p>

<img src="http://wxapp.tc.qq.com/262/20304/stodownload?m=fe710280d3694acff34d47cd254d8c1b&filekey=30350201010421301f02020106040253480410fe710280d3694acff34d47cd254d8c1b02034613b0040d00000004627466730000000132&hy=SH&storeid=2632486bb00047401000000000000010600004f50534803cd2a00b10d983bf&bizid=1023" alt="" width="200" height="300">

<img src="./img/img1.jpg" alt="">
<br>
<img src="./img/img2.jpg" alt="">

<p>
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum euismod massa ut quam dapibus, vel accumsan nulla iaculis.
    Curabitur vel leo in dolor tincidunt laoreet. Fusce nec ligula eu nisl facilisis facilisis. Quisque vel elit vel purus bibendum
    ultrices. Nulla facilisi. Aliquam erat volutpat. Nunc accumsan dui vel sapien tincidunt, et bibendum velit tempus.
</p>

<img src="http://wxapp.tc.qq.com/262/20304/stodownload?m=fe710280d3694acff34d47cd254d8c1b&filekey=30350201010421301f02020106040253480410fe710280d3694acff34d47cd254d8c1b02034613b0040d00000004627466730000000132&hy=SH&storeid=2632486bb00047401000000000000010600004f50534803cd2a00b10d983bf&bizid=1023" alt="" width="200" height="300">

<img src="./img/img1.jpg" alt="">
<br>
<img src="./img/img2.jpg" alt="">

</body>

</html>

2. 依赖库安装

我们选择Playwright对浏览器进行操作

(1)安装Playwright依赖库(Playwright支持Async\Await语法,故需要Python3.7+)

pip install playwright

(2)安装Chromium、Firefox、WebKit等浏览器的驱动文件(内置浏览器)

python -m playwright install

3. 转换方法

方法一:html内容生成blob对象,然后进行base64编码,构建data url

        该方法参考了screenshot-api-server这个开源项目,刚开始使用他的方法时发现本地图片无法加载,因为本地的图片不能在screenshot-api-server的网络中访问到。后来尝试将本地图片转换为base64格式嵌入到html文件中,发现可以完成转换。

        screenshot-api-server是由javascript构建的项目,作者想尝试用python实现这样的功能。先构建data url:

def create_blob_and_url(html_content):
    """
    创建Blob和URL
    :param html_content: HTML内容"""
    # 将HTML内容编码为字节
    html_bytes = html_content.encode('utf-8')
    # 创建一个io.BytesIO对象,模拟Blob
    blob = io.BytesIO(html_bytes)
    # 获取Blob的base64编码
    blob_base64 = base64.b64encode(blob.getvalue()).decode('utf-8')
    # 生成data URL
    url = f'data:text/html;base64,{blob_base64}'
    return url

        然后在浏览器中打印:

def generate_pdf(url, scroll_speed):
    """
    生成pdf
    :param url: 页面URL
    :param scroll_speed: 滚动速度"""
    with sync_playwright() as p:
        browser = p.chromium.launch(
            headless=True,                # 是否使用无头模式            
            args=['--no-sandbox',
                '--disable-setuid-sandbox',
                '--disable-gpu',
                '--unlimited-storage',
                '--disable-dev-shm-usage',
                '--full-memory-crash-report',
                '--disable-extensions',
                '--mute-audio',
                '--no-zygote',
                '--no-first-run',
                '--start-maximized'
                ],        # 传递给浏览器的命令行参数
        )
        page = browser.new_page()
        page.goto(url)
        page.wait_for_load_state('networkidle')
        # 生成PDF
        page.pdf(path='template1.pdf', format='A4')
        browser.close() 

        打印之后发现只有前面的图片加载成功,后面的网图和本地图片都没有加载成功。后来发现在浏览器界面预览后可以加载全部图片,于是想到模拟浏览器滚动页面,滚动至底部在打印。

def generate_pdf(url, scroll_speed):
    """
    生成pdf
    :param url: 页面URL
    :param scroll_speed: 滚动速度"""
    with sync_playwright() as p:
        browser = p.chromium.launch(
            headless=True,                # 是否使用无头模式            
            args=['--no-sandbox',
                '--disable-setuid-sandbox',
                '--disable-gpu',
                '--unlimited-storage',
                '--disable-dev-shm-usage',
                '--full-memory-crash-report',
                '--disable-extensions',
                '--mute-audio',
                '--no-zygote',
                '--no-first-run',
                '--start-maximized'
                ],        # 传递给浏览器的命令行参数
        )
        page = browser.new_page()
        page.goto(url)
         # 获取页面高度
        page_height = page.evaluate('document.body.scrollHeight')
        # 定义滚动步长,控制滚动速度
        scroll_step = 500
        # 计算总共需要滚动的步数
        total_steps = page_height // scroll_step
        for step in range(total_steps):
            # 计算每一步的滚动位置
            scroll_position = step * scroll_step
            # 执行 JavaScript 代码,将页面滚动到指定位置
            page.evaluate(f'window.scrollTo(0, {scroll_position});')
            # 等待一小段时间,模拟滚动速度
            page.wait_for_timeout(scroll_speed)
        # 等待网络空闲
        page.wait_for_load_state('networkidle')
        # 生成PDF
        page.pdf(path='template1.pdf', format='A4')
        browser.close()

        果然,打印成功!

方法二:直接操作html文件

        既然是借助浏览器打印的pdf,为什么不可以直接操作html文件呢?于是作者开始新的尝试,在脚本中使用浏览器打开页面加载html,操作浏览器页面滚动,等待页面加载,打印pdf.

        尝试过后发现,html中的本地文件只需提供相对路径\绝对路径,网络图片也可以正常加载。

def generate_pdf(file_path, scroll_speed):
    """
    生成pdf
    :param url: 页面URL
    :param scroll_speed: 滚动速度"""
    with sync_playwright() as p:
        browser = p.chromium.launch(
            headless=True,                # 是否使用无头模式            
            args=['--no-sandbox',
                '--disable-setuid-sandbox',
                '--disable-gpu',
                '--unlimited-storage',
                '--disable-dev-shm-usage',
                '--full-memory-crash-report',
                '--disable-extensions',
                '--mute-audio',
                '--no-zygote',
                '--no-first-run',
                '--start-maximized'
                ],        # 传递给浏览器的命令行参数
        )
        page = browser.new_page()

        page.goto('file:///' + file_path)
        # 获取页面高度
        page_height = page.evaluate('document.body.scrollHeight')
        # 定义滚动步长,控制滚动速度
        scroll_step = 500
        # 计算总共需要滚动的步数
        total_steps = page_height // scroll_step
        for step in range(total_steps):
            # 计算每一步的滚动位置
            scroll_position = step * scroll_step
            # 执行 JavaScript 代码,将页面滚动到指定位置
            page.evaluate(f'window.scrollTo(0, {scroll_position});')
            # 等待一小段时间,模拟滚动速度
            page.wait_for_timeout(scroll_speed)
        # 等待网络空闲
        page.wait_for_load_state('networkidle')
        # 生成PDF
        page.pdf(path='template2.pdf', format='A4')
        browser.close()

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

枕石zs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值