晒一下知乎Live团队送的新年礼物:
感谢。所以我将送上一系列知乎Live主题的文章
在我的知乎Live里面,我留下了调查问卷,其中一项是可以反馈自己感兴趣的Python相关技术。意料之内,最受大家欢迎的就是爬虫。所以最近开始写了一些和爬虫有关的内容。不过虽然现在还是1月,但是我发誓这是本年最后一次写爬虫文章了(要写吐了)。
模型设计
做知乎Live的全文搜索,首先要抓取到全部Live信息。我尝试了下,只要是登录状态就可以使用知乎提供的API获取。
接口分为2种:
- https://api.zhihu.com/lives/ongoing?purchasable=0&limit=10&offset=10 (未结束)
- https://api.zhihu.com/lives/ended?purchasable=0&limit=10&offset=10 (已结束)
每个Live包含非常多的字段,还包含了一些预留字段。我们简单分析下并实现对应的模型。主讲人模型
主讲人字段如下:
1 |
|
主讲人部分我单独存储,不直接放进elasticsearch的原因有2个:
- 全文搜索和主讲人有关的字段只有主讲人的名字和描述,其他内容没有意义
- 主讲人可能会有多个Live,重复存储浪费空间
所以我还是按着惯例,存在主流数据库中,由于目前Live总体才2k多,数据量太小,且更新机会不多(爬虫抓取是离线的)直接放进了SQLite就好了。
为了未来在数据量变大需要改变存储的数据库时可以灵活切换,我使用提供对象关系映射(ORM)的SQLAlchemy来写模型(models/speaker.py):
1 |
|
speaker_id就是上面API中的speaker[‘member’][‘id’],但是由于它是一个字符串,和常见的主键使用整数不一样(虽然也可以),所以多加一个字段存储。另外额外的写了一个add方法,相当于创建之前通过speaker_id先检查下库中是否存在对应条目。
Live模型
Live字段比较多,为了未来字段格式改变造成文本不可用,得列一下:
1 |
|
这些字段通过名字能比较清晰的了解用途。接着我们考虑用什么elasticsearch的Python客户端。官方提供了elasticsearch-
py 这个低级别客户端。
最开始我使用了elasticsearch-
py,所谓低级别,就是各种操作未做或者少做封装,比如搜索用到的参数就要手动拼成一个字典。相信如果你之前了解或者用过elasticsearch,就会知道它的搜索参数多的令人发指。如果业务需求比较简单,elasticsearch-
py还是可满足的。随着需求变多变复杂,你会发现拼这样一个多键值、多层嵌套字典的逻辑变得越来越不可维护,且写错了不容易发现,如果你对业务和elasticsearch不熟悉,非常容易掉坑儿。
那有没有什么更好写搜索和模型的方式嘛?
官方提供了基于elasticsearch-py的高级别客户端elasticsearch-dsl-
py。DSL是领域专用语言(Domain
Specific Language)的简称,也就是专门针对某一特定问题的计算机语言,特点是「求专不求全」。elasticsearch-dsl-
py就是针对elasticsearch的特定语言。
它允许我们用一种非常可维护的方法来组织字典:
1 |
|
允许用一种函数式的方法来做查询:
1 |
|
上述例子表示从live这个索引(类似数据库中的Database)中找到subject字典包含python,但是description字段不包含量化的Live。
当然这个DSL也支持用类代表一个doc_type(类似数据库中的Table),实现ORM的效果。我们就用它来写Live模型:
1 |
|
爬虫
在之前我已经分享了一个功能比较齐备的基于aiphttp的爬虫实现,还分享过如何使用API登录知乎并获得token。今天用之前的积累,实现这个爬虫。由于之前已经展示用完整代码,今天只介绍如何让它们「串」起来。
首先是初始化部分:
1 |
|
初始化的时候通过add_url添加2种API、共2倍max_tasks个url。由于aiohttp的auth参数不支持原来requests的ZhihuOAuth,所以添加一个值为空字典的self.headers方便生成包含知乎API请求需要的headers。
知乎API设计的不错,返回的结果中包含了上一页和下一页的地址和是否结束:
1 |
|
所以parse_link的结果也就是只是一个url了。我把存储爬取下来的Live和主讲人信息的方法也放进了这个方法:
1 |
|
其中flatten_live_dict是从嵌套的字典里面把需要的键值抽出来:
1 |
|
这个函数对id做了特殊处理,是因为在flatten过程中,有多个id会被无情替换,所以额外处理了。
其中参数keys分2种:
1 |
|
这样就完成了一个爬虫。我没有添加爬虫的断点续爬(发现之前爬过停止抓取)功能。这是因为Live一般在结束后都还是可以有人参与,去评价。反正总量也就是一分钟抓取完而已。如果想要添加这种减少重复抓取的功能,最简单的方式如下:
1 |
|
版权声明:本文由 董伟明 原创,未经作者授权禁止任何微信公众号和向掘金(juejin.im)转载,技术博客转载采用 保留署名-非商业性使用-禁止演绎 4.0-国际许可协议
python