Python利用wkhtmltopdf将网页上的文章保存为PDF文件

发布于:2024-05-16 ⋅ 阅读:(59) ⋅ 点赞:(0)

目前接到一个任务,就是把confluence上的所有文章保存为PDF,其实就是把HTML字符串保存为PDF文件,一开始在网上各种搜,copy代码下来运行都会报错,简直要疯了,最后找到了一个大佬写的代码,可以运行!!!在此十分感谢!!!原文链接见文章末尾

首先安装wkhtmltopdf,去官网下载wkhtmltopdf的exe文件运行安装就行

我把大佬的代码修改了一下,结合Python协程,让文章下载的速度更快,其实还可以再结合多线程完成,看具体的需求了。代码如下

import re
import requests  # pip install requests
import parsel  # pip install parsel
import pdfkit  # pip install pdfkit
import asyncio
import aiohttp  # pip install aiohttp

html_str = """
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
{article}
</body>
</html>
"""


def change_title(title):
    """
    替换标题中的特殊字符
    :param title: 传入文章标题
    :return: 返回一个替换掉特殊字符的标题
    """
    # 使用re.compile()将正则表达式的字符串形式编译为一个对象,通过该对象提供的一些列方法对文本进行匹配查找
    pattern = re.compile(r"[/\:*?\"<>|]")  # '/ \ : * ? " < > |'
    # re.sub() 第一个参数对应的正则表达式,第二个参数为要替换成的字符串, 第三个参数为源字符串
    new_title = re.sub(pattern, "_", title)  # 替换为下划线
    return new_title


async def get_all_page_url(url):
    """
    获取需要保存的文章的url并保存为PDF文件
    :param url: 文章列表的url
    :return:
    """
    headers = {
        'user-agent': 'xxx'  # 写自己浏览器的信息
    }
    response = requests.get(url=url, headers=headers)
    selector = parsel.Selector(response.text)
    # 获取所有文章的URL
    hrefs = selector.css('ul.column_article_list li  a::attr(href)').getall()

    # wkhtmltopdf.exe 存放的路径
    path = r"xxx\bin\wkhtmltopdf.exe"
    config = pdfkit.configuration(wkhtmltopdf=path)

    async with aiohttp.ClientSession() as client:
        await asyncio.gather(*[get_page_content_to_pdf(client, link, config, headers) for link in hrefs])


async def get_page_content_to_pdf(client, url, config_obj, headers):
    """
    保存网页上的内容为PDF文件
    :param client: 
    :param url: 
    :param config_obj: 
    :param headers: 
    :return: 
    """
    select = {
        'title': '#articleContentId::text',
        'content': '#content_views'
    }
    async with client.get(url, headers=headers) as resp:
        assert resp.status == 200
        text = await resp.text()
        selector = parsel.Selector(text)
        title = selector.css(select['title']).get()
        content = selector.css(select['content']).get()
        new_title = change_title(title)
        # 创建文件保存地址以及保存文件的名字和格式
        pdf_path = './pdf/' + new_title + '.pdf'
        # str.format() 字符串格式化方法
        html = html_str.format(article=content)
        print(f'正在保存:{title}')
        # 把 html 通过 pdfkit 变成 pdf 文件
        pdfkit.from_string(html, pdf_path, configuration=config_obj)
        print(f'{title}保存完成')


if __name__ == '__main__':
    _url = 'https://blog.csdn.net/2301_77659011/category_12530863.html'
    asyncio.run(get_all_page_url(_url))

经过发现,如果要保存的网页上有图片之类的资源,需要图片的链接是完整的外部链接(即以http开头的),不然保存下来的pdf里会没有图片的部分,目前没有发现更好的解决方法

关于HTML转PDF,参考:HTML转PDF

关于协程中的aiohttp.ClientSession,参考:aiohttp.ClientSession