题图来自于NextDay
接上篇(参见 先实现自己的一个小需求),简单概括一下:省去从书单文章中手动 Copy-Paste 的动作,查看豆瓣评价,查看 Kindle Unlimited 中是否有相应的版本。
今天简单介绍一下具体如何操作。用到的还是 如何用 Python 写一个最简单的爬虫 中的老朋友 Python 的 Beautifulsoup4 库(本文不再介绍如何使用 BS4)
在开始之前,我们回顾一下步骤:
从文章中爬取书名:以《》为关键词,用到「正则表达式」知识
找到豆瓣和 Kindle Unlimited 上该书的搜索页面:以拼接 URL 的方式实现
将结果生成一个 HTML 页面:生成 Excel 文件作为知识拓展。
可以以这篇为例:这100本重磅好书,2月免费读!(内有福利)
首先我们定义一个 List,用来保存所有书名:
lst_book = []
抓取书名的核心代码(网页全文用正则表达式匹配书名号)
lst_og_tx = soup.find_all(recursive=True,text=re.compile('《(.*?)》'))
考虑到书名号括起来的不一定是书籍,所以我们需要把原文内容保留,供人工作为参考。这里需要注意的是「1对多」的关系,因为书名已经去重,那书名是 1,但可能有多段原文中提到同一本书,我们都需要保留下来。
可以用 Key-Value 的方式建立一个「书名->N条原文 List」的匹配关系。我们这里采用最原始的方法,存两个 List,一个 List 用来存放未去重的书名清单,另一个用来存放获取的原文内容,一对一比例地存放,比如这段原文中出现了3个书名,那么在原文 List 里我们就存放三次。
# lst_book_duplicate 用来存放未去重的书名数组;
# lst_og 用来存放原文内容
for p in lst_og_tx:
p1 = re.compile('《(.*?)》')
p2 = p1.findall(p)
lst_book_duplicate.extend(p2)
for i in range(len(p2)):
lst_og.append(p)
取到的书名 List 需要做一个去重的操作(注意需要保持原有的排序顺序)
def _sort_list(lst):
new_lst = []
new_lst = list(set(arr))
new_lst.sort(key=lst.index)
return new_lst
lst_book = _sort_list(lst_book_duplicate)
书名有了,接下来就是第二步拼接 URL 了,因为数据我还有其他用途,所以先统一转换成 JSON 格式。
def _lst_to_json(lst,lst_d,lst_o):
new_lst=[]
for tmp in lst:
obj={}
obj['序号']=lst.index(tmp)+1
obj['书名']=tmp
#用 favicon 显示比较醒目一点,也可以用文字代替
obj['豆瓣'] = '<a href="'+db_query_url+tmp+'"><img src="https://www.douban.com/favicon.ico" height="30px" /></a>'
obj['KU'] = '<a href="'+ku_query_url+tmp+'"><img src="https://www.amazon.cn/favicon.ico" height="30px" /></a>'
obj['京东'] = '<a href="'+jd_query_url+tmp+'"><img src="https://www.jd.com/favicon.ico" height="30px" /></a>'
obj['原文'] = lst_o[lst_d.index(tmp)]
new_lst.append(obj)
json_data = json.dumps(new_lst, ensure_ascii=False)
return json_data
_lst_to_json(lst_book,lst_book_duplicate,lst_og)
Question 1:豆瓣、Kindle Unlimited、京东,搜索某本书的 URL 你能找出来吗?1
接下来,就是最后一步了,生成我们想要的 HTML 文件,格式如下:
我们可以用 HTML 语法, 手工生成 Table 标签。这里有个讨巧的地方,前面我们已经有了一个 JSON 文件,那么也利用 Python 的一个第三方库来轻松实现:
html_content = json2html.convert(json = json_data)
但需要注意的是,一些特殊字符,比如 < > / “ 会被转义掉,需要再转回来。
最后,我们把生成好的 HTML 内容保存到文件:
with open(d_tool._genRandomStr(6)+".html", "wb") as file:
file.write(bytes(html_content,encoding='utf-8'))
这样,一个可以快速定位到豆瓣和 KU 的书单就生成好了,是不是很简单?本身需求也很简单,只是替代手工 Copy-Paste,如果还需要自动去标记豆瓣想读,涉及到登录等一系列的操作,后面有机会再聊。
总结一下,通过这样一个小例子,我们学到了 Python 的哪些知识点:
BeautifulSoup4 中如何使用正则表达式
List 去重并保留原有顺序
List 转 JSON
JSONArray 转 HTML Table
写入文件
用到的 Python 库:
from bs4 import BeautifulSoup
import requests
import re
import json
from json2html import *
一切需求,只有当真正开始动手,你才会意识到,这个需求究竟是大还是小,复杂还是简单,做的过程中也可能衍生出其他需求,变成 冬日里的一次 Yak Shaving 类似的情境。对于初学者尤其明显,当经验丰富之后,评估才会比较准确。
所以,对于「实现一个功能」这件事,不要「掉以轻心」,但也不要「闻风丧胆」,尝试用一种探索的心态去实现一个实际的需求就好。
人生苦短,快学 Python ( ̀⌄ ́)
db_query_url='https://www.douban.com/search?cat=1001&q='
ku_query_url='https://www.amazon.cn/s?i=digital-text&node=1337022071&k='
jd_query_url='https://search.jd.com/Search?stock=1&keyword=' ↩
发送给作者