Skip to content

Instantly share code, notes, and snippets.

@snowman
Forked from Josscii/weixin.md
Last active April 1, 2020 04:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save snowman/97fa792582e727dcf62b1eb6f980c0bd to your computer and use it in GitHub Desktop.
Save snowman/97fa792582e727dcf62b1eb6f980c0bd to your computer and use it in GitHub Desktop.
wechat_spider 原理扫盲帖

wechat_spider 原理扫盲帖

这篇文章旨在为刚接触 wechat_spider 的人提供一个快速了解这个项目基本原理的途径。

思路

首先我们随便进入一个微信公众号详情页。

如图,点击这个查看历史消息,然后会跳转到一个微信公众号的历史详情页。

通过点击右上角的更多,我们可以把这个详情页的链接复制出来,是这个样子的。为了方便后面叙述,我们把这个链接称之为原始链接

https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=MjM5ODQ2MDIyMA==&scene=124#wechat_redirec

再多试试几个公众号的链接你会发现,这些链接大同小异,除了 __biz 这个参数值不同,其他都一样。很容易就可以猜到,这个 biz 就是公众号的唯一标示。

先说一下,其实这不是唯一能进入历史详情页的链接。通过我建立自己的微信公众号发现,微信后台为我们提供另外一个详情页的入口。就是下面这个。

http://mp.weixin.qq.com/mp/getmasssendmsg?__biz=MzIwMTYzMzcwOQ==#wechat_webview_type=1&wechat_redirect

好了,这里貌似我们已经拿到了链接,当我们去访问这个链接时,你会发现问题。那就是,通过微信访问这个链接,可以跳转到正确的历史详情页;然而通过普通浏览器访问却告诉我们只能在微信中打开。

为什么会这样呢?

这就需要我们对微信客户端进行抓包了。值得注意的是,由于微信使用的 https,所以我们抓包必须安装证书。抓包的详细技术流程我这里就不提了。

在微信客户端里访问上面的链接,通过抓包,我们会拿到微信在请求历史详情页时的真实链接,就像下面这样。

https://mp.weixin.qq.com/mp/getmasssendmsg?__biz=MzI5MjEzNzA1MA==&uin=MjM0MTU0MjI0NA%3D%3D&key=8b7cd00eddf4ed78ed80ca4ab30303a0c6e3b16028072178c0018eac7b9088c41b531e3c6681d6496d0247d4958f4ee1a6f319449aaf40362c7fcfa760a71ece90109e392d744ee90c9e14415633bd6e&devicetype=iMac+MacBookPro12%2C1+OSX+OSX+10.12+build(16A323)&version=12000410&lang=zh_CN&nettype=WIFI&ascene=0&fontScale=100&pass_ticket=3W1cPqsZmPxCYDuAkx0ATXzpJhNRYsYvLEY4V3QJ3tI2levhhW%2Fr5SQ8hQB0WNmD

好了,访问这个链接你会发现在普通浏览器中也可以正确的访问历史记录了。

这个链接里面参数很多,其中最关键的几个参数是:

  • __biz 微信公众号唯一标示
  • key
  • pass_ticket

其他的参数我们还好伪造,但是多抓几次包你会发现这个 key 和 pass_ticket 会变,而且应该会失效。

既然我们无法伪造这两个参数,反过来思考如果能让微信客户端自动为我们补全参数不就好了吗?

对,群主的思路就是这样的。

首先我们把需要抓取的公众号的 biz 都存在本地,前面说过历史详情页的原始链接都一样,只要通过通过替换 __biz 就可以构造出来所有我们想要的所有原始链接了。

接下来,我们让微信客户端去访问这个链接,利用中间人攻击抓包,就可以拦截到微信访问真实链接的请求,拦截到的请求的 response 是一个 html,里面就有我们想要的历史详情页的数据结构了。

实现

思路就是这样,下面谈一下实现。

我采用的抓 mac 微信客户端的请求来得到数据。

首先运行群主的 robot,会让你指定输入框的位置,如果你不知道上面的原理,你会很懵逼,其实就是需要在微信里面开一个和文件助手(其他账号也可以,只要不怕被骚扰)的对话框,然后把鼠标放到输入框的位置就行了,这个就是用来后台程序拼接好链接后输入到微信里。

接下来是点击链接的位置,也就是原始链接输入并且发给文件助手后,那个链接在对话框中的位置,因为我们需要点击这个链接,让微信用自己的浏览器打开。

下面这个图是位置的示意图。

这两个位置指定好后,通过代码里面模拟鼠标和键盘操作,就可以实现,输入原始链接,自动点击链接的自动化了。

至此我们完成了第一步,通过微信访来自动访问原始链接。

接下来,我们需要开启一个代理来监听拦截微信的这个访问从而得到真实链接。

翻页请求

到这里,我们就拿到了一个公众号的历史详情页了,不过还没完,通过分析数据你会发现这个链接只返回了最近的十个推送文章,那么新的需求又来了,如何实现获取到一个公众号所有的文章呢?

其实很简单,前面我们让微信通过自己的浏览器打开了一个公众号的历史详情页之后,继续向下翻页,你的代理又继续拦截到新的请求了,查看这些请求的参数,你会发现新的请求只多了三个参数。

  • frommsgid 从哪个推送开始
  • count 获取多少个
  • f 值默认为 json

这就清楚了,通过构造参数,很容易就可以实现翻页的操作了。

另外这里还有一个 bonus,就是后面链接的返回内容都是 json 格式了,而不是第一次的 html。

阅读数和点赞数

好了,还剩下一个内容就是阅读数和点赞数的获取。在微信客户端点击一篇文章后,还是通过抓包,你会拦截到微信的去获取阅读数和点赞数的一个请求如下:

http://mp.weixin.qq.com/mp/getappmsgext?__biz=MjM5NDk2NTc4MQ==&appmsg_type=9&mid=2247484991&sn=a1a675384a953260e689cbb16e50653f&idx=1&scene=4&title=%E9%97%BB%E5%A6%88%E8%82%B2%E5%84%BF%E9%9F%B3%E9%A2%91%E4%B8%93%E6%A0%8F%E5%BD%95%E5%88%B6%E8%8A%B1%E7%B5%AE&ct=1491434706&abtest_cookie=&devicetype=iPhone%20OS9.3.2&version=/mmbizwap/zh_CN/htmledition/js/appmsg/index34f0d8.js&f=json&r=0.5300119849853218&is_need_ad=0&comment_id=3278966785&is_need_reward=0&both_ad=1&reward_uin_count=0&msg_daily_idx=1&uin=777&key=777&pass_ticket=&wxtoken=2536410355&devicetype=iPhone%26nbsp%3BOS9.3.2&clientversion=369428256&x5=0&f=json/

同样的,去请求这个链接会返回的是一个 json,里面就是你想要的数据了。

批量化

最后就是批量化了。

我们可以从数据库去读取链接后,实现一个链接分发调度服务来实现链接的方法拼接,比如从数据库按更新时间拿到一批公众号数据,打入 redis 队列,robot 程序不断读取 redis 队列的链接来实现(爬取文章可以拼接成文章列表页,爬取阅读点赞指标可以拼接成文章详情页)。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment