赞
踩
题图:Photo by Ian Simmonds on Unsplash
标题叫“用Python批改知识星球作业”,感觉太标题党了,所以换了个词,不过等AI更强大点是有可能做到的。咱们知识星球,每周都要统计大家的作业完成情况与打卡次数,因为知识星球没有给星主提供运营统计数据,所以,我只能自己动手解决,特别推荐产品和运营人员学点编程,懂点爬虫,因为互联网人都是靠数据说话的。
思路分析和撸代码总共花了1个小时,但是写这篇文章却花了我4个小时。现在知道程序员为什么宁可多写几行代码也不愿意写文档了吧。写文章真不容易,听说公众号的赞赏功能又回来了,以后又有动力更新文章了。
我们的目标是统计出最近一周在星球里的打卡与作业完成情况,所以我们先要想办法拿到数据,再对数据进行统计分析。因为知识星球提供了 PC 浏览器版本,数据的抓取我们直接从 Chrome 浏览器找入口。
爬虫获取数据就是利用程序模拟浏览器发起网络请求,将数据采集回来,所以,我们先来分析网络请求在浏览器里面是怎样的。微信扫描登录知识星球 https://wx.zsxq.com/dweb/ 后, 浏览器右键「检查」,打开开发者模式选择 「Network」就可以看到浏览器发出的每个网络请求,选择你要进行统计的圈子,你会看到有很多请求。
这些请求全部是和该圈子相关的,在这个阶段首先你要对整个往页的数据有个大概了解,比如在该页面提供的功能有圈子的基本介绍、星主的基本信息、中间是帖子列表,左侧是圈子列表,此时你需要根据每个请求的返回结果做出判断
groups 请求的数据对应页面左边的圈子列表。
topics?count=20 正是我们要找的帖子数据的请求接口
- {
- "topic_id": 48551524482128,
- "group": {
- "group_id": 518855855524,
- "name": "Python之禅和朋友们"
- },
- "type": "talk",
- "talk": {
- "owner": {
- "user_id": 15551441848112,
- "name": "叶宪",
- "avatar_url": "https://file.zsxq.19.jpg"
- },
- "text": "我尝试了一下,8位0-9纯数字的MD5暴力破解花了约140秒。"
- },
- "likes_count": 0,
- "comments_count": 0,
- "rewards_count": 0,
- "digested": false,
- "sticky": false,
- "create_time": "2018-06-05T23:39:38.197+0800",
- "user_specific": {
- "liked": false,
- "subscribed": false
- }
- }

根据接口返回的结果,分析得出每次请求返回的结果包含了20条帖子数据,每条帖子的数据结构也非常清晰,type 表示帖子的类型,talk 是普通的帖子,还有一种叫 solution,表示作业,talk 字段里面指定了发帖者的信息,和所发的内容,还有创建时间。这是一个嵌套的json 字典结构,用 MongoDB 来直接存储这些数据是最方便的,不需要构建 Schema,直接作为一个文档(json)存到数据库就可以,方便后面根据条件进行过滤分组统计。
思路清晰后,写代码其实是很快的,Mongodb 的安装这里就不介绍了,参考网上的教程可以解决。 只需要两个依赖库就可以搞定。
- pip install pymongo
- pip install requests
现在获取数据的接口找到了,存储数据的方案也确定了,可以正式开始撸代码实现了。先来确定如果我们用代码来模拟浏览器发送获取帖子数据的请求,我们需要提供给哪些请求数据。
- def crawl():
- url = "https://api.zsxq.com/v1.10/groups/518855855524/topics?count=20"
- res = requests.get(url, headers=headers) # get 请求
- topics = res.json().get("resp_data").get("topics")
- for i in topics:
- print(i.get("talk").get("text")[:10])
- db.topics.insert_one(i)
现在你还只是获取了前20条数据,要想获取所有的帖子,还需要分页查询,这时你需要使用浏览器加载更多数据来查看请求里面的分页参数是什么。你会发现它是使用上一次请求返回的数据中最后一条帖子的创建时间作为分页参数 end_time 象服务器获取的,所以我们把代码改成:
- def crawl(url):
- res = requests.get(url, headers=str_to_dict(headers))
- topics = res.json().get("resp_data").get("topics")
- if len(topics) <= 1:
- return
- for i in topics:
- print(i.get("talk").get("text")[:10])
- db.topics.insert_one(i)
- else:
- last_time = i.get("create_time")
- crawl("https://api.zsxq.com/v1.9/groups/518855855524/topics?count=20" + "&end_time=" + parse.quote(last_time))
我使用递归的方式将这个圈子里面所有的帖子全部爬下来。
数据拿到了,现在正是可以进入分析统计阶段了。
我们需要用到 MongoDB 的聚合功能,根据每个人的发帖数进行分组排名,并指定匹配查询条件(我查询的是时间大于某个指定的日期),有人说,是不是我还需要先去学完 MongoDB 才能做统计了。其实也不,你可以借用强大的搜索引擎来帮助你怎么做这这种复杂的操作。
话说回来,你还是要对MongoDB有基本的了解和掌握简单的操作,快速入门后才知道怎么去搜索你要的答案,否则也是无从下手。
- def statics():
- # 打卡
- talk = db.topics.aggregate(
- [
- {"$match": {"create_time": {"$gte": "2018-05-28T00:00:14.202+0800"}}},
- {
- "$group": {
- "_id": {
- "user_id": "$talk.owner.user_id",
- "name": "$talk.owner.name",
- },
- "count": {"$sum": 1},
- }
- },
- {"$sort": {"count": -1}},
- ]
- )

这是我根据刷选条件,根据帖子的创建时间大于等于指定时间,再根据发帖者的id和名字进行分组统计,最后按照降序进行排列。 type 为 solution 的作业帖子也使用同样的方式,即可统计出来。最终写入 cvs 文件,展示出来的效果是:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。