微信公众号文章URL的种类与结构

微信公众号是微信围墙花园的一部分。外界想访问其中内容时,往往需要面对一个很长的URL——其过分的长度与不甚清晰的含义给使用者带来了不小的困扰,有时携带的跟踪参数还会给分享者带来泄露身份的风险。本文将对微信公众号文章URL的三种类型进行简单的描述,并记录相关的发现。

背景部分较为琐碎,赶时间可按此跳至正题

背景

微信公众平台上线于2012年8月17日,伴随着微信用户量的增长,与其余社交功能紧密整合的公众号逐渐成为不少人获取信息的主要渠道,众多新兴的“自媒体”将传统媒体打得落花流水。开设一个公众号成了接触国内各类受众的方式中最有效的一种,还在建站的恐怕只剩些老古板了。相信称微信公众号是近些年来中国大陆最主要的在线信息发布渠道都不为过,其中不乏许多仅在公众号中发表的优秀内容。然而,微信公众号对外界极为封闭,使用正常的浏览器仅能通过文章的URL访问文章页,无法了解这个公众号的其他内容。搜狗的微信搜索是它们在微信客户端外唯一公开的访问渠道,然而腾讯对它仍限制重重:可以用它查看某个公众号的简介资料,早前还会在简介页列出它最新的十篇文章,但此功能最晚在2020年初已经失效了;可以进行全局的文章搜索,但效果实在差强人意,有时换用许多关键词也只能得到充斥着垃圾内容的结果。更糟糕的是,从这里得到的文章链接一段时间后(目前为6小时)就会过期失效。纵使不少用户对微信公众号有各种怨言,目前它在国内内容领域的霸主地位,似乎是不可撼动的。

首次接触微信公众号的时候,觉得它那时的一些特性使它有成为一个历史信息存档库的潜质:内容全都储存在腾讯自己的服务器上,服务可靠且持久,内容发布者无需承担储存过去文章的成本;文章发布后无法修改,删除重发代价较大(一般的订阅号每日只能推送一次消息),能忠实地记录文章首次发表时的模样。不过,微信平台随后进行的一些规则变动则使当时的期待有些站不住脚了。对于后一点,微信于2018年2月7日上线了修改文章错别字的功能,不过这个功能还是较为“克制”的,其允许修改的字数不多(目前为20个),作用恐怕确实如微信团队“微信派”所说只限于修改错别字了。

20字的变动或许已经足以让作者删去文章中存在争议的句子,可好歹文章还在。但关乎文章保存时限的前一点,根据微信公告里的说法,则是应监管方的一纸文件而不再成立了:援引网信办于2017年9月7日印发的《互联网用户公众账号信息服务管理规定》第七条中“互联网用户公众账号信息服务提供者应当对同一主体在同一平台注册公众账号的数量合理设定上限”的规定,微信在2018年先后两次将原先企业50个、个人5个公众号的限额缩至企业2个,个人1个。在首次调低开设公众号限额后,我便遇到了个人运营的公众号被迁移至新“主体”,先前内容均被删除的情况(原帐号似乎是个人为了企业号的权限而向他人买来的)。此外,微信还宣布将自动注销210天内不活跃的非认证公众号(目前个人号与企业号均可进行“微信认证”)。对于一般的免费UGC平台,只要平台还在,用户发布的内容就能一直留在网上,即是用户已经淡出,或因疾病等不可抗力无法访问网站。说实话,他们恐怕本就没打算让微信公众平台成为一个适合长期保存内容的地方,否则它就不会是个只可用自家的搜狗来搜索、对帐号的历史文章列表不提供日期选择器,仅可在微信里不停地往下翻才能列出文章的围墙花园了。

话说回来,国内的地方政府与传统新闻媒体网站有一个通病:更换CMS时往往会更改URL的格式,过去保存在收藏夹里的链接一夜之间就失效了;有的可能还会顺带清理早期的内容,想要寻找的旧资料在新网站里连影子都没有。此时只能在搜索引擎里换着关键词来寻找转载与快照页,或是去 Wayback Machinearchive.is 这类存档网站来碰运气了。相比之下,那些不会被随意清理的微信公众号发布的内容则显得可靠很多。再者,目前许多机构会将微信公众号和新浪微博作为主要的公关渠道进行运营,网站往往疏于打理、更新较缓,甚至已经荒废、连域名都已被菠菜网站抢注了,使用开放互联网中的搜索引擎根本无法及时获得信息。微信,是不得不用的。

