1. 系列文章

  1. 网页爬虫第一课:从案例解构爬虫基本概念
  2. 填坑18年:我总结的CSS选择器
  3. 爬虫数据持久化方式的选择
  4. 爬取静态博客网页以分析本网站拓扑结构
  5. python程序的性能测试及瓶颈分析
  6. Python工程项目的规范开发指南

2. 缘起

kindle中国在2024-06-30彻底关闭之后, 用户购买的电子书甚至都不能下载了. 太可恶了! 西方发达资本主义国家的老板们, 良心真是大大滴坏~ 资本家们算是指望不上了, 咱们还是积极自救吧… 瞅着将近一千本的电子书, 真是欲哭无泪啊 😒😭

还好有大神从天而降, 发明了神器: Kindle_download_helper. 将这个开源程序作为第一个研究对象. 对于这个工具的试用方法可以参见这篇教程: 一键批量下载 Kindle 全部电子书工具 + 移除 DRM 解密插件 + 格式转换教程 (开源免费).

对了, 万一要是没有大神怎么办? 一千本电子书如果手动下载, 一本书1分钟, 1000分钟就是17个小时, 我擦~ 一天花17个小时搞这破事儿, 太不划算了. 不行, 我也要学会爬虫. 虽然说 “爬虫玩的好, 牢饭吃到饱”, 但是咱可以偷偷摸摸地玩嘛~ 🤣 说干就干, 这篇就借着Kindle_download_helper的东风, 咱们把这样一个可以实用的爬虫程序解构一下, 拆解出 “一个完整的爬虫程序, 需要哪些步骤和技术”.

What is Web Crawler Web Spider Web Crawling Web Scraping Crawler Spider Bot

3. 代码框架解读

3.1. 主要功能

我从Kindle_download_helper的核心功能”只下载一本kindle电子书”这个功能讲起. 其中涉及到的程序文件主要有:

  1. cli.py
  2. kindle.py

这两个文件中主要实现了如下功能:

  1. build_options
  2. get_all_books
  3. download_books_by_options
  4. download_all_books
  5. download_selected_books
  6. download_one_book

3.2. 代码流程

其流程大致如下:

上面流程图中的功能对应的代码实现如下表所示:

功能 gist(代码片段)
build_options cli.build_options.py
get_all_books kindle.get_all_books_func.py
download_books_by_options cli.download_books_by_options.py
download_all_books kindle.download_all_books_func.py
download_selected_books kindle.download_selected_books_func.py
download_one_book kindle.download_one_book_func.py

相关的代码片段如下文.

3.3. build_options

3.4. get_all_books

3.5. download_books_by_options

3.6. download_all_books

3.7. download_selected_books

3.8. download_one_book

4. 重要细节

4.1. 主要技术

在这个程序中, 关键的两个函数是: get_all_booksdownload_one_book: 先用get_all_books获取用户所有书籍的信息列表, 然后用download_one_book逐个地下载电子书文件. Kindle_download_helper项目不涉及页面解构的解析, 只涉及对相关资源的请求和下载, 因此重点需要放在URI构造和资源请求.

URI构造

kindle.get_all_books_func.py中, 涉及URI构造的代码如下:

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
startIndex = start_index
batchSize = 100
payload = {
"param": {
"OwnershipData": {
"sortOrder": "DESCENDING",
"sortIndex": "DATE",
"startIndex": startIndex,
"batchSize": batchSize,
"contentType": CONTENT_TYPES[filetype],
"itemStatus": ["Active"],
}
}
}

if filetype == "EBOK":
payload["param"]["OwnershipData"].update(
{
"originType": ["Purchase"],
}
)
else:
batchSize = 18
payload["param"]["OwnershipData"].update(
{
"batchSize": batchSize,
"isExtendedMYK": False,
}
)

资源请求

而在kindle.get_all_books_func.py中, 根据构造完成的URI进行资源请求, 代码如下:

1
2
3
4
r = self.session.post(
self.urls["payload"],
data={"data": json.dumps(payload), "csrfToken": self.csrf_token},
)

其中的payload即为构造URI的用户信息.

4.2. 工程细节

除了上述的主要技术部分, 在应用时为了应对特殊的实际情况, 往往需要用一些小trick. 譬如:

  1. kindle.get_all_books_func.py中, 请求资源时可能被亚马逊官网reject, 此时作者进行了3次资源请求.
  2. 作者在同时实现了命令行和GUI两个版本, 分别是根目录下的kindle.pykindle_gui.py两个文件. 其中:
    • kindle.py是入口, 而命令行工具的功能实现在文件Kindle_download_helper下的cli.py文件;
    • kindle_gui.py使用的QT作为GUI库. 这个GUI窗口只是对cli.py中命令工具套的壳. 在学习kindle_gui.py时, 我应该重点学习它对`cli.py`中底层功能函数的组织方法.

小结与展望

通过Kindle_download_helper中, 我发现网页爬虫的核心概念只有两个:

  1. 构造资源定位符URI;
  2. 请求资源(request.postrequest.get).

此外, Kindle_download_helper让我学到了一个以前不曾尝试过的编写小工具的方式: 命令行 + 套壳GUI. 以后对于我自己编写的比较成熟的命令行工具, 我也要试着尝试用这种方式来完善自己的代码武器库.

在本系列文章的下一篇中, 我将以自己的博客站点作为试验对象:

  1. 爬取博客网站上的文章;
  2. 提取其元数据和正文内容;
  3. 并将爬虫采集到的内容在硬盘上做轻量级的持久化存储.

更进一步, 作为成果展示, 我将利用从博客网站爬取到的网页数据, 绘制本博客站点的拓扑结构.

我希望这个采集工具能进一步拓展成为针对静态博客站点的综合分析工具: 包括网站结构分析, 博客内容的分析, 对博主的用户画像. 当然, 实现这些功能仅仅利用爬虫技术是远远不够的. 到时候, 我也将该工具以'命令行+套壳GUI'的形式发布出来.