用Python來統計知識星球打卡作業

Python之禪發表於2018-06-06

640?wx_fmt=jpeg

題圖:Photo by Ian Simmonds on Unsplash

標題叫“用Python批改知識星球作業”,感覺太標題黨了,所以換了個詞,不過等AI更強大點是有可能做到的。我們們知識星球,每週都要統計大家的作業完成情況與打卡次數,因為知識星球沒有給星主提供運營統計資料,所以,我只能自己動手解決,特別推薦產品和運營人員學點程式設計,懂點爬蟲,因為網際網路人都是靠資料說話的。

思路分析和擼程式碼總共花了1個小時,但是寫這篇文章卻花了我4個小時。現在知道程式設計師為什麼寧可多寫幾行程式碼也不願意寫文件了吧。寫文章真不容易,聽說公眾號的讚賞功能又回來了,以後又有動力更新文章了。

我們的目標是統計出最近一週在星球裡的打卡與作業完成情況,所以我們先要想辦法拿到資料,再對資料進行統計分析。因為知識星球提供了 PC 瀏覽器版本,資料的抓取我們直接從 Chrome 瀏覽器找入口。

第一步:思路分析

爬蟲獲取資料就是利用程式模擬瀏覽器發起網路請求,將資料採集回來,所以,我們先來分析網路請求在瀏覽器裡面是怎樣的。微信掃描登入知識星球 https://wx.zsxq.com/dweb/ 後, 瀏覽器右鍵「檢查」,開啟開發者模式選擇 「Network」就可以看到瀏覽器發出的每個網路請求,選擇你要進行統計的圈子,你會看到有很多請求。

640?wx_fmt=png

這些請求全部是和該圈子相關的,在這個階段首先你要對整個往頁的資料有個大概瞭解,比如在該頁面提供的功能有圈子的基本介紹、星主的基本資訊、中間是帖子列表,左側是圈子列表,此時你需要根據每個請求的返回結果做出判斷

groups 請求的資料對應頁面左邊的圈子列表。

640?wx_fmt=png

topics?count=20 正是我們要找的帖子資料的請求介面

640?wx_fmt=png


找到了獲取資料的請求介面後,我們先來對返回的資料結構瞭解一下

{
   "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

現在獲取資料的介面找到了,儲存資料的方案也確定了,可以正式開始擼程式碼實現了。先來確定如果我們用程式碼來模擬瀏覽器傳送獲取帖子資料的請求,我們需要提供給哪些請求資料。

640?wx_fmt=png


再來詳細看這個請求的細節資訊,確定了完整的 url 和請求方法 GET,以及很重要的請求頭資訊。頭資訊我們把它封裝成字典放在get方法中。

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))

我使用遞迴的方式將這個圈子裡面所有的帖子全部爬下來。

640?wx_fmt=png

第三步:資料統計

資料拿到了,現在正是可以進入分析統計階段了。

我們需要用到 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 檔案,展示出來的效果是:

640?wx_fmt=jpeg


完整程式碼可在公眾號回覆“xingqiu”獲取

640?wx_fmt=jpeg


相關閱讀:

相關文章