Henry的博客

有理想的人,生活总是火热的

欢迎来到Henry的博客,希望与您在iOS开发领域共同交流与学习


Python实践一、将网页转换为pdf电子书

本人是个”收集狂”(不要想歪了哈,我只是喜欢收藏技术贴),遇到好的东西就喜欢收藏或记录下来,尤其是好的技术文章或者工具。这里就要提到廖雪峰老师的官方网站了,廖老师写的Python、JavaScript、Git教程真心好呀,每每都要去逛逛。于是就有了今天这个需求,把廖老师的教程由网页转为PDF电子书,这样就可以随时随地离线学习加收藏了。说到这里进入今天的主题Python爬虫实践:将网页转换为pdf电子书

写爬虫似乎没有比用 Python 更合适了,Python 社区提供的爬虫工具多得让你眼花缭乱,各种拿来就可以直接用的 library 分分钟就可以写出一个爬虫出来,今天就琢磨着写一个爬虫,将廖雪峰的 Python 教程 爬下来做成 PDF 电子书方便离线阅读。

开始写爬虫前,我们先来分析一下该网站的页面结构,网页的左侧是教程的目录大纲,每个 URL 对应到右边的一篇文章,右侧上方是文章的标题,中间是文章的正文部分,正文内容是我们关心的重点,我们要爬的数据就是所有网页的正文部分,下方是用户的评论区,评论区对我们没什么用,所以可以忽略它。

工具准备

弄清楚了网站的基本结构后就可以开始准备爬虫所依赖的工具包了。requests、beautifulsoup 是爬虫两大神器,reuqests 用于网络请求,beautifusoup 用于操作 html 数据。有了这两把梭子,干起活来利索,scrapy 这样的爬虫框架我们就不用了,小程序派上它有点杀鸡用牛刀的意思。此外,既然是把 html 文件转为 pdf,那么也要有相应的库支持, wkhtmltopdf 就是一个非常好的工具,它可以用适用于多平台的 html 到 pdf 的转换,pdfkit 是 wkhtmltopdf 的Python封装包。首先安装好下面的依赖包,接着安装 wkhtmltopdf

1
2
3
4
pip install requests
pip install beautifulsoup4
pip install pdfkit
pip install PyPDF2

安装 wkhtmltopdf

Ubuntu 和 CentOS 可以直接用命令行进行安装。

1
2
$ sudo apt-get install wkhtmltopdf # ubuntu
$ sudo yum intsall wkhtmltopdf # centos

Windows平台直接在 wkhtmltopdf 官网2下载稳定版的进行安装,安装完成之后把该程序的执行路径加入到系统环境 $PATH 变量中,否则 pdfkit 找不到 wkhtmltopdf 就出现错误 No wkhtmltopdf executable found 这里就要多说几句了,因为这里处理不好,程序执行pdfkit.from_file(htmls, file_name, options=options)的时候就会报错。

现在开始手动安装wkhtmltopdf(博主电脑操作系统是 macOS 10.12.2)

1、去官网下wkhtmltopdf。下载完成运行wkhtmltox-0.12.4_osx-cocoa-x86-64.pkg

2、把wkhtmltoimage和wkhtmltopdf复制到/usr/bin目录,更改所有者,并增加可执行属性

1
2
3
4
5
6
sudo cp /usr/local/bin/wkhtmltopdf /usr/bin/
sudo cp /usr/local/bin/wkhtmltoimage /usr/bin/
sudo chown root:root /usr/bin/wkhtmltopdf
sudo chown root:root /usr/bin/wkhtmltoimage
sudo chmod +x /usr/bin/wkhtmltopdf
sudo chmod +x /usr/bin/wkhtmltoimage

不出意外你在执行第一句的时候就会遇到chmod:Unable to change file modle on /usr/bin这是因为苹果从 OS X El Capitan 10.11 系统开始使用了 Rootless 机制,可以将该机制理解为一个更高等级的系统的内核保护措施,系统默认将会锁定 /system、/sbin、/usr 这三个目录。

关闭Rootless

