如何爬取微信公眾號文章(一)

微信公眾號是目前最為流行的自媒體之一,上面有大量的內(nèi)容,如何將自己感興趣的公眾號內(nèi)容爬取下來入客,離線瀏覽,或者作進(jìn)一步的分析呢腿椎?
下面我們討論一下微信公眾號文章的爬取桌硫。


image.png

環(huán)境搭建

  • windows 7 x64
  • python3.7 (Anaconda 3)
  • vscode編輯器
  • Firefox開發(fā)版

爬蟲原理分析

首先網(wǎng)頁登陸微信公眾平臺(https://mp.weixin.qq.com/),登陸成功后啃炸,點(diǎn)擊新建群發(fā)->自建圖文,插入超連接在如下的對話框中铆隘,點(diǎn)擊選擇其他公眾號。

編輯超鏈接

在彈出的編輯超鏈接的對話框中南用,輸入想要爬取的公眾號名字膀钠,回車
搜索公眾號

下拉列表中第一個就是我們想找的掏湾,點(diǎn)擊它,彈出的這個公眾號的文章列表托修,是按照時(shí)間排序的忘巧。
文章列表

我們看一下這個過程中前后端交互的HTTP請求和響應(yīng)。

檢索公眾號

請求url: https://mp.weixin.qq.com/cgi-bin/searchbiz
方法: GET
提交的參數(shù)為

{
   "action": "search_biz",
   "begin": "0",
   "count": "5",
   "query": "地球知識局",
   "token": "138019412",
   "lang": "zh_CN",
   "f": "json",
   "ajax": "1"
}

請求中的字段

action 動作
begin 列表的起始
count 列表的數(shù)目
query 查詢的字符串
f 參數(shù)格式 這里為json
ajax 應(yīng)該代碼ajax請求
lang 語言 這里是中文
token 這應(yīng)該是授權(quán)信息睦刃,下文會深究

得到的響應(yīng)為

{
    "base_resp": {
        "ret": 0,
        "err_msg": "ok"
    },
    "list": [
        {
            "fakeid": "MzI1ODUzNjQ1Mw==",
            "nickname": "地球知識局",
            "alias": "diqiuzhishiju",
            "round_head_img": "http://mmbiz.qpic.cn/mmbiz_png/DCftNYRGoKWLHFETxuTzGBguTwAibl0p8BpXmNIkBTmNth2Vd6vEWibtT8mLYWG6e5aiaa97u5LmjhbXn19a8Cr6g/0?wx_fmt=png",
            "service_type": 1
        },
        {
            "fakeid": "MzU5MjI3MzIyMg==",
            "nickname": "地球知識局庫",
            "alias": "",
            "round_head_img": "http://mmbiz.qpic.cn/mmbiz_png/b5kRqlMaRNHJnJ1ibFUPOichbvtVGk7CWicj406ZAccBuOpr2JibShHSAvUN7iaSuQj3rN66P8akeKa63rjy11NNkicw/0?wx_fmt=png",
            "service_type": 2
        },
        {},
        {},
        {}
    ],
    "total": 45
}

響應(yīng)中各字段的含義不難看出

fakeid 為該公眾號的唯一的id砚嘴,為一串bs64編碼
nikename 為公眾號的名稱
alias 為別名
round_head_img 為圓形logo的url
service_type 服務(wù)類型 不太清楚 沒必要深究用不到

獲取公眾號文章列表

請求網(wǎng)址:https://mp.weixin.qq.com/cgi-bin/appmsg
請求方法:GET
提交的參數(shù):

{
    "action": "list_ex",
    "begin": "0",
    "count": "5",
    "fakeid": "MzI1ODUzNjQ1Mw==",
    "type": "9",
    "query": "", 
    "token": "138019412",
    "lang": "zh_CN",
    "f": "json",
    "ajax": "1"
}

action 行為
begin 列表開始索引
count 列表返回的公眾號的時(shí)間區(qū)間長度,如5表示返回5天的數(shù)據(jù)
fakeid 這個公眾號的ID
type 不知道
query 檢索的關(guān)鍵字涩拙,這里為空
token 用戶的token
lang 語言
f 數(shù)據(jù)格式,這里為json
ajax

響應(yīng)為

{
    "app_msg_cnt": 919,
    "app_msg_list": [
        {
            "aid": "2247518136_1",
            "appmsgid": 2247518136,
            "cover": "https://mmbiz.qlogo.cn/mmbiz_jpg/DCftNYRGoKWG0USHVfs1FG2pGKfz0BMUI3FLibHTrYe1a7WMKzZnazCKDJ9OUfuibGbewFqIiakic8MEqDkNiaXHH7w/0?wx_fmt=jpeg",
            "create_time": 1578235906,
            "digest": "三不管地帶容易出問題",
            "is_pay_subscribe": 0,
            "item_show_type": 0,
            "itemidx": 1,
            "link": "http://mp.weixin.qq.com/s?__biz=MzI1ODUzNjQ1Mw==&mid=2247518136&idx=1&sn=812ec79199ae793f28770287969d0f2b&chksm=ea0462d2dd73ebc40f6ecc4f1f52fb2a3e0c798ca152aa89cc42b8e77ef6e54234695ad43025#rd",
            "tagid": [],
            "title": "肆虐非洲的“博科圣地”究竟是什么际长?",
            "update_time": 1578235905
        },
        {},
        {}
           
    ],
    "base_resp": {
        "err_msg": "ok",
        "ret": 0
    }
}

響應(yīng)的字段

app_msg_cnt 表示這個公眾號已經(jīng)發(fā)布了919次文章,不代表919篇文章
aid 文章唯一的id兴泥,應(yīng)該是
appmsgid 代表一次群發(fā)工育,如三篇文章是一次性群發(fā)的,其appmsgid相同
cover 文章封面圖片的url
create_time 創(chuàng)建時(shí)間戳
digest 文章的摘要信息
is_pay_subscribe
item_show_type
itemidx 在這次群發(fā)中的序號
link 文章的url
tagid 為一個列表
title 文章的標(biāo)題
update_time 文章更新的時(shí)間戳
這些已經(jīng)包含了一篇文章的元數(shù)據(jù)了搓彻。

token從哪兒來

上面的GET方法提交的參數(shù)有中都有個token字段如绸,這個字段的用途應(yīng)該鑒權(quán)用的,這個值從哪兒來的旭贬?我們在前面的HTTP請求中找怔接,發(fā)現(xiàn)幾乎所有的請求中的都帶有這個token,我猜測這個token是用戶登陸時(shí)從后端返回來的稀轨。
為了印證這個判斷扼脐,重新登陸一次,發(fā)現(xiàn)了有這樣的一個HTTP請求奋刽。
請求網(wǎng)址:https://mp.weixin.qq.com/cgi-bin/bizlogin?action=login
請求方法:POST
表單數(shù)據(jù):

{
    "userlang": "zh_CN",
    "redirect_url": "",
    "token": "",
    "lang": "zh_CN",
    "f": "json",
    "ajax": "1"
}

響應(yīng):

{
    "base_resp": {
        "err_msg": "ok",
        "ret": 0
    },
    "redirect_url": "/cgi-bin/home?t=home/index&lang=zh_CN&token=1193797244"
}

后端返回了一個重定向的uri瓦侮,其中就包含了token的值。
完成這個請求后佣谐,頁面進(jìn)行了重定向肚吏,并且以后的每次請求都有會有l(wèi)ang=zh_CN&token=xxxx這兩個參數(shù)。

代碼實(shí)現(xiàn)

完成了上面這些分析狭魂,下面我們進(jìn)行代碼實(shí)現(xiàn)须喂。

# -*- coding:utf-8 -*-
# written by wlj @2020-1-6 23:12:47 
#功能:爬取一個公眾號的所有歷史文章存入數(shù)據(jù)庫
#用法:python wx_spider.py [公眾號名稱] 如python wx_spider.py 地球知識局
import time
import json
import requests,re,sys
from requests.packages import urllib3
from pymongo import MongoClient
urllib3.disable_warnings()

#全局變量
s = requests.Session()
headers = {
    'User-Agent':"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0",
    "Host": "mp.weixin.qq.com",
    'Referer':'https://mp.weixin.qq.com/'
}
#cookies 字符串,這是從瀏覽器中拷貝出來的字符串,略過不講
cookie_str = "xxxx"
cookies = {}

#加載cookies趁蕊,將字符串格式的cookies轉(zhuǎn)化為字典形式
def load_cookies():
    global cookie_str,cookies
    for item in cookie_str.split(';'):
        sep_index = item.find('=') 
        cookies[item[:sep_index]] =item[sep_index+1:]

#爬蟲主函數(shù)
def spider():
    #本地的mongodb數(shù)據(jù)庫
    mongo = MongoClient('127.0.0.1',27017).wx.gzh
    #加載cookies
    load_cookies()

    #訪問官網(wǎng)主頁
    url = 'https://mp.weixin.qq.com'
    res = s.get(url=url,headers=headers,cookies = cookies,verify=False)
    if res.status_code ==  200:
        #由于加載了cookies坞生,相當(dāng)于已經(jīng)登陸了,系統(tǒng)作了重定義掷伙,response的url中含有我們需要的token
        print(res.url)

        #獲得token
        token = re.findall(r'.*?token=(\d+)',res.url)
        if token:
            token = token[0]
        else:#沒有token的話是己,說明cookies過時(shí)了,沒有登陸成功任柜,退出程序
            print('登陸失敗')
            return

        print('token',token)

        #檢索公眾號
        url = 'https://mp.weixin.qq.com/cgi-bin/searchbiz'
        data = {
            "action": "search_biz",
            "begin": "0",
            "count": "5",
            "query": sys.argv[1],
            "token": token,
            "lang": "zh_CN",
            "f": "json",
            "ajax": "1"
        }
        res = s.get(url=url,params = data,cookies=cookies,headers=headers,verify=False)
        if res.status_code == 200:
            #搜索結(jié)果的第一個往往是最準(zhǔn)確的
            #提取它的fakeid
            fakeid = res.json()['list'][0]['fakeid']
            print('fakeid',fakeid)

            page_size = 5
            page_count = 1
            cur_page = 1

            #分頁請求文章列表
            while cur_page <= page_count:
                url = 'https://mp.weixin.qq.com/cgi-bin/appmsg'
                data = {
                    "action": "list_ex",
                    "begin": str(page_size*(cur_page-1)),
                    "count": str(page_size),
                    "fakeid": fakeid,
                    "type": "9",
                    "query": "", 
                    "token": token,
                    "lang": "zh_CN",
                    "f": "json",
                    "ajax": "1"
                }
                res = s.get(url=url,params = data,cookies=cookies,headers=headers,verify=False)
                if res.status_code == 200:
                    print(res.json())
                    print('cur_page',cur_page)
                    #文章列表位于app_msg_list字段中
                    app_msg_list = res.json()['app_msg_list']
                    for item in app_msg_list:
                        #通過更新時(shí)間戳獲得文章的發(fā)布日期
                        item['post_date'] = time.strftime("%Y-%m-%d",time.localtime(int(item['update_time'])))
                        #插入數(shù)據(jù)庫卒废,如果已經(jīng)存在同aid的話沛厨,更新,不存在摔认,插入
                        mongo.update_one(
                            {'aid':item['aid']},
                            {"$set":item},
                            upsert=True
                        )
                        print(item['post_date'],item['title'])
                    
                    if cur_page == 1:#若是第1頁逆皮,計(jì)算總的分頁數(shù)
                        #總的日期數(shù),每page_size天的文章為一頁
                        app_msg_cnt = res.json()['app_msg_cnt']
                        print('app_msg_cnt',app_msg_cnt)
                        #計(jì)算總的分頁數(shù)
                        if app_msg_cnt % page_size == 0:
                            page_count = int(app_msg_cnt / page_size)
                        else:
                            page_count = int(app_msg_cnt / page_size) + 1
                    
                #當(dāng)前頁面數(shù)+1
                cur_page += 1
                

            print('完成参袱!')
    

spider()

結(jié)果

爬取的結(jié)果

可以看到电谣,所有文章的元數(shù)據(jù)已經(jīng)存入數(shù)據(jù)庫了。
下一節(jié)抹蚀,我們講如何利用文章的url來爬取文章內(nèi)容剿牺,這個比較簡單。
這兒還存在一個問題环壤,騰訊的這個接口有頻率限制晒来,當(dāng)爬取的次數(shù)太多,頻率太快時(shí)郑现,就請求不到數(shù)據(jù)了湃崩,會返回這樣的信息。

{'base_resp': {'err_msg': 'freq control', 'ret': 200013}}

至少間隔一天接箫,這個賬號才能繼續(xù)爬取攒读,不知道如何破解。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載列牺,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。
  • 序言:七十年代末拗窃,一起剝皮案震驚了整個濱河市瞎领,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌随夸,老刑警劉巖九默,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異宾毒,居然都是意外死亡驼修,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進(jìn)店門诈铛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來乙各,“玉大人,你說我怎么就攤上這事幢竹《停” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵焕毫,是天一觀的道長蹲坷。 經(jīng)常有香客問我驶乾,道長,這世上最難降的妖魔是什么循签? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任级乐,我火速辦了婚禮,結(jié)果婚禮上县匠,老公的妹妹穿的比我還像新娘风科。我一直安慰自己,他們只是感情好聚唐,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布丐重。 她就那樣靜靜地躺著,像睡著了一般杆查。 火紅的嫁衣襯著肌膚如雪扮惦。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天亲桦,我揣著相機(jī)與錄音崖蜜,去河邊找鬼。 笑死客峭,一個胖子當(dāng)著我的面吹牛豫领,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播舔琅,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼等恐,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了备蚓?” 一聲冷哼從身側(cè)響起课蔬,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎郊尝,沒想到半個月后二跋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡流昏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年扎即,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片况凉。...
    茶點(diǎn)故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡谚鄙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出刁绒,到底是詐尸還是另有隱情襟锐,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布膛锭,位于F島的核電站粮坞,受9級特大地震影響蚊荣,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜莫杈,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一互例、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧筝闹,春花似錦媳叨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至议双,卻和暖如春痘番,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背平痰。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工汞舱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人宗雇。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓昂芜,卻偏偏與公主長得像,于是被迫代替她去往敵國和親赔蒲。 傳聞我的和親對象是個殘疾皇子泌神,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 上一篇我們講了如何獲取一個公眾號下面的文章,但是有時(shí)候只獲取文章內(nèi)容還是不夠舞虱,還要得到文章的閱讀數(shù)和點(diǎn)贊數(shù)欢际。公眾號...
    雨田君的記事本閱讀 6,681評論 0 2
  • 背景說明 ??感覺微信公眾號算得是比較難爬的平臺之一,不過一番折騰之后還是小有收獲的砾嫉。沒有用Scrapy(估計(jì)爬太...
    happyJared閱讀 13,665評論 20 31
  • 很多的微信公眾號都提供了質(zhì)量比較高的文章閱讀幼苛,對于自己喜歡的微信公眾號窒篱,所以想做個微信公眾號爬蟲焕刮,爬取相關(guān)公眾號的...
    Evtion閱讀 12,019評論 8 23
  • 先向大家拜年,祝您家的老人身輕體健福壽安康壯得像牦牛墙杯!祝您家的孩子學(xué)習(xí)進(jìn)步百尺竿頭更進(jìn)一步配并!祝您的事業(yè)芝麻開花步步...
    沉默如me閱讀 1,619評論 0 93
  • 過了很久之后的我才想明白 你開始時(shí)和我說的那句“謝謝你” 不過是三塊錢一瓶的綠茶蓋子里的那種謝謝你的參與 你最后分...
    嵐風(fēng)的葉子閱讀 165評論 0 0