生而為人,我很抱歉!深夜爬蟲, 我很抱歉 ,附微信 “ 網抑雲” 公眾號爬蟲教程!
生而為人,我很抱歉!深夜爬蟲, 我很抱歉 ,附微信 “ 網抑雲” 公眾號爬蟲教程!
最近真的是被
網抑雲
這個梗刷爆了,到處都是, 生而為人,我很抱歉,哈哈哈, 碰巧最近學習了一波微信公眾號的爬取方式,想試一試, 特地在此獻醜了。我是小何, 不定期更新自動化測試教學, 其餘時間學軟體測試和linux中。 八月,繼續加油。
關注我喲!!!
資料參考: 本篇部落格,基於她的思想之中, 改編於我自己,我自己動手敲了一下, 發現還是存在很大的不同, 也是存在一點問題的, 所以這篇部落格,我用我自己的話講一下這次爬蟲的思路,用簡單的話說明白。
工具:
- 抓包工具 Fiddle 官網下載,有點慢
- 第三方包:
import requests
import json
from urllib.parse import urlencode
import pdfkit
# wkhtmltopdf github : https://github.com/JazzCore/python-pdfkit/wiki/Installing-wkhtmltopdf
import os
import warnings # 關閉警告
關於pdfkit 這是一個三方庫, 它可以將html轉換為pdf 或者 image 影象, 他需要一個軟體的支援, 就是wkhtmltopdf wkhtmltopdf 官網
下載之後, 在 bin 目錄下 可以看到 wkhtmltopdf.exe 這個工具,先記下路徑,後面需要用到。
開啟fiddle,開始抓包
- 關於fiddle的基礎配置, 在下載的時候,都會提到, 這裡就不提了。
開啟fiddle 隨意開啟一個微信公眾號。往下拖動微信下拉條,讓他載入更多文章,就像這樣。
微信公眾號的包很好分別的,主要他是一個json包, 而且通過網址我們也可以很好的認出來, 這張截圖上注意的點我都標出來了。
其實這也可以理解成一個ajax請求吧, 我們只要構造一個字典引數加入,就可以構造成一個請求網址,這樣就可以實現多頁爬取了。原理我覺得差不多, 主要還是 構造引數 params 的甄別了,這個很重要, 有些引數沒構造成功就不能正確的訪問進去。
關於ajax的我也寫了一篇部落格, 可以進去看看 = =
那下面我們就開始編寫爬蟲吧!
構造基本引數:
一般我們所講的基本引數 也就是瀏覽器的請求頭, 還有一些請求引數, cookies之類的, 這裡額外加一個 請求引數params , 看程式碼:
圖方便, 這裡也可以先構造一個檔案 to_pdf
to_pdf.py
# -*- coding : utf-8 -*-
# @Time : 2020/8/6 18:31
# @author : 公眾號【測試員小何】
# @Software : PyCharm
# @CSDN : https://me.csdn.net/qq_45906219
## to_pdf.py
# 目標公眾號【測試員小何】
biz = 'MzA4NDQwMTczOA=='
# 微信登入後的一些標識引數
pass_ticket = 'DdDXKrOnQztW4p81Nm3nRQQ/EAFMCDz5MZO5KeBYdedjaZPH4nLFHL2LWE1uxHVJ'
appmsg_token = '1073_tnlzQTEqvlr9EgXQdC6zALGHfcJw4By9Bx69bQ~~'
uin = 'Mzc2MDg4MzgxNA=='
key = '360754e56e033319af5321321189d5b230b2bfe570313f76e4ed2ef0cd8a1fcfd087786d9da1d826ed30da55f477215359ba761e2a2af5f92213b05c17d89153631a537e35fc91f6d7fb3009e7113958'
# 安裝的wkhtmltopdf.exe檔案路徑 這個是儲存為pdf的 其實他還有儲存為檔案
wkhtmltopdf_path = r'D:\wkhtmltox\wkhtmltopdf\bin\wkhtmltopdf.exe'
上面提到一些 biz , passticket 之類引數的,我們可以在這裡看到。
#公眾號【測試員小何】
def __init__(self):
"""spider the wxChat station"""
self.session = requests.session()
self.offset = 0 # 偏移量
self.json_name = 'wxChat.json' # 儲存json檔名
self.down_path = 'D:/wxChatPDF/' # 下載地址
self.__initGetBaseData()
def __initGetBaseData(self):
"""
input some BaseParams about the requests session
"""
self.headers = {
'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.5;q=0.4',
"User-Agent": 這個太長了,在fiddle上面直接複製就可以
'X-Requested-With': 'XMLHttpRequest'
}
self.cookies = {
'wxtokenkey': '777',
"wxuin": "3760883814",
"devicetype": "android - 29",
"version": "27000",
"lang": "zh_CN",
"pass_ticket": to_pdf.pass_ticket,
"wap_sid2": "COaQqoEOElxramROblROTTRtTktEa29yU3drdExUODg4RjZCaVVsX3lycHV0VUJ"
"3aWtmMl9xNDlJVFN0YTZJY0NxdzZsTXdfaHJxMmZyTTRUZGlGdTZHNVEtNzh5REVFQUFBfjDDsK / 5BTgNQJVO"
}
self.profile_url = "https://mp.weixin.qq.com/mp/profile_ext?"
self.session.headers.update(self.headers) # 更新headers
self.session.cookies.update(self.cookies) # 更新cookies
self.params = {
'action': 'getmsg',
'__biz': to_pdf.biz,
'f': 'json',
'offset': '10', # 若需要多頁爬取, 構造 : str(self.offset)
'count': '10',
'is_ok': '1',
'scene': '124',
'uin': to_pdf.uin,
'key': to_pdf.key, # 這個好像二十分鐘就會失效, 需要隔段時間更換
'pass_ticket': to_pdf.pass_ticket,
'wxtoken': '',
'appmsg_token': to_pdf.appmsg_token,
'x5': '0',
}
這裡講一下 self.profile_url 這個引數, 我們在fiddle上面獲得的網址是很長的一段, 其實只要卡到 ? 這裡就可以了, 可以試試的。
請求網址, 通過json包,分割出標題和網址:
上面我們構造了一些引數,然後就可以正常的請求網址了,如下:
#公眾號【測試員小何】
def run(self):
"""提取出標題和網址,
儲存到json包 然後下載轉換為Pdf"""
items = {}
for jsons in self.get_json():
json_list = jsons.get('general_msg_list')
json_list = json.loads(json_list) # 轉換為json 型別
json_list1 = json_list.get('list')
for json_one in json_list1:
# 遍歷這個列表字典 先解析最外層標題和網址
json_list_info = json_one.get('app_msg_ext_info')
title = json_list_info.get('title')
content_url = json_list_info.get('content_url')
items[title] = content_url # 裝入字典
json_list_info1 = json_list_info.get('multi_app_msg_item_list')
for json_two in json_list_info1: # 解析第二層標題和網址
title2 = json_two.get('title')
content_url2 = json_two.get('content_url')
items[title2] = content_url2
# 轉換字典型別
items_json = json.dump(items, ensure_ascii=False, indent=4,
fp=self.json_name) # ensure_ascii=False(輸出中文), indent=4(縮排為4)
# 先寫入檔案,避免佔用太多記憶體消耗
with open(self.json_name, 'a+', encoding='utf-8') as fw:
fw.write(items_json)
print('dump the wxChat.json is successful!')
# 下載ok之後, 就開始下載
self.down()
def get_json(self):
"""得到所有文章的連結
構造一個office偏移量 不斷的請求構造網址 分析Json包
"""
print('【ps】 spider the wxChart is starting!')
for i in range(1, 2):
print(f'The SpiderDemo is spider doing {i} pages')
# self.offset += 10 * i # 開啟這個 表示 多頁爬取
# 對比了很多次 構造網址 和 原網址 還是有很多的區別 在這裡改一改才能正確的成功
self.profile_url += (urlencode(self.params) + '&f=json HTTP/1.1')
self.profile_url = self.profile_url.replace('%3D%3D&f=json', '==&f=json', 1)
r = self.session.get(self.profile_url, verify=False)
yield r.json()
這裡發現了一個很大的問題, 不管是在 get() 裡面構造字典, 還是先構造字典,都發現我不能正確的獲取到資料, 最後列印了一下構造網址, 發現存在很大問題。
我構造的
https://mp.weixin.qq.com/mp/profile_ext?action=getmsg&__biz=MzA4NDQwMTczOA%3D%3D&f=json&offset=10&count=10&is_ok=1&scene=124&uin=Mzc2MDg4MzgxNA%3D%3D&key=9a979acccbefb6032e6ea1a3ed3fbe82a67e7244eb884c9b4fd497550577b4c57f82cb7c0998ef8dc91cf1dca069ca16fe8cce902f238a72294726745094a68c5efb99f91df5e2592c7540ec90d5b09b&pass_ticket=DdDXKrOnQztW4p81Nm3nRQQ%2FEAFMCDz5MZO5KeBYdedjaZPH4nLFHL2LWE1uxHVJ&wxtoken=&appmsg_token=1073_tnlzQTEqvlr9EgXQdC6zALGHfcJw4By9Bx69bQ~~&x5=0&f=json
從fiddle上面複製的
https://mp.weixin.qq.com/mp/profile_ext?action=getmsg&__biz=MzA4NDQwMTczOA==&f=json&offset=10&count=10&is_ok=1&scene=124&uin=Mzc2MDg4MzgxNA%3D%3D&key=9a979acccbefb6032e6ea1a3ed3fbe82a67e7244eb884c9b4fd497550577b4c57f82cb7c0998ef8dc91cf1dca069ca16fe8cce902f238a72294726745094a68c5efb99f91df5e2592c7540ec90d5b09b&pass_ticket=D2Ir2BvSw4lli9ZReGdqnsFacl0N6Lnpmj9h4EE4CBdqV7cd7co7eRRnOBO4EsG%2F&wxtoken=&appmsg_token=1073_o%252FrQqQ5kpRJZNWMKabr8tLelugCSKx8mIN5IGQ~~&x5=0&f=json HTTP/1.1
對比了一下,發現我自己的url 第一個 == 變成了 %3D%3D , 字尾也少了很多。然後進行了修改, 最後成功進入了。
如下:
#公眾號【測試員小何】
self.profile_url += (urlencode(self.params) + '&f=json HTTP/1.1')
self.profile_url = self.profile_url.replace('%3D%3D&f=json', '==&f=json', 1)
上面的程式碼包括很多json的拆分, 這個我推薦一個網址, 很好用。
可以線上解析 json 包, 線上解析json
讀入本地檔案,下載html,轉換pdf
上面我們程式碼中講到, 為了減少記憶體的佔用, 我把檔案先儲存到本地了,那麼現在就可以直接讀取本地檔案,程式碼如下:
#公眾號【測試員小何】
def pathisok(self, path):
"""判斷目錄是否存在, 不存在就建立 進入檔案"""
if not os.path.exists(self.down_path):
os.mkdir(self.down_path)
def down(self):
"""
開啟json包,根據標題,網址開始下載,
爬取之後儲存的格式可以很多種,這裡我使用一下之前學到的一個新工具 to_pdf
將網頁轉換為html頁面
"""
self.pathisok(self.down_path)
with open(self.json_name, 'r', encoding='utf-8') as fr:
for index in fr:
if ':' in index: # 判斷是否是不是標題和網址
title = index.strip().split(':')[0]
url = ''.join(index.strip().split(':')[1:]).strip(',')
# 對網址進一步處理
url = url.replace('http', 'https:')
# 如果不修改檔名稱 一定會報錯 OsError的錯誤 找了很久
title = title.replace('\\', '').replace('/', '').replace(':', '').replace(':', '') \
.replace('*', '').replace('?', '').replace('?', '').replace('“', '') \
.replace('"', '').replace('<', '').replace('>', '').replace('|', '_')
print('- ' * 40)
print(f'The title is {title} starting spider')
print('- ' * 40)
# print(os.path.join(self.down_path, title + '.pdf'))
pdfkit.from_url(url, os.path.join(self.down_path, title + '.pdf'),
configuration=pdfkit.configuration(wkhtmltopdf=to_pdf.wkhtmltopdf_path))
# pdfkit.from_url(value, os.path.join(self.savedir, key + '.pdf'),
# configuration=pdfkit.configuration(wkhtmltopdf=self.cfg.wkhtmltopdf_path))
print('- ' * 40)
print(f'The title is {title} spider is successful')
print('- ' * 40)
else:
pass
這裡要注意一點的, 關於檔案的名稱,有的時候爬蟲我們給檔案起名稱不會在意很多細節, 如果檔名中有特殊符號, 或者其他不允許的符號,那麼建立檔案或者目錄就會報錯, 大致就是IOError 之類的錯誤了。 所以我們需要對 Title 進行再一次的修改, 確保不會出現問題。
開啟pdf, 深夜了,開啟 ‘網抑雲’ 。
好了, 這就是本次爬蟲的全部過程了。
全部程式碼:
如果對軟體測試有興趣,想了解更多的測試知識,解決測試問題,以及入門指導,
幫你解決測試中遇到的困惑,我們這裡有技術高手。如果你正在找工作或者剛剛學校出來,
又或者已經工作但是經常覺得難點很多,覺得自己測試方面學的不夠精想要繼續學習的,
想轉行怕學不會的,都可以加入我們644956177或者關注公眾號【測試員小何】領取資料哦~。
群內可領取最新軟體測試大廠面試資料和Python自動化、介面、框架搭建學習資料!
# -*- coding : utf-8 -*-
# @Time : 2020/8/6 17:00
# @author : 公眾號【測試員小何】
# @Software : PyCharm
# @CSDN : https://me.csdn.net/qq_45906219
import requests
import json
from urllib.parse import urlencode
import pdfkit
# wkhtmltopdf github : https://github.com/JazzCore/python-pdfkit/wiki/Installing-wkhtmltopdf
import os
import warnings
warnings.filterwarnings('ignore')
class wxChatSpider(object):
def __init__(self):
"""spider the wxChat station"""
self.session = requests.session()
self.offset = 0
self.json_name = 'wxChat.json'
self.down_path = 'D:/wxChatPDF/' # 下載地址
self.__initGetBaseData()
def run(self):
"""提取出標題和網址,
儲存到json包 然後下載轉換為Pdf"""
items = {}
for jsons in self.get_json():
json_list = jsons.get('general_msg_list')
json_list = json.loads(json_list) # 轉換為json 型別
json_list1 = json_list.get('list')
for json_one in json_list1:
# 遍歷這個列表字典 先解析最外層標題和網址
json_list_info = json_one.get('app_msg_ext_info')
title = json_list_info.get('title')
content_url = json_list_info.get('content_url')
items[title] = content_url # 裝入字典
json_list_info1 = json_list_info.get('multi_app_msg_item_list')
for json_two in json_list_info1: # 解析第二層標題和網址
title2 = json_two.get('title')
content_url2 = json_two.get('content_url')
items[title2] = content_url2
# 轉換字典型別
items_json = json.dump(items, ensure_ascii=False, indent=4,
fp=self.json_name) # ensure_ascii=False(輸出中文), indent=4(縮排為4)
# 先寫入檔案,避免佔用太多記憶體消耗
with open(self.json_name, 'a+', encoding='utf-8') as fw:
fw.write(items_json)
print('dump the wxChat.json is successful!')
# 下載ok之後, 就開始下載
self.down()
def get_json(self):
"""得到所有文章的連結
構造一個office偏移量 不斷的請求構造網址 分析Json包
"""
print('【ps】 spider the wxChart is starting!')
for i in range(1, 2):
print(f'The SpiderDemo is spider doing {i} pages')
# self.offset += 10 * i # 開啟這個 表示 多頁爬取
# 對比了很多次 構造網址 和 原網址 還是有很多的區別 在這裡改一改才能正確的成功
self.profile_url += (urlencode(self.params) + '&f=json HTTP/1.1')
self.profile_url = self.profile_url.replace('%3D%3D&f=json', '==&f=json', 1)
r = self.session.get(self.profile_url, verify=False)
yield r.json()
def pathisok(self, path):
"""判斷目錄是否存在, 不存在就建立 進入檔案"""
if not os.path.exists(self.down_path):
os.mkdir(self.down_path)
def down(self):
"""
開啟json包,根據標題,網址開始下載,
爬取之後儲存的格式可以很多種,這裡我使用一下之前學到的一個新工具 to_pdf
將網頁轉換為html頁面
"""
self.pathisok(self.down_path)
with open(self.json_name, 'r', encoding='utf-8') as fr:
for index in fr:
if ':' in index: # 判斷是否是不是標題和網址
title = index.strip().split(':')[0]
url = ''.join(index.strip().split(':')[1:]).strip(',')
# 對網址進一步處理
url = url.replace('http', 'https:')
# 如果不修改檔名稱 一定會報錯 OsError的錯誤 找了很久
title = title.replace('\\', '').replace('/', '').replace(':', '').replace(':', '') \
.replace('*', '').replace('?', '').replace('?', '').replace('“', '') \
.replace('"', '').replace('<', '').replace('>', '').replace('|', '_')
print('- ' * 40)
print(f'The title is {title} starting spider')
print('- ' * 40)
# print(os.path.join(self.down_path, title + '.pdf'))
pdfkit.from_url(url, os.path.join(self.down_path, title + '.pdf'),
configuration=pdfkit.configuration(wkhtmltopdf=to_pdf.wkhtmltopdf_path))
# pdfkit.from_url(value, os.path.join(self.savedir, key + '.pdf'),
# configuration=pdfkit.configuration(wkhtmltopdf=self.cfg.wkhtmltopdf_path))
print('- ' * 40)
print(f'The title is {title} spider is successful')
print('- ' * 40)
else:
pass
def __initGetBaseData(self):
"""
input some BaseParams about the requests session
"""
self.headers = {
'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.5;q=0.4',
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36"
" (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36 QBCore/4.0.1301.400 "
"QQBrowser/9.0.2524.400 Mozilla/5.0 (Windows NT 6.1; WOW64)"
" AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2875.116"
" Safari/537.36 NetType/WIFI MicroMessenger/7.0.5 WindowsWechat",
'X-Requested-With': 'XMLHttpRequest'
}
self.cookies = {
'wxtokenkey': '777',
"wxuin": "3760883814",
"devicetype": "android - 29",
"version": "27000",
"lang": "zh_CN",
"pass_ticket": to_pdf.pass_ticket,
"wap_sid2": "COaQqoEOElxramROblROTTRtTktEa29yU3drdExUODg4RjZCaVVsX3lycHV0VUJ"
"3aWtmMl9xNDlJVFN0YTZJY0NxdzZsTXdfaHJxMmZyTTRUZGlGdTZHNVEtNzh5REVFQUFBfjDDsK / 5BTgNQJVO"
}
self.profile_url = "https://mp.weixin.qq.com/mp/profile_ext?"
self.session.headers.update(self.headers) # 更新headers
self.session.cookies.update(self.cookies) # 更新cookies
self.saveDir = 'D:/wxChatSpider'
self.params = {
'action': 'getmsg',
'__biz': to_pdf.biz,
'f': 'json',
'offset': '10', # 若需要多頁爬取, 構造 : str(self.offset)
'count': '10',
'is_ok': '1',
'scene': '124',
'uin': to_pdf.uin,
'key': to_pdf.key, # 這個好像二十分鐘就會失效, 需要隔段時間更換
'pass_ticket': to_pdf.pass_ticket,
'wxtoken': '',
'appmsg_token': to_pdf.appmsg_token,
'x5': '0',
}
if __name__ == '__main__':
import to_pdf
# 裡面是第三方庫的配置, 以及一些微信公眾號的引數
spider = wxChatSpider()
spider.run()