推荐文章:Python+fiddler:爬取微信公众号的文章
优采云 发布时间: 2022-10-20 10:25推荐文章:Python+fiddler:爬取微信公众号的文章
这几天,师父有一个小项目,很有意思。如何使用python抓取微信公众号中的新闻信息。一般流程如下。
图 1:流程
其实我们可以看到这里并没有想象中的“智能”——还是需要手动刷公众号文章,然后才能采集信息。(错误:更新的第 9 部分更智能,更少手动滑动)
1.从你的电脑下载fiddler
图 2:下载 fiddler2。安装后,首先看到的是这个
图 3:第一次打开 fiddler 后
这里是fiddler的介绍。
3.设置
图4:设置工具-选项-HTTPS
然后设置操作:单击操作,选择信任根证书并将根证书导出到桌面(出现提示时选择是)。
图 5:设置操作
图 6:设置工具-选项-连接4。手机设置(我用的是红米手机,其他手机大致相同)
图 7:设置移动 WiFi 代理(代理)
图8:手机进入网站192.168.124.14:8888
图 9:点击 FiddlerRoot 证书
图10:下载后安装,随便起个名字,我命名为“Fiddler2”
5.重启电脑Fiddler,手机打开公众号文章,电脑Fiddler采集信息
图 11:录制
图 12:详细分析
图13:复制Fiddler记录的链接,在浏览器中点击
图 14:过滤
图 15:过滤后的信息
可以看出,由于过滤起到了作用,所以序列号出现了跳跃。
6.导出所有信息
图 16:导出移动浏览历史
图 17:txt 信息 7. Python 提取公众号信息
import numpy as np
data = []
with open(r'...\1_Full.txt', 'r', encoding='utf-8') as fp:
for line in fp:
if 'Referer: https://mp.weixin.qq.com/' in line: //将含有重要信息的链接保留到data中
data.append(line[9:])
// 去重
data = np.unique(data)
8.通过电脑微信客户端抓取公众号信息
一遍遍地刷手机后,我一定很无聊。. 如果你可以通过鼠标点击电脑作为内部客户端,然后通过fiddler采集信息,那么你就不需要刷手机了。. 注意调整fiddler的时候,anaconda的jupyter是关闭的(spyder可以用),否则fiddler会有问题。
操作几乎相同。
首先,修改fiddler-Tools-Options-HTTPS,将HTTPS流量解密为“来自所有进程”。
图18:电脑采集微信公众号的操作
然后,同样在自己的浏览器中,输入IP地址+8888下载证书。
图 19:下载 FiddlerRoot 证书
下载后安装。
图 20:安装证书
过滤器的其他设置和上面的手机设置一样,都是过滤掉关于wp.weixin的内容。
然后,在电脑上刷一下微信公众号,过滤器就可以记录所有公众号文章。注意一旦开启fiddler,电脑无法访问其他网页,因为百度等反爬机制非常严格,会检测到fiddler已经启动。
9. 操作更自动化、智能化
无论是刷手机采集信息,还是通过电脑刷公众号,仍然需要人点击信息,不够智能。参考新案例后,这里可以进行颠覆性的改进。
首先,本文前面的模块仍然需要了解。当你已经可以在电脑端刷微信公众号文章,fiddler可以采集https信息,然后继续下一步。以微信公众号“首都之窗”为例。
(一)电脑微信端操作
打开提琴手。
点击设置-通用设置-使用系统默认浏览器打开网页。
图 21:电脑微信设置
然后,随意点击“资本窗口”的任意文章,就会在浏览器中弹出。把它放在那里并忽略它。顺便把fiddler中记录的文章信息删除了。把提琴手留空,记录下图25的关键内容!
这一步的目的是为了在浏览器中顺利打开和刷新公众号的历史新闻。
图 22:首先单击 文章
图 23:文章 在浏览器中弹出
图 24:完成操作
然后,进入“首都之窗”公众号,点击查看历史新闻。
图 25:查看历史消息
同理,浏览器弹出“历史消息”(微信客户端不能下拉,因为fiddler收不到信息),然后开始往下滑几下,需要看到新的内容弹出,同时您看到 fiddler 正在记录更新的信息。fiddler 更新消息是最重要的内容。
图 26:在浏览器中多次下拉“历史消息”
(2)提琴手信息分析
只需在浏览器中下拉公众号历史消息,fiddler采集就得到了更新的信息。让我们开始分析。
图 27:分析由于下拉历史消息而采集的记录
选择第8条记录(这条记录来自浏览器下拉历史采集到的消息),重要的部分已经圈在了表头。
(3)链接分析(看不懂就看代码怎么拼出链接的)
首先,在Request headers中,该链接全拼是/mp/profile_ext?action=getmsg&__biz=MzA5NDY5MzUzMQ==&f=json&offset=20&count=10&is_ok=1&scene=124&uin=777&key=777&pass_ticket=&wxtoken=&appmsg_token=1052_D6g2L7mM%252BaKLoVQK33V8q4D4wk3doi7QeR3Zog~~&x5 =0&f=json HTTP/1.1
分析这个链接。如您所见,它由几个部分组成。
①/mp/profile_ext? ②action=getmsg ③&__biz=MzA5NDY5MzUzMQ== ④&f=json⑤&offset=20 ⑥&count=10 ⑦&is_ok=1 ⑧&scene=124 ⑨&uin=777 ⑩&key=777 ⑪&pass_ticket= ⑫&wxtoken= ⑬&appmsg_token=1052_D6g2L7mM%252BaKLoVQK33V8q4D4wk3doi7QeR3Zog~~ ⑭&x5=0&f=json HTTP/1.1
那么我们需要注意的信息是:
③__biz:公众号的id(公众号的biz是唯一的),⑤offset:翻页标志,⑬appmsg_token:时间敏感的token(一段时间后会改变)
让我们看看以下链接
GET /mp/profile_ext?action=getmsg&__biz=MzA5NDY5MzUzMQ==&f=json&offset=40&count=10&is_ok=1&scene=124&uin=777&key=777&pass_ticket=&wxtoken=&appmsg_token=1052_D6g2L7mM%252BaKLoVQK33V8q4D4wk3doi7QeR3Zog~~&x5=0&f=json HTTP/1.1
GET /mp/profile_ext?action=getmsg&__biz=MzA5NDY5MzUzMQ==&f=json&offset=60&count=10&is_ok=1&scene=124&uin=777&key=777&pass_ticket=&wxtoken=&appmsg_token=1052_D6g2L7mM%252BaKLoVQK33V8q4D4wk3doi7QeR3Zog~~&x5=0&f=json HTTP/1.1
biz 和 appmsg_token 一样,改变了偏移量,是一个新的页面。因此,在第一步中,我们找到了翻页的规律。只有这三个在链接中发生变化,其他不变。因此,该链接可以用 python 编写为:
api = 'https://mp.weixin.qq.com/mp/profile_ext?action=getmsg&__biz={0}&f=json&offset={1}&count=10&is_ok=1&scene=124&uin=777&key=777&pass_ticket=&wxton=&appmsg_token={2}&x5=0&f=json HTTP/1.1'.format(
__biz, offset, appmsg_token)
(4) cookie 和标头
cookie保存微信登录信息,抓取时需要填写。我们只需要关注wsp_sid2的cookies信息即可。
cookie 也来自图 27。发现 wap_sid2=CK6vyK4CElxLdmda............
标题也来自图 27。找到“User-Agent”:“Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362”
(5) 爬行
好了,上面找到了很多资料。初始python如下:
import requests
import json
# 链接拼接三个信息
__biz = "MzA5NDY5MzUzMQ=="
appmsg_token = "1052_D6g2L7mM%252BaKLoVQK33V8q4D4wk3doi7QeR3Zog~~"
offset = 20
# cookies和headers
cookies = "wap_sid2=CK6vyK4CElxLdmda......."
headers = {'Cookie':cookies,
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362"
}
# api拼出来
api = 'https://mp.weixin.qq.com/mp/profile_ext?action=getmsg&__biz={0}&f=json&offset={1}&count=10&is_ok=1&scene=124&uin=777&key=777&pass_ticket=&wxton=&appmsg_token={2}&x5=0&f=json HTTP/1.1'.format(
__biz, offset, appmsg_token)
# 抓取并且json化
resp = requests.get(api, headers = headers, verify=False).json()
为什么你需要jsonize resp?我们可以尝试打开9(3)开头链接的网页,
图28:拼写api打开的网页是这样的
显然这是一条 json 消息。因此需要json化。把网页的整个文本复制下来放到网页里面,就可以看到完整的json结构了。这是 resp 的 web 结果。
图 29:将图 28 的文本信息复制到
那么resp在python中json化后的结果如下
图30:python中resp的结构
(6) 网页分析
接下来重点介绍resp的结构和逐层分析。图30中,resp中的errmsg=ok和ret=0,都表示网页可以正常打开(如果报错,ret=-3)。next_offset 为下一页的翻页标志,需要保存。
next_offset = resp.get('next_offset')
general_msg_list = resp.get('general_msg_list')
# 将general_msg_list转为json格式
msg_list = json.loads(general_msg_list)['list']
general_msg_list 是重要内容。点击general_msg_list,依然是json结构。
图 31:general_msg_list 仍然是一个 json 结构。
把里面的文字复制出来放在中间看看是什么。
图 32:将 general_msg_list 放入的结果
所以被json化后的msg_list在python中是这样的
图 33:在 python 中 jsonizing general_msg_list
可以看到,msg_list 中有 10 条记录。我们抽出一条记录进行具体分析。在分析之前,我们需要明确一件事。msg_list 收录 10 条记录,不是 10 条 文章,而是 10 条推送。某个公众号推送消息可能同时发布多个文章,也可能是一个文章。因此,请了解单个 msg 记录是指推送(可能同时有多个 文章)。
图 34:一次推送 3 篇文章一起发表 文章
msg = msg_list[0]
图 35:特定消息
味精收录“app_msg_ext_info”和“comm_msg_info”。在 中,两个内容是这样的。
图 36:msg 的两个特定部分---app_msg_ext_info 和 comm_msg_info
然后comm_msg_info收录了推送的基本信息:推送ID、时间等。
什么是 app_msg_ext_info?让我慢慢分析。首先title、digest一路到is_multi,都是push的开头文章(也就是图34中图片的文章的信息)。比如titletitle/digest关键词/content_url链接/source_url原创链接等。
is_multi 是判断push是否有读文章;=1 表示是,=0 表示否。那么这个等于1,说明本次推送中还有其他的文章,存在于multi_app_msg_item_list中。
取出 multi_app_msg_item_list。
multi_app_msg_item_list = app_msg_ext_info.get('multi_app_msg_item_list')
图 37:推送 文章 的剩余两篇文章隐藏在 multi_app_msg_item_list
至此,我们已经分析了整个流程。
总结
图 38:总结如何迈出第一步
图 39:详细的分析结构
(7)具体代码如下
import requests
import json
from datetime import datetime
import pandas as pd
import time
class WxMps:
def __init__(self, biz, appmsg_token, cookies, offset, city):
self.biz = biz
self.msg_token = appmsg_token
self.offset = offset
<p>
self.headers = {'Cookie':cookies, 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36'
}
self.city = city
def parse1(self, resp):
# 控制下一个抓取的offset
offset = resp.get('next_offset')
# 将包含主要内容的list转为json格式
general_msg_list = resp.get('general_msg_list')
# 一个msg_list中含有10个msg
msg_list = json.loads(general_msg_list)['list']
df1 = pd.DataFrame(columns = ['msg_id', 'post_time', 'msg_type', 'title', 'cover', 'author', 'digest', 'source_url', 'content_url'])
# 循环message列表
for msg in msg_list:
# msg是该推送的信息,包含了comm_msg_info以及app_msg_ext_info两个信息,注意某一个推送中可能含有多个文章。
comm_msg_info = msg.get('comm_msg_info')
app_msg_ext_info = msg.get('app_msg_ext_info')
# 该推送的id
msg_id = comm_msg_info.get('id')
# 该推送的发布时间,例如1579965567需要转化为datetime,datetime.fromtimestamp(1579965567)
post_time = datetime.fromtimestamp(comm_msg_info['datetime'])
# 该推送的类型
msg_type = comm_msg_info.get('type')
if app_msg_ext_info:
# 推送的第一篇文章
title, cover, author, digest, source_url, content_url = self.parse2(app_msg_ext_info)
df2 = self.df_process(msg_id, post_time, msg_type, title, cover, author, digest, source_url, content_url)
df1 = pd.concat([df1, df2])
# 判断是不是多篇文章
is_multi = app_msg_ext_info.get("is_multi")
# 如果是1,继续爬取;如果是0,单条推送=只有一篇文章
if is_multi:
multi_app_msg_item_list = app_msg_ext_info.get('multi_app_msg_item_list')
for information in multi_app_msg_item_list:
(title, cover, author, digest, source_url, content_url) = self.parse2(information)
df2 = self.df_process(msg_id, post_time, msg_type, title, cover, author, digest, source_url, content_url)
df1 = pd.concat([df1, df2])
return df1, offset
def start(self):
offset = self.offset
df1 = pd.DataFrame(columns = ['msg_id', 'post_time', 'msg_type', 'title', 'cover', 'author', 'digest', 'source_url', 'content_url'])
while offset