• 使用 Python 将 HTML 转成 PDF


    背景

    很多人应该经常遇到在网上看到好的学习教程和资料但却没有电子档的,心里顿时痒痒,
    下述指导一下大家,如何将网站上的各类教程转换成 PDF 电子书。

    关键核心

    • 主要使用的是wkhtmltopdf的Python封装—【pdfkit】

    环境安装

    • python3系列
    • pip install requests
    • pip install beautifulsoup4
    • pip install pdfkit
    • 如果是liunx系,则 sudo yum intsall wkhtmltopdf
    • 如果是windows系,则下载稳定版的 wkhtmltopdf 进行安装,安装完成之后把该程序的执行路径加入到系统环境 $PATH 变量中

    牛刀小试

    一个简单的例子:

    import pdfkit pdfkit.from_url('http://google.com', 'out.pdf') 
    pdfkit.from_file('test.html', 'out.pdf') 
    pdfkit.from_string('Hello!', 'out.pdf')
    

    你也可以传递一个url或者文件名列表:

     pdfkit.from_url(['google.com', 'yandex.ru', 'engadget.com'], 'out.pdf')  
     pdfkit.from_file(['file1.html', 'file2.html'], 'out.pdf')
    

    也可以传递一个打开的文件:

    with open('file.html') as f:         
        pdfkit.from_file(f, 'out.pdf')
    

    实例代码实现

    如将自强学堂中的django教程,生成一个pdf文件

    #coding=utf-8
    from __future__ import unicode_literals
    import os,sys,re,time
    import requests,codecs
    from bs4 import BeautifulSoup
    from urllib.parse import urlparse
    import pdfkit
    import platform
    requests.packages.urllib3.disable_warnings()
    
    system=platform.system()
    print(sys.getdefaultencoding())
    
    str_encode='gbk' if system is 'Windows' else 'utf-8'
    print(str_encode)
    
    html_template = """
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
    {content}
    </body>
    </html>
    
    """
    
    if not os.path.exists(os.path.join(os.path.dirname(__file__),'html')):
        os.mkdir(os.path.join(os.path.dirname(__file__),'html'))
    
    
    url_list=[]
    start_url='http://www.ziqiangxuetang.com/django/django-tutorial.html'
    
    # s=requests.session()
    # html_doc=s.get('{}'.format(start_url),verify=False).content
    
    # soup = BeautifulSoup(html_doc,'html.parser')
    # print(soup.prettify())
    
    def get_url_list(url):
        """
        获取所有URL目录列表
        :return:
        """
        last_position = find_last(url, "/") + 1
        tutorial_url_head = url[0:last_position]
        domain = get_domain(url) + "/"
        print(domain)
    
        response = requests.get(url)
        soup = BeautifulSoup(response.content, "html.parser")
        urls = []
        for a in soup.find_all("a"):
            href = str(a.get('href'))
            result = href.find('/')
            if result == -1:
                url = tutorial_url_head + href
            else:
                url = domain + href
            if 'django' in url:
                urls.append(url)
        return urls
    
    
    def find_last(string, char):
        last_position = -1
        while True:
            position = string.find(char, last_position + 1)
            if position == -1:
                return last_position
            last_position = position
    
    
    def get_domain(url):
        r = urlparse(url)
        return r.scheme + "://" + r.netloc
    
    
    def parse_url_to_html(url,name):
        """
        解析URL,返回HTML内容
        :param url:解析的url
        :param name: 保存的html文件名
        :return: html
        """
        try:
            response = requests.get(url)
            soup = BeautifulSoup(response.content, 'html.parser')
            # 正文
            body = soup.find_all(class_="w-col l10 m12")
            h = str(body)
            html = h[1:-1]
            html = html_template.format(content=html)
            html = html.encode("utf-8")
            title=soup.title.get_text()
            print(url)
            with open('{}/{}'.format(os.path.join(os.path.dirname(__file__),'html'),name), 'wb') as f:
                f.write(html)
            return '{}/{}'.format(os.path.join(os.path.dirname(__file__),'html'),name)
        except Exception as e:
            print(e)
    
    
    def save_pdf(htmls, file_name):
        """
        把所有html文件保存到pdf文件
        :param htmls:  html文件列表
        :param file_name: pdf文件名
        :return:
        """
        options = {
            'page-size': 'Letter',
            'margin-top': '0.75in',
            'margin-right': '0.75in',
            'margin-bottom': '0.75in',
            'margin-left': '0.75in',
            'encoding': "UTF-8",
            'custom-header': [
                ('Accept-Encoding', 'gzip')
            ],
            'cookie': [
                ('cookie-name1', 'cookie-value1'),
                ('cookie-name2', 'cookie-value2'),
            ],
            'outline-depth': 10,
        }
        pdfkit.from_file(htmls, file_name, options=options)
    
    def main():
        start = time.time()
        urls = get_url_list(start_url) 
        htmls = [parse_url_to_html(url, str(index) + ".html") for index, url in enumerate(urls)]
        print(htmls)
        try:
            save_pdf(htmls, 'cralwer_{}.pdf'.format(time.strftime('%Y_%m_%d_%H_%M_%S')))
        except Exception as e:
            print(e)
        for html in htmls:
                os.remove(html)
        total_time = time.time() - start
        print(u"总共耗时:{0:.2f}秒".format(total_time))
    
    main()        
    

    大概思路

    • 先传入一个起始站点的url,本例以自强学堂为例,http://www.ziqiangxuetang.com/django/django-tutorial.html
    • 然后,通过爬虫获取所有含django的url地址,存放在一个列表中,然后再依次获取url,解析各个url中的正文body内容,通过人工分析,各个url正文Body对应的class为w-col l10 m12,所以只需要爬取w-col l10 m12的内容即可。
    • 将获取到的正文内容存放在html文件中,最终返回一个含所有html文件地址的列表htmls。
    • 通过pdfkit.from_file接收一个htmls列表,生成对应pdf文件。

    常见问题

    • IOError: ‘No wkhtmltopdf executable found’
      确保 wkhtmltopdf 在你的系统路径中($PATH),会通过 configuration进行了配置 (详情看上文描述)。 在Windows系统中使用where wkhtmltopdf命令 或 在 linux系统中使用 which wkhtmltopdf 会返回 wkhtmltopdf二进制可执行文件所在的确切位置.

    • IOError: ‘Command Failed’
      如果出现这个错误意味着 PDFKit不能处理一个输入。你可以尝试直接在错误信息后面直接运行一个命令来查看是什么导致了这个错误 (某些版本的 wkhtmltopdf会因为段错误导致处理失败

    • 正常生成,但是出现中文乱码
      在html中加入

    参考

    志军的项目: https://github.com/lzjun567/crawler_html2pdf

    欢迎订阅号

  • 相关阅读:
    元祖操作
    列表操作
    字符串操作
    zabbix监控脚本
    nginx 使用php-fpm的配置php环境参数
    网卡切换脚本
    mysql数据备份脚本
    docker概念
    网络链路质量监控smokeping
    搭建speedtest
  • 原文地址:https://www.cnblogs.com/jinjiangongzuoshi/p/7777771.html
Copyright © 2020-2023  润新知