关闭和开启 Rootless 非常简单,方法如下:重启 Mac,听到开机启动声后按下 Command+R,进入恢复模式,在上面的菜单实用工具中找到并打开 Terminal(如果顶部没出现菜单,请继续重启^_^)。输入如下命令:

1
2
$ csrutil disable #关闭 Rootless
$ csrutil enable #开启 Rootless

OK,到此我们工具及环境配置好了,下面开始实现功能。

爬虫实现

一切准备就绪后就可以上代码了,不过写代码之前还是先整理一下思绪。程序的目的是要把所有 URL 对应的 html 正文部分保存到本地,然后利用 pdfkit 把这些文件转换成一个 pdf 文件。我们把任务拆分一下,首先是把某一个 URL 对应的 html 正文保存到本地,然后找到所有的 URL 执行相同的操作。用 Chrome 浏览器找到页面正文部分的标签,按 F12 找到正文对应的 div 标签:<div class="x-wiki-content">,该 div 是网页的正文内容。用 requests 把整个页面加载到本地后,就可以使用 beautifulsoup 操作 HTML 的 dom 元素 来提取正文内容了。

具体的实现代码如下:用 soup.find_all 函数找到正文标签,然后把正文部分的内容保存到 a.html 文件中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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_="x-wiki-content")[0]
# 标题
title = soup.find('h4').get_text()
# 标题加入到正文的最前面,居中显示
center_tag = soup.new_tag("center")
title_tag = soup.new_tag('h1')
title_tag.string = title
center_tag.insert(1, title_tag)
body.insert(1, center_tag)
html = str(body)
# body中的img标签的src相对路径的改成绝对路径
pattern = "(<img .*?src=\")(.*?)(\")"
def func(m):
if not m.group(3).startswith("http"):
rtn = m.group(1) + baseUrl + m.group(2) + m.group(3)
return rtn
else:
return m.group(1)+m.group(2)+m.group(3)
html = re.compile(pattern).sub(func, html)
html = html_template.format(content=html)
html = html.encode("utf-8")
with open(name, 'wb') as f:
f.write(html)
return name
except Exception as e:
logging.error("解析错误", exc_info=True)

第二步就是把页面左侧所有 URL 解析出来。采用同样的方式,找到 左侧菜单标签

    具体代码实现逻辑:因为页面上有两个uk-nav uk-nav-side的 class 属性,而真正的目录列表是第二个。所有的 url 获取了,url 转 html 的函数在第一步也写好了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def get_url_list():
    """
    获取所有URL目录列表
    :return:
    """
    response = requests.get(htmlUrl)
    soup = BeautifulSoup(response.content, "html.parser")
    menu_tag = soup.find_all(class_="uk-nav uk-nav-side")[1]
    urls = []
    for li in menu_tag.find_all("li"):
    url = baseUrl + li.a.get('href')
    urls.append(url)
    return urls

    最后一步就是把 html 转换成pdf文件了。转换成 pdf 文件非常简单,因为 pdfkit 把所有的逻辑都封装好了,你只需要调用函数 pdfkit.from_file

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    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)

    执行 save_pdf 函数,电子书 pdf 文件就生成了:

    程序执行图

    生成PDF文件

    点击下载源码

    最近的文章

    Python实践二、数据分析

    写这篇文章的起因是我一个朋友的女友发表博士论文中要对采集的数据进行分析拟合,刚好这段时间工作不忙,在研究Python,所以就去折腾折腾。下面是两个需求: 12345需求一:根据公式计算MVD、MDNMND计算公式:[Σ(Bin Diameter*Num)]/ΣnumMVD计算公式:ΣVolum/Σn …

    于  Python 继续阅读
    更早的文章

    iOS开发-使用Cocoapods创建私有podspec

    Cocoapods是非常好用的一个iOS依赖管理工具,使用它可以方便的管理和更新项目中所使用到的第三方库,以及将自己的项目中的公共组件交由它去管理。制作共有podSpec可以参考我博客园里另一篇文章上传代码到cocoapod ,自己的框架提供给开发者使用。其实共有和私有的区别就在于对podSpec的 …

    于  iOS开发 继续阅读