使用Python快速獲取公衆號文章定製電子書(二)

原文連接html

個人GitHub博客地址python

接上篇文章使用Python快速獲取公衆號文章定製電子書(一)。咱們如今已經成功的將公衆號歷史消息的前十條文章給爬取了出來,使用 content_url 這個關鍵字段,咱們即可以輕易的獲取文章具體內容,並將文章保存到本地文件中。實際上上面這些東西已是咱們實現爬取公號文章的核心功能了,剩下的就是如何經過某種方式將公衆號的全部文章一次性爬取出來。android

咱們用手機在公衆號的歷史消息界面中做上拉加載操做,在 Charles 中爬取接口,像以前同樣,咱們經過 response 找到了咱們須要的接口,此次的接口返回很漂亮,是一個 JSON 數據。git

看返回,咱們發現 general_msg_list 字段相似於上篇文章中的 msgList 的內容,裏面是十條文章的數據列表,具體的處理方式和以前也大差不差,並且這裏自己就是 JSON 返回,處理起來顯然更容易。github

咱們來看看這個請求接口的 URL 形式:正則表達式

url ="https://https://mp.weixin.qq.com/mp/profile_ext?" \
         "action=getmsg&" \
         "__biz=MjM5ODIyMTE0MA==&" \
         "f=json&" \
         "offset=11&" \
         "count=10&" \
         "is_ok=1&" \
         "scene=124&" \
         "uin=777&" \
         "key=777&" \
         "pass_ticket=Pu%2FH3aPR7f%2FzgA52T%2Bv4fU9wSWkY5tgGGgAXWewji2dqpMDrjaxUbBR%2Fmo2e%2FaMX&" \
         "wxtoken=&" \
         "appmsg_token=956_cnSiifKearMa6Um6cS3fcmXnu1AfKSYN5dSOSA~~&" \
         "x5=1&" \
         "f=json"
複製代碼

offset這個字段咱們劃重點,它表明的就是從咱們此次上拉加載以後第一個要獲取的文章的偏離值,結合咱們剛剛拿到的response中字段can_msg_continuenext_offset,咱們的思路就有了,當can_msg_continue爲動態的改變 url 的 offset 參數,將每次接口返回的 next_offset 字段做爲下次請求的 offset 參數,不斷遞歸調用,這樣理論上就能夠一次性爬取完該公衆號的全部文章。下面是核心邏輯的具體源碼。json

