一個(gè)好的公眾號爬蟲(chóng)將內容抓取保存下來(lái)慢慢賞析
優(yōu)采云 發(fā)布時(shí)間: 2021-08-15 19:09一個(gè)好的公眾號爬蟲(chóng)將內容抓取保存下來(lái)慢慢賞析
有時(shí)候我們會(huì )遇到一個(gè)很好的公眾號,里面的每一篇文章都值得反復閱讀。這時(shí)候我們就可以使用公眾號爬蟲(chóng)抓取并保存內容,慢慢欣賞。
安裝 Fiddler
Fiddler的下載地址為:安裝完成后,確保手機和電腦的網(wǎng)絡(luò )是同一個(gè)局域網(wǎng)。
Finder 配置
點(diǎn)擊工具>>選項>>連接面板,參考下圖配置,Fiddler的默認端口是8888,如果8888端口被占用,可以修改為其他端口。
點(diǎn)擊工具>>選項>>HTTPS面板,參考下圖進(jìn)行配置
安卓手機配置
進(jìn)入WLAN設置,選擇當前局域網(wǎng)的WIFI設置,代理設置為手動(dòng),代理服務(wù)器主機名為Finder,右上角點(diǎn)擊在線(xiàn),端口號為8888。
在手機瀏覽器中訪(fǎng)問(wèn)配置的地址:8888,顯示Fiddler Echo Service時(shí),手機配置成功。
為了讓 Finddler 攔截 HTTPS 請求,必須在手機中安裝 CA 證書(shū)。在 :8888 中,單擊 FiddlerRoot 證書(shū)下載并安裝證書(shū)。至此配置工作完成。
微信歷史頁(yè)面
以【騰旭大神網(wǎng)】為例,點(diǎn)擊【上海新聞】菜單的二級菜單【歷史新聞】。
觀(guān)察 Fiddler 的變化。此時(shí),左側窗口中會(huì )陸續出現多個(gè)URL連接地址。這是 Fiddler 截獲的 Android 請求。
Result:服務(wù)器的響應結果 Protocol:請求協(xié)議,微信協(xié)議都是HTTPS,所以需要在手機和PC端安裝證書(shū)。 HOST: 主機名 URL: URL 地址
以 .com/mp/profile_ext?action=home... 開(kāi)頭的 URL 之一正是我們所需要的。點(diǎn)擊右側的Inspectors面板,然后點(diǎn)擊下方的Headers和WebView面板,會(huì )出現如下圖案
標題面板
Request Headers:請求行,收錄請求方法、請求地址、等待C??lient的請求協(xié)議、Cookies:請求頭
WebView 面板
WebView面板顯示服務(wù)器返回的HTML代碼的渲染結果,Textview面板顯示服務(wù)器返回的HTML源代碼
獲取歷史頁(yè)面
在上一節中,公眾號消息歷史頁(yè)面已經(jīng)可以在 Fiddler 的 WebView 面板中顯示。本節使用Python抓取歷史頁(yè)面。創(chuàng )建一個(gè)名為wxcrawler.py的腳本,我們需要URL地址和HEADER請求頭來(lái)抓取頁(yè)面,直接從Finder中復制
將標頭轉換為 Json
# coding:utf-8
import requests
class WxCrawler(object):
# 復制出來(lái)的 Headers,注意這個(gè) x-wechat-key,有時(shí)間限制,會(huì )過(guò)期。當返回的內容出現 驗證 的情況,就需要換 x-wechat-key 了
headers = """Connection: keep-alive
x-wechat-uin: MTY4MTI3NDIxNg%3D%3D
x-wechat-key: 5ab2dd82e79bc5343ac5fb7fd20d72509db0ee1772b1043c894b24d441af288ae942feb4cfb4d234f00a4a5ab88c5b625d415b83df4b536d99befc096448d80cfd5a7fcd33380341aa592d070b1399a1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Linux; Android 10; GM1900 Build/QKQ1.190716.003; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/67.0.3396.87 XWEB/992 MMWEBSDK/191102 Mobile Safari/537.36 MMWEBID/7220 MicroMessenger/7.0.9.1560(0x27000933) Process/toolsmp NetType/WIFI Language/zh_CN ABI/arm64
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/wxpic,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,en-US;q=0.9
Cookie: wxuin=1681274216; devicetype=android-29; version=27000933; lang=zh_CN; pass_ticket=JvAJfzySl6uLWYdYwzyQ+4OqrqiZ2zfaI4F2OCVR7omYOmTjYNKalCFbr75X+T6K; rewardsn=; wxtokenkey=777; wap_sid2=COjq2KEGElxBTmotQWtVY2Iwb3BZRkIzd0Y0SnpaUG1HNTQ0SDA4UGJOZi1kaFdFbkl1MHUyYkRKX2xiWFU5VVhDNXBkQlY0U0pRXzlCZW9qZ29oYW9DWmZYOTdmQTBFQUFBfjD+hInvBTgNQJVO
X-Requested-With: com.tencent.mm"""
url = "https://mp.weixin.qq .com/mp/profile_ext?action=home&__biz=MjEwNDI4NTA2MQ==&scene=123&devicetype=android-29&version=27000933&lang=zh_CN&nettype=WIFI&a8scene=7&session_us=wxid_2574365742721&pass_ticket=JvAJfzySl6uLWYdYwzyQ%2B4OqrqiZ2zfaI4F2OCVR7omYOmTjYNKalCFbr75X%2BT6K&wx_header=1"
# 將 Headers 轉換為 字典
def header_to_dict(self):
headers = self.headers.split("\n")
headers_dict = dict()
for h in headers:
k,v = h.split(":")
headers_dict[k.strip()] = v.strip()
return headers_dict;
def run(self):
headers = self.header_to_dict()
response = requests.get(self.url, headers=headers, verify=False)
print(response.text)
if __name__ == "__main__":
wx = WxCrawler()
wx.run()
下圖是控制臺打印的內容,其中JavaScript中變量msgList的值就是需要的內容
下一步是提取msgList的內容,使用正則表達式提取內容,返回一個(gè)文章list
import re
import html
import json
def article_list(self, context):
rex = "msgList = '({.*?})'"
pattern = re.compile(pattern=rex, flags=re.S)
match = pattern.search(context)
if match:
data = match.group(1)
data = html.unescape(data)
data = json.loads(data)
articles = data.get("list")
return articles
以下是解析msgList的結果
title:文章title content_url:文章link source_url:原鏈接,可能為空 摘要:摘要封面:封面圖片 datetime:推送時(shí)間
其他內容存儲在 multi_app_msg_item_list 中
{'comm_msg_info':
{
'id': 1000033457,
'type': 49,
'datetime': 1575101627,
'fakeid': '2104285061',
'status': 2,
'content': ''
},
'app_msg_ext_info':
{
'title': '快查手機!5000多張人臉照正被賤賣(mài),數據曝光令人觸目驚心!',
'digest': '誰(shuí)有權收集人臉信息?',
'content': '',
'fileid': 0,
'content_url': 'http:\/\/mp.weixin.qq.com\/s?__biz=MjEwNDI4NTA2MQ==&mid=2651824634&idx=1&sn=3e4c8eb35abb1b09a4077064ba0c44c8&chksm=4ea8211079dfa8065435409f4d3d3538ad28ddc197063a7e1820dafb9ee23beefca59c3b32d4&scene=27#wechat_redirect',
'source_url': '',
'cover': 'http:\/\/mmbiz.qpic.cn\/mmbiz_jpg\/G8vkERUJibkstwkIvXB960sMOyQdYF2x2qibTxAIq2eUljRbB6zqBq6ziaiaVqm8GtEWticE6zAYGUYqKJ3SMuvv1EQ\/0?wx_fmt=jpeg',
'subtype': 9,
'is_multi': 1,
'multi_app_msg_item_list':
[{
'title': '先有雞還是先有蛋?6.1億年前的胚胎化石揭曉了',
'digest': '解決了困擾大申君20多年的問(wèn)題',
'content': '',
'fileid': 0,
'content_url': 'http:\/\/mp.weixin.qq.com\/s?__biz=MjEwNDI4NTA2MQ==&mid=2651824634&idx=2&sn=07b95d31efa9f56d460a16bca817f30d&chksm=4ea8211079dfa8068f42bf0e5df076a95ee3c24cab71294632fe587bcc9238c1a7fb7cd9629b&scene=27#wechat_redirect',
'source_url': '',
'cover': 'http:\/\/mmbiz.qpic.cn\/mmbiz_jpg\/yl6JkZAE3S92BESibpZgTPE1BcBhSLiaGOgpgVicaLdkIXGExe3mYdyVkE2SDXL1x2lFxldeXu8qXQYwtnx9vibibzQ\/0?wx_fmt=jpeg',
'author': '',
'copyright_stat': 100,
'del_flag': 1,
'item_show_type': 0,
'audio_fileid': 0,
'duration': 0,
'play_url': '',
'malicious_title_reason_id': 0,
'malicious_content_type': 0
},
{
'title': '外交部驚現“李佳琦”!網(wǎng)友直呼:“OMG被種草了!”',
'digest': '種草了!',
'content': '', ...}
...]
獲取單個(gè)頁(yè)面
在上一節中,我們可以得到app_msg_ext_info中的content_url地址,需要從comm_msg_info的不規則Json中獲取。這是使用demjson模塊完成不規則的comm_msg_info。
安裝 demjson 模塊
pip3 install demjson
import demjson
# 獲取單個(gè)文章的URL
content_url_array = []
def content_url(self, articles):
content_url = []
for a in articles:
a = str(a).replace("\/", "/")
a = demjson.decode(a)
content_url_array.append(a['app_msg_ext_info']["content_url"])
# 取更多的
for multi in a['app_msg_ext_info']["multi_app_msg_item_list"]:
self.content_url_array.append(multi['content_url'])
return content_url
獲取單個(gè)文章的地址后,使用requests.get()函數獲取HTML頁(yè)面并解析
# 解析單個(gè)文章
def parse_article(self, headers, content_url):
for i in content_url:
content_response = requests.get(i, headers=headers, verify=False)
with open("wx.html", "wb") as f:
f.write(content_response.content)
html = open("wx.html", encoding="utf-8").read()
soup_body = BeautifulSoup(html, "html.parser")
context = soup_body.find('div', id = 'js_content').text.strip()
print(context)
所有歷史文章
當你向下滑動(dòng)歷史消息時(shí),出現Loading...這是公眾號正在翻頁(yè)的歷史消息。查Fiddler,公眾號請求的地址是.com/mp/profile_ext? action=getmsg&__biz...
翻頁(yè)請求地址返回結果,一般可以分析。
ret:是否成功,0代表成功msg_count:每頁(yè)的條目數 can_msg_continue:是否繼續翻頁(yè),1代表繼續翻頁(yè)general_msg_list:數據,包括標題、文章地址等信息
def page(self, headers):
response = requests.get(self.page_url, headers=headers, verify=False)
result = response.json()
if result.get("ret") == 0:
msg_list = result.get("general_msg_list")
msg_list = demjson.decode(msg_list)
self.content_url(msg_list["list"])
#遞歸
self.page(headers)
else:
print("無(wú)法獲取內容")
總結
這里已經(jīng)爬取了公眾號的內容,但尚未爬取單個(gè)文章的閱讀和查看數量。想想看,如何抓取這些內容變化?
示例代碼:
PS:公眾號內回復:Python,可以進(jìn)入Python新手學(xué)習交流群,一起
-END-