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、付费专栏及课程。

余额充值