如何爬取微信公眾號的所有文章

準(zhǔn)備階段

為了實現(xiàn)該爬蟲我們需要用到如下工具

  • Chrome瀏覽器
  • Python 3 語法知識
  • Python的Requests庫

此外学赛,這個爬取程序利用的是微信公眾號后臺編輯素材界面盈包。原理是,當(dāng)我們在插入超鏈接時,微信會調(diào)用專門的API(見下圖)劳跃,以獲取指定公眾號的文章列表。因此,我們還需要有一個公眾號诈茧。

fig1

正式開始

我們需要登錄微信公眾號,點擊素材管理捂掰,點擊新建圖文消息敢会,然后點擊上方的超鏈接。

fig2

接著这嚣,按F12鸥昏,打開Chrome的開發(fā)者工具,選擇Network

fig3

此時在之前的超鏈接界面中姐帚,點擊「選擇其他公眾號」吏垮,輸入你需要爬取的公眾號(例如中國移動)

fig4

此時之前的Network就會刷新出一些鏈接,其中以"appmsg"開頭的便是我們需要分析的內(nèi)容

fig5

我們解析請求的URL

https://mp.weixin.qq.com/cgi-bin/appmsg?action=list_ex&begin=0&count=5&fakeid=MzI1MjU5MjMzNA==&type=9&query=&token=143406284&lang=zh_CN&f=json&ajax=1

它分為三個部分

  • https://mp.weixin.qq.com/cgi-bin/appmsg: 請求的基礎(chǔ)部分
  • ?action=list_ex: 常用于動態(tài)網(wǎng)站,實現(xiàn)不同的參數(shù)值而生成不同的頁面或者返回不同的結(jié)果
  • &begin=0&count=5&fakeid: 用于設(shè)置?里的參數(shù)膳汪,即begin=0, count=5

通過不斷的瀏覽下一頁唯蝶,我們發(fā)現(xiàn)每次只有begin會發(fā)生變動,每次增加5遗嗽,也就是count的值粘我。

接著,我們通過Python來獲取同樣的資源痹换,但直接運行如下代碼是無法獲取資源的

import requests
url = "https://mp.weixin.qq.com/cgi-bin/appmsg?action=list_ex&begin=0&count=5&fakeid=MzI1MjU5MjMzNA==&type=9&query=&token=1957521839&lang=zh_CN&f=json&ajax=1"
requests.get(url).json() 
# {'base_resp': {'ret': 200003, 'err_msg': 'invalid session'}}

我們之所以能在瀏覽器上獲取資源征字,是因為我們登錄了微信公眾號后端。而Python并沒有我們的登錄信息娇豫,所以請求是無效的柔纵。我們需要在requests中設(shè)置headers參數(shù),在其中傳入Cookie和User-Agent锤躁,來模擬登陸

由于每次頭信息內(nèi)容都會變動搁料,因此我將這些內(nèi)容放入在單獨的文件中,即"wechat.yaml"系羞,信息如下

cookie:  ua_id=wuzWM9FKE14...
user_agent: Mozilla/5.0...

之后只需要讀取即可

# 讀取cookie和user_agent
import yaml
with open("wechat.yaml", "r") as file:
    file_data = file.read()
config = yaml.safe_load(file_data) 

headers = {
    "Cookie": config['cookie'],
    "User-Agent": config['user_agent'] 
}

requests.get(url, headers=headers, verify=False).json()

在返回的JSON中郭计,我們就看到了每個文章的標(biāo)題(title), 摘要(digest), 鏈接(link), 推送時間(update_time)和封面地址(cover)等信息。

appmsgid是每一次推送的唯一標(biāo)識符椒振,aid則是每篇推文的唯一標(biāo)識符昭伸。

fig6

實際上,除了Cookie外澎迎,URL中的token參數(shù)也會用來限制爬蟲庐杨,因此上述代碼很有可能輸出會是{'base_resp': {'ret': 200040, 'err_msg': 'invalid csrf token'}}

