一键下载:将知乎专栏导出成电子书
老是有同学问,学了 Python 基础后不知道可以做点什么来提高。今天就再用个小例子,给大家讲讲,通过 Python 和爬虫,可以完成怎样的小工具。
在知乎上,你一定关注了一些不错的专栏(比如 Crossin的编程教室
)。但万一有那么一天,你喜欢的答主在网上被人喷了,一怒之下删帖停更,这些好内容可就都看不到了。尽管这是小概率事件(可也不是没发生过),但未雨绸缪,你可以把关注的专栏导出成电子书,这样既可以离线阅读,又不怕意外删帖了。
只是需要工具和源码的可以拉到文章底部获取代码。
【最终效果】
运行程序,输入专栏的 id,也就是网页地址上的路径:
之后程序便会自动抓取专栏中的文章,并按发布时间合并导出为 pdf 文件。
【实现思路】
这个程序主要分为三个部分:
- 抓取专栏文章地址列表
- 抓取每一篇文章的详细内容
- 导出 PDF
1. 抓取列表
在之前的文章 爬虫必备工具,掌握它就解决了一半的问题 中介绍过如何分析一个网页上的请求。按照其中的方法,我们可以通过开发者工具的 Network 功能找出专栏页面获取详细列表的请求:
https://www.zhihu.com/api/v4/columns/crossin/articles
观察返回结果中发现,通过 next
和 is_end
的值,我们能获取下一次列表请求的地址(相当于向下滚动页面的触发效果)以及判断是否已经拿到所有文章。
而 data
中的 id
、title
、url
就是我们需要的数据。因为 url
可以通过 id
拼出,所以我们的代码里未保存它。
使用一个 while 循环,直到抓取完所有文章的 id
和 title
,保存在文件中。
while True:
resp = requests.get(url, headers=headers)
j = resp.json()
data = j['data']
for article in data:
# 保存id和title(略)
if j['paging']['is_end']:
break
url = j['paging']['next']
# 按 id 排序(略)
# 导入文件(略)
2. 抓取文章
有了所有文章的 id
/ url
,后面的抓取就很简单了。文章主体内容就在 Post-RichText
的标签中。
需要稍微花点功夫的是一些文本上的处理,比如原页面的图片效果,会加上 noscript
标签和 data-actual
、src="data:image
这样的属性,我们为了正常显示得把它们去掉。
url = 'https://zhuanlan.zhihu.com/p/' + id
html = requests.get(url, headers=headers).text
soup = BeautifulSoup(html, 'lxml')
content = soup.find(class_='Post-RichText').prettify()
# 对content做处理(略)
with open(file_name, 'w') as f:
f.write(content)
到这一步,就已经完成了所有内容的抓取,可以在本地阅读了。
3. 导出 PDF
为了更便于阅读,我们使用 wkhtmltopdf + pdfkit,将这些 HTML 文件打包成 PDF。
wkhtmltopdf 是一个 HTML 转 PDF 的工具,需要单独安装,具体可参考它的官网介绍。
- https://wkhtmltopdf.org/downloads.html
- https://github.com/JazzCore/python-pdfkit/wiki/Installing-wkhtmltopdf
pdfkit 是对此工具封装的 Python 库,可从 pip 安装:
pip install pdfkit
使用起来很简单:
# 获取htmls文件名列表(略)
pdfkit.from_file(sorted(htmls), 'zhihu.pdf')
这样就完成了整个专栏导出。
不仅是知乎专栏,几乎大多数信息类网站,都是通过 1.抓取列表 2.抓取详细内容 这两个步骤来采集数据。因此这个代码稍加修改,即可用在很多别的网站上。只不过有些网站需登录后访问,那么就需要对 headers 里的 cookie 信息进行设置。此外,不同网站的请求接口、参数、限制都不尽相同,所以还是要具体问题具体分析。
关于这些爬虫的开发技巧,都可以在我们的 爬虫实战 课程中学到。有需要的请在公众号里回复 爬虫实战
【源码下载】
获取知乎专栏下载器源码,请在公众号(Crossin的编程教室)里回复关键字 知乎
除了代码外,本专栏打包好的 PDF 也一并奉上,欢迎阅读与分享。
- idea 创建的maven+spring+mybatis项目整合 报错无法创建bean
- 代数几何:点,线,抛物线,圆,球,弧度和角度
- 被解放的姜戈05 黑面管家
- 用数据来告诉你2018年的未来趋势
- JavaWeb(三)JSP之3个指令、6个动作、9个内置对象和4大作用域
- 被解放的姜戈03 所谓伊人
- JS魔法堂: Native Promise Only源码剖析
- JavaWeb(三)JSP概述
- 人工智能拥有意识,仅是一个时间问题而已
- sqlserver 配置c3p0 连接池
- spring mvc 返回图片的请求
- JavaWeb(二)cookie与session的应用
- JS魔法堂:函数重载 之 获取变量的数据类型
- 开发问题(一)在windows和linux端口占用问题
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Android获取清单文件中的meta-data,解决碰到数值为null的问题
- android studio 打包自动生成版本号与日期,apk输入路径详解
- 详解关于AndroidQ获取不到imsi解决方案
- 解决webview 第二次调用loadUrl页面不刷新的问题
- 浅谈 Android 7.0 多窗口分屏模式的实现
- Kotlin中的对象表达式和对象声明的具体使用
- Android 实现为点击事件添加震动效果
- 解决Android studio Error:(30, 31) 错误: 程序包 不存在的问题
- AndroidQ 沙箱适配多媒体文件(小结)
- Bitcron 主题 Berry(简约wordpress主题)
- Android 实现图片转二进制流及二进制转字符串
- Android 如何实现exclude aar包中的某个jar包
- android 实现控件左右或上下抖动教程
- Android Studio引入FFmpeg的方法
- Android Studio 中获取屏幕宽度实例