def crawl_msg(offset=0):
   """ 爬取更多文章 """
   url = "https://..." \
         "action=getmsg&" \
         "__biz=MjM5ODIyMTE0MA==&" \
         "f=json&" \
         "offset={offset}&" \
         "count=10&" \
         "is_ok=1&" \
         "scene=124&" \
         "uin=777&" \
         "key=777&" \
         "pass_ticket=Pu%2FH3aPR7f%2FzgA52T%2Bv4fU9wSWkY5tgGGgAXWewji2dqpMDrjaxUbBR%2Fmo2e%2FaMX&" \
         "wxtoken=&" \
         "appmsg_token=956_cnSiifKearMa6Um6cS3fcmXnu1AfKSYN5dSOSA~~&" \
         "x5=1&" \
         "f=json".format(offset=offset)
   # url 反轉義
   url = html.unescape(url)

   headers = """ Host: ... Connection: keep-alive X-Requested-With: XMLHttpRequest User-Agent: Mozilla/5.0 (Linux; Android 6.0.1; NX531J Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.132 MQQBrowser/6.2 TBS/044030 Mobile Safari/537.36 MicroMessenger/6.6.5.1280(0x26060532) NetType/WIFI Language/zh_CN Accept: */* Referer: ...action=home&__biz=MjM5ODIyMTE0MA==&scene=124&devicetype=android-23&version=26060532&lang=zh_CN&nettype=WIFI&a8scene=3&pass_ticket=Pu%2FH3aPR7f%2FzgA52T%2Bv4fU9wSWkY5tgGGgAXWewji2dqpMDrjaxUbBR%2Fmo2e%2FaMX&wx_header=1 Accept-Encoding: gzip, deflate Accept-Language: zh-CN,en-US;q=0.8 Cookie: sd_userid=96301522585723838; sd_cookie_crttime=1522585723838; pgv_pvid=1151171120; tvfe_boss_uuid=92a513a6354c3896; rewardsn=; wxtokenkey=777; wxuin=1336178621; devicetype=android-23; version=26060532; lang=zh_CN; pass_ticket=Pu/H3aPR7f/zgA52T+v4fU9wSWkY5tgGGgAXWewji2dqpMDrjaxUbBR/mo2e/aMX; wap_sid2=CL3vkf0EEnBSWHFYWmVoZVpOMjU0cnBpSUhiLWF2cmZHVVVLVWZrWUp4QVRlclVVOTRwS1hmMGNUR0VJaXp1RlVzbGViM2wtZnVfakZVd21RcGxxbzI3U3R3cmtYYlUycXpLU0FzcGJFSm1ESkZsYVhzSzhBd0FBMLbQ5dcFOA1AlU4= Q-UA2: QV=3&PL=ADR&PR=WX&PP=com.tencent.mm&PPVN=6.6.5&TBSVC=43603&CO=BK&COVC=044030&PB=GE&VE=GA&DE=PHONE&CHID=0&LCID=9422&MO= NX531J &RL=1080*1920&OS=6.0.1&API=23 Q-GUID: 6a875f18ea5ba76bb6afb9ca13b788cb Q-Auth: 31045b957cf33acf31e40be2f3e71c5217597676a9729f1b """
   headers = headers_to_dict(headers)
   response = requests.get(url, headers=headers, verify=False)
   if '<title>驗證</title>' in response.text:
       raise Exception("獲取微信公衆號文章失敗,多是由於你的請求參數有誤,請從新獲取")
   result = response.json()
   if result.get("ret") == 0:
       msg_list = result.get("general_msg_list")
       data = json.loads(msg_list)
       articles = data.get("list")
       articles_lists = dict()
       for item in articles:
           if item.get("app_msg_ext_info"):
               articles_lists[item["app_msg_ext_info"]["title"]] = item["app_msg_ext_info"]["content_url"]
       rex = r'\\/'
       for item in articles_lists:
           pattern = re.sub(rex, '/', html.unescape(articles_lists.get(item)))
           response = requests.get(pattern, headers=headers, verify=False)
           parser_text_to_file(item, response.text)
       print("抓取數據: offset=%s, data=%s" % (offset, msg_list))
       has_next = result.get("can_msg_continue")
       if has_next == 1:
           next_offset = result.get("next_offset")
           time.sleep(2) # 每兩秒爬取一次
           crawl_msg(next_offset)
   else:
       print("沒法正確獲取內容,請從新從Fiddler/Charles獲取請求參數和請求頭")
       exit()
複製代碼

代碼比較細,筆者講挑重點解析下。首先咱們經過format()來動態的更換 url 的offset 參數,從而在遞歸過程當中不斷變動 offset,最下面的crawl_msg(next_offset)就是將該次請求返回的 next_offset 做爲 offset 參數繼續調用。微信

代碼中有兩塊驗證邏輯,公衆號的相關接口在爬取過程當中,cookie 會有過時的現象,有多是由於時間過時,也有多是由於使用次數達到過時上限。這個時候,粗暴的解決方式就是從新再進入一次歷史消息界面,再把最新的 Header替換到代碼上。固然,網上也有一些方式能夠徹底的全自動解決cookie過時問題,這個優化項,你們能夠自行研究,我這裏的源碼就沒有涉及到了。cookie

值得注意的是,上段代碼中,有不少的url處理操做,好比經過html.unescape()來進行 url 反轉義,或者使用正則表達式來處理 content_url 中的一些干擾字符。咱們在作爬取的過程當中,數據和解析上可能有不少奇奇怪怪的問題,這個時候應該有耐心,經過慢慢的嘗試,去接近最終的解決方法。app

結合咱們上一節的代碼,咱們先獲取前十條,再從第十一條開始獲取後面的上拉加載更多數據,下面是我爬取「小道消息」公衆號的結果。你們能夠參考下。

固然了,筆者的 demo 很粗陋,很簡單,不少細節也沒考慮到,但核心思想和方式是有的。若是各位看官能經過個人文章,有所啓發,進行個性化的拓展,這對筆者來講就是最大的激勵。

筆者認爲,每一個人的需求是不一樣的,但遇到需求,可以經過某種思路,觸類旁通,用本身的方式來達成目的,並在達成目的的過程當中,解決問題。

這是咱們技術人應該要作到,也是我在這兩篇文章中,想表達的東西。

參考資料

掘金小冊:基於Python實現微信公衆號爬蟲--劉志軍

對了,有須要完整源碼的同窗,請在公衆號後臺回覆公號文章爬取


推薦閱讀

使用Python快速獲取公衆號文章定製電子書(一)

機器學習股票價格預測從爬蟲到預測-預測與調參

機器學習股票價格預測初級實戰

機器學習股票價格預測從爬蟲到預測(數據爬取部分)

關注公衆號獲取更多幹貨文章-AI極客研修站

相關文章
相關標籤/搜索