接著我們寫一個循環(huán),獲取所有文章的JSON夹供,并進(jìn)行保存灵份。

import json
import requests
import time
import random

import yaml
with open("wechat.yaml", "r") as file:
    file_data = file.read()
config = yaml.safe_load(file_data) 

headers = {
    "Cookie": config['cookie'],
    "User-Agent": config['user_agent'] 
}

# 請求參數(shù)
url = "https://mp.weixin.qq.com/cgi-bin/appmsg"
begin = "0"
params = {
    "action": "list_ex",
    "begin": begin,
    "count": "5",
    "fakeid": config['fakeid'],
    "type": "9",
    "token": config['token'],
    "lang": "zh_CN",
    "f": "json",
    "ajax": "1"
}

# 存放結(jié)果
app_msg_list = []
# 在不知道公眾號有多少文章的情況下,使用while語句
# 也方便重新運行時設(shè)置頁數(shù)
i = 0
while True:
    begin = i * 5
    params["begin"] = str(begin)
    # 隨機暫停幾秒哮洽,避免過快的請求導(dǎo)致過快的被查到
    time.sleep(random.randint(1,10))
    resp = requests.get(url, headers=headers, params = params, verify=False)
    # 微信流量控制, 退出
    if resp.json()['base_resp']['ret'] == 200013:
        print("frequencey control, stop at {}".format(str(begin)))
        break
    
    # 如果返回的內(nèi)容中為空則結(jié)束
    if len(resp.json()['app_msg_list']) == 0:
        print("all ariticle parsed")
        break
        
    app_msg_list.append(resp.json())
    # 翻頁
    i += 1

在上面代碼中填渠,我將fakeid和token也存放在了"wechat.yaml"文件中,這是因為fakeid是每個公眾號都特有的標(biāo)識符鸟辅,而token則會經(jīng)常性變動氛什,該信息既可以通過解析URL獲取,也可以從開發(fā)者工具中查看

fig7

在爬取一段時間后匪凉,就會遇到如下的問題

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

此時你在公眾號后臺嘗試插入超鏈接時就能遇到如下這個提示

fig8

這是公眾號的流量限制枪眉,通常需要等上30-60分鐘才能繼續(xù)。為了完美處理這個問題再层,你可能需要申請多個公眾號贸铜,可能需要和微信公眾號的登錄系統(tǒng)斗智斗勇堡纬,或許還需要設(shè)置代理池。

但是我并不需要一個工業(yè)級別的爬蟲萨脑,只想爬取自己公眾號的信息隐轩,因此等個一小時饺饭,重新登錄公眾號渤早,獲取cookie和token,然后運行即可瘫俊。我可不想用自己的興趣挑戰(zhàn)別人的飯碗鹊杖。

最后將結(jié)果以JSON格式保存。

# 保存結(jié)果為JSON
json_name = "mp_data_{}.json".format(str(begin))
with open(json_name, "w") as file:
    file.write(json.dumps(app_msg_list, indent=2, ensure_ascii=False))

或者提取文章標(biāo)識符扛芽,標(biāo)題骂蓖,URL,發(fā)布時間這四列信息川尖,保存成CSV登下。

info_list = []
for msg in app_msg_list:
    if "app_msg_list" in msg:
        for item in msg["app_msg_list"]:
            info = '"{}","{}","{}","{}"'.format(str(item["aid"]), item['title'], item['link'], str(item['create_time']))
            info_list.append(info)
# save as csv
with open("app_msg_list.csv", "w") as file:
    file.writelines("\n".join(info_list))         

下一篇,將介紹如何根據(jù)每個文章的連接地址叮喳,來獲取每篇文章的閱讀量信息被芳。

參考資料

最終代碼如下,使用方法為python wechat_parser.py wechat.yaml

import json
import requests
import time
import random
import os
import yaml
import sys