然而,微信公众号文章的URL始终为人诟病。在“PC时代”,再三流的CMS系统也不至于生成形如https://mp.weixin.qq.com/s?__biz=MjM5NjM4MDAxMg==&mid=2655090139&idx=1&sn=7516984a032cb031ce73577fe0ae5366这样令人凌乱的URL。可是,微信(以及不少现代社交网站与IM)会将用于跟踪用户的参数掺进本来足以定位一篇文章的URL里,并把这一长串吓人的东西藏在漂亮的消息气泡内,再贴上一张诱人的缩略图。在它们的围墙花园里,这些ugly detail都被掩盖在华丽的界面之下。但当用户想把文章分享到围墙花园之外,就不得不面对它们的底层实现——URL。在当前版本的微信中,选择“复制链接”会得到形如https://mp.weixin.qq.com/s/ruYM6tbU06iI4zfeQDTuLA的短链接,而如果选择“在浏览器打开”,便会在浏览器的地址栏中看到形似段首例子的长URL,并带有多项可以起跟踪作用的参数。

网上有一些爬取微信公众号文章的经验介绍,不过在下面的文章中,我仅将尝试介绍微信公众号文章URL的三种类型,并描述哪些参数是必须的。(其实是水平太差了,不想研究如何爬取)

三种URL格式

这里使用新华社的公众号“新华网”于北京时间2021年5月8日18:36:40发表的《注意!疫苗接种第二针不能这样打!》作为例子。

https://mp.weixin.qq.com/s/-rwvHhqYbKGCVFeXRNknYQ
https://mp.weixin.qq.com/s?__biz=MzA4MjQxNjQzMA==&mid=2768628484&idx=1&sn=93dcc54ce807f7793739ee2fd2377056
https://mp.weixin.qq.com/s?src=11&timestamp=1620536401&ver=3057&signature=vCDI8FQcumnNGv4ScvFP-swQRlirdQSqTfjS8m-oFzgHMkqlNM3ljzjSevcjXLC-z-n0RzzMkNt-lwKMUaskfaqFFrpYZNq4ZCKkFFGj8L*KvH780aEUBJFvWTGmMGLC

为了下文叙述方便,会将以上三种URL分别称为“短链接”、“完整链接”与“搜狗临时链接”。观察上述三种URL,可以判断公众号文章链接具有的共同点是第一级路径为”s”,而其后跟随的参数则没有明确的语言含义。

微信公众平台使用的域名是mp.weixin.qq.com。它使用了mp这个子域名,而“微信公众平台”目前的官方英文名为”WeChat Official Accounts Platform”,即“微信官方号平台”,其中没有可以缩写出mp的单词。起初我由“公众”这个词猜测它可能指”mass platform”,不过根据下文“参见”中列出的一些介绍微信历史的文章,微信公众平台曾被称为“媒体平台”与“官号平台”。域名中mp这个缩写是“媒体平台”(media platform)留下的,而它目前使用的英文名则是“官号平台”留下的。

直接访问mp.weixin.qq.com会跳转至微信公众平台管理后台的登录页,该页面内还有一些指向微信公众平台开发文档与帮助文档的链接。

这种链接应当是最为经典的公众号文章链接了。目前版本的微信中,浏览文章时在菜单栏內选择“在浏览器打开”,便可在浏览器的地址栏内见到此种风格的URL。这也是将文章转发给好友时,气泡类型的链接实际使用的URL种类。

公众号文章会设置一个全局的JavaScript变量msg_link,它的值就是完整链接格式的文章URL。这个变量可直接在文章的HTML代码中找到。(使用时需要对其进行HTML entity decode,将&替换为&)

在Android版微信中使用“在浏览器打开”会把微信Webview内使用的URL直接传给浏览器应用,这样得到的URL会比上面例子中提到的更长,其中含有不少非必要的参数,其中部分相信可用于追溯出生成这个链接的微信用户的身份。

__biz,为business的缩写,对该公众号发布的所有文章均相同。参数值使用base64编码,解码后为一串数字,相信是公众号的ID。
mid,相信指”message id”,对应到公众号后台中“素材库”里的“图文消息”,它的值对于某一个公众号似乎是自增的。如果一条群发的“图文消息”内有多篇文章,那么其中各篇文章的mid值均相同。公众号支持的消息类型可参见微信公众平台群发接口的文档
idx,指”index”,值为该篇文章在某次群发中的顺序。若只发布了一篇文章,这个值就为1。
sn,指”signature”(下文详述),相信其格式为一个32位的16进制数,也即128位的2进制数。它应该是通过某种校验和算法生成的,而常用的128位散列算法只有MD5吧。当然,这个值是微信公众平台后端生成的,访问者只能将其原封不动地保存下来。