if len(sys.argv) < 2:
    print("too few arguments")
    sys.exit(1)

yaml_file = sys.argv[1]
if not os.path.exists(yaml_file):
    print("yaml_file is not exists")
    sys.exit(1)
    

with open(yaml_file, "r") as file:
    file_data = file.read()
config = yaml.safe_load(file_data)

headers = {
    "Cookie": config['cookie'],
    "User-Agent": config['user_agent'] 
}

# 請求參數(shù)
url = "https://mp.weixin.qq.com/cgi-bin/appmsg"
begin = "0"
params = {
    "action": "list_ex",
    "begin": begin,
    "count": "5",
    "fakeid": config['fakeid'],
    "type": "9",
    "token": config['token'],
    "lang": "zh_CN",
    "f": "json",
    "ajax": "1"
}

# 存放結(jié)果
if os.path.exists("mp_data.json"):
    with open("mp_data.json", "r") as file:
        app_msg_list = json.load(file)
else:
    app_msg_list = []
# 在不知道公眾號有多少文章的情況下馍悟,使用while語句
# 也方便重新運行時設(shè)置頁數(shù)
i = len(app_msg_list) // 5
while True:
    begin = i * 5
    params["begin"] = str(begin)
    # 隨機暫停幾秒畔濒,避免過快的請求導(dǎo)致過快的被查到
    time.sleep(random.randint(1,10))
    resp = requests.get(url, headers=headers, params = params, verify=False)
    # 微信流量控制, 退出
    if resp.json()['base_resp']['ret'] == 200013:
        print("frequencey control, stop at {}".format(str(begin)))
        break
    
    # 如果返回的內(nèi)容中為空則結(jié)束
    if len(resp.json()['app_msg_list']) == 0:
        print("all ariticle parsed")
        break
        
    app_msg_list.append(resp.json())
    # 翻頁
    i += 1

# 保存結(jié)果為JSON
json_name = "mp_data.json"
with open(json_name, "w") as file:
    file.write(json.dumps(app_msg_list, indent=2, ensure_ascii=False))
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市锣咒,隨后出現(xiàn)的幾起案子侵状,更是在濱河造成了極大的恐慌,老刑警劉巖毅整,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件趣兄,死亡現(xiàn)場離奇詭異,居然都是意外死亡悼嫉,警方通過查閱死者的電腦和手機诽俯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來承粤,“玉大人暴区,你說我怎么就攤上這事⌒岭” “怎么了仙粱?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長彻舰。 經(jīng)常有香客問我伐割,道長候味,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任隔心,我火速辦了婚禮白群,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘硬霍。我一直安慰自己帜慢,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布唯卖。 她就那樣靜靜地躺著粱玲,像睡著了一般。 火紅的嫁衣襯著肌膚如雪拜轨。 梳的紋絲不亂的頭發(fā)上抽减,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機與錄音橄碾,去河邊找鬼卵沉。 笑死,一個胖子當(dāng)著我的面吹牛法牲,可吹牛的內(nèi)容都是我干的史汗。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼皆串,長吁一口氣:“原來是場噩夢啊……” “哼淹办!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起恶复,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤怜森,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后谤牡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體副硅,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年翅萤,在試婚紗的時候發(fā)現(xiàn)自己被綠了恐疲。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡套么,死狀恐怖培己,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情胚泌,我是刑警寧澤省咨,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站玷室,受9級特大地震影響零蓉,放射性物質(zhì)發(fā)生泄漏笤受。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一敌蜂、第九天 我趴在偏房一處隱蔽的房頂上張望箩兽。 院中可真熱鬧,春花似錦章喉、人聲如沸汗贫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽芳绩。三九已至掀亥,卻和暖如春撞反,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背搪花。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工遏片, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人撮竿。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓吮便,卻偏偏與公主長得像,于是被迫代替她去往敵國和親幢踏。 傳聞我的和親對象是個殘疾皇子髓需,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345