大致于2016年底某时开始,新文章的msg_link变量会带上chksm参数,显然指”checksum”。它的值为一76位的16进制数,即304位的2进制数。它可能是由多个散列值拼接而成,或是由较长的散列结果截短得到的。但实际访问时将其略去亦不会报错,且与其他几个必要的参数一样是恒定的,不知道有何作用。从链接长度方面考虑,分享链接时不妨将其略去,保留上述4个参数即可。

考虑到校验和常被用于确认下载到的文件内容是否与发布者的一致,我曾猜想这个值或许与修正文章错别字的功能有关,用于标记生成这个链接时文章的版本。但借用一位老友的公众号进行修改文字的实验后发现chksm并未改变,因此相信可以认为chksm也是文章发布后就不再变化的。

关于链接中的sn参数,一篇发布于2014年9月的文章《解读微信公众平台图文消息的链接组成》提到,早期的公众号文章链接没有sn参数。从上述的规则亦可以发现,凭__biz, mid, idx三个参数足以定位到某篇文章,sn的作用似乎有些多余。这篇文章的作者认为,这么做是为了防止未经授权的用户通过修改mid的方式查看素材库内尚未发布的图文消息,而sn是文章推送时生成的。记忆中生成预览链接时也会得到一个此种性质的参数,不过我暂时没有条件确认。

值得一提的是,这篇文章提到早期mid, idx, sn三个参数分别会被叫做appmsgid, itemidx, sign,这一点亦可由其文中举出冯大晖公众号“小道消息”于2013年发布的《业绩考核》一文的msg_link得到验证:https://mp.weixin.qq.com/s?__biz=MjM5ODIyMTE0MA==&appmsgid=10000382&itemidx=1。实验确认这两种参数名可以随意替换,互为别名。这些早期的文章仅需要前三个参数即可访问,不需要也没有sn

如果某个公众号进行了保留原始内容的“主体迁移”,迁移后的文章的这四个参数均会改变。

对于用户来说,sn的加入并不能带来什么好处,反倒拉长了URL,使体验恶化。这个改动保持了原有的链接结构,但着实给用户带来了一些麻烦。

根据一些 ,微信大致于2016年11月中旬启用了一种短链接——其实并不短,但相比于完整链接的长度,确实短不少。链接的值或许为某种变种的base64,其长度恒定为22个字符。其中不带有可用与追踪用户身份的参数。

不过,要想获得这种链接,只能使用微信客户端(手机和电脑版)内的“复制链接”功能,HTML中并无这种链接的痕迹。此外,“复制链接”似乎不是在WebView层实现的,研究这种短链接的生成应该需要对native代码进行逆向工程。个人怀疑它可能是对sn进行某种编码得到的,但这仅是猜想,没有依据。

对于早期那些没有sn的文章,“复制链接”的行为与在微信中直接点击文字链接打开的中“复制链接”似乎相同,无法得到这种短链接。但那些早期文章的链接本来就不算太长,无伤大雅。

考虑到可以由短链接得到完整链接(通过msg_link),且短链接在长度上显著较短,更符合一般用户对链接格式的认识(有人认为带有query string的URL较为不美观),我认为在分享微信公众号文章时应当尽量使用“复制链接”得到的这种短链接。

这种链接可谓是臭名昭著了。搜狗是腾讯自家的玩意儿,只有搜狗能索引微信里的文章。用户找到了搜狗的微信搜索,偶尔还得输好几个验证码,终于读到几篇文章,它们可能会被存进收藏夹或者发到其他地方。六小时后,这些好端端的页面突然就“链接过期”了。将这些链接复制到微信内打开,便能跳转到正常的文章页面。但我亦发现,将网上一些几年前的这种临时链接复制进微信,也只能得到“参数错误”,无法访问原文。或许是他们后端有调整,又或者这种链接与永久链接的对应关系仅会保留不多于半年的时间,我不得而知。

src,显然指”source”。目前发现的值有3和11两种,含义未知。
timestamp,生成这个链接时的UNIX Timestamp,在服务器返回查询结果时便已确定,即按下搜索键或翻页的时刻。
ver,显然指”version”。可能是生成下面signature使用的算法版本,每个数字会使用一段时间,可能不超过一天。
signature,某种签名。长度恒定为128个字符,其中带有星号。

有效期应为6小时,到期后只能通过微信客户端才能查看。

曾经这些页面里也有msg_link,可以在它还没过期时通过这个变量得到文章的正常URL。然而,微信在2016年底某时将这个变量的值以及其他提到sn的地方都抹掉了。

他们这么做的主要目的或许是为了反爬吧。但这种行为实在恶心用户,把用户产生的内容当作是自家的宝贝,捂在怀里,生怕见光。

参见

|←
Hello, World! →