缘起

几年前我就不再使用微信公众号,同时将所有的公众号的「接收文章推送」功能关闭,免得触发小红点影响心情。不再使用公众号的原因主要有两点。

  1. 微信开始在公众号文章中插入广告。
  2. 微信将公众号由「订阅制模式」改为了「推荐制模式」,文章也由严格的时间排序改为了推荐顺序。

作为作者,我很欢迎这两个功能,插入广告允许作者获取一定收益。「订阅制」改「推荐制」也可以让新创账号更快的获取流量。

但作为读者,这两点我是排斥的。

不可否认的是,公众号平台上还是有很多优秀的创作者、内容,完全放弃似乎又有些因噎废食。

那么是否有办法在规避以上两个缺点的前提下,实现公众号的纯粹阅读?

我一直都有使用 RSS 阅读文章的习惯,使用 RSS 将各个信息源聚合在一个软件中,减少了无谓的软件安装,同时也提供了干净清爽的阅读体验。

TTRSS

若将公众号转为 RSS 岂不美哉?

几年前我其实写过一篇使用 RSS 订阅微信公众号的文章,当时介绍了几个方案。

  1. 通过「其他平台」订阅作者的文章。对有些作者来说,除了微信公众号,还会将文章发表在「博客」、「搜狐号」等平台,网络上可以直接找到这类平台的 RSS 源。
  2. 使用「WeRss - 微信公众号RSS订阅」(疑似也不可用了)等公众号转 RSS 的付费平台,这类平台直接提供公众号的 RSS 源,付费即可。
  3. 使用 「Feeddd」 等免费的工具,但这类方案「死」的比较快。

当时有使用过一段时间的「Feeddd」,但很快就不可用了,之后就没再怎么用过公众号。

最近「Vibe Coding」比较火,总想写点啥,借此机会打算让 AI 帮我写个公众号转 RSS 的工具。

方案调研

公众号转 RSS 的核心是获取公众号文章的链接,由于公众号只能在微信里使用,基本没有明面的接口获取链接,因此实现起来还是相当麻烦。调研了解了几种方案。

  1. 逆向分析微信客户端,找到微信客户端获取公众号文章的接口,进而模拟调用。
  2. 使用自动化手段,模拟人工的方式自动操作微信,获取指定公众号的链接。
  3. 通过微信读书的接口,获取公众号链接。
  4. 通过微信公众号平台的接口,获取公众号链接。

逆向

逆向分析客户端相当麻烦,需要一定的安全知识。从平台来说,又可分为逆向 Android 客户端、Windows 客户端。

Android 逆向相关的知识我是完全不懂,要搞基本要从头学,成本太高。

Windows 稍微懂一点,但近期微信 Windows 刚好大版本更新,从 3.x 升级到 4.x,整个底层架构完全进行了更新,网上相关的资料也还不多,自己完全从零逆向成本也很高。

而且最重要的是,每次微信版本更新,相关的接口都需要适配更新,维护起来也比较麻烦。再者一旦被微信检测到,很容易被封号。

我基本一开始就排除了逆向的方式。

自动化操作

之前使用的「Feeddd」使用的也是自动化的手段,在 Android 手机上安装一个软件,并安装相应的自动化脚本,之后就可以定时自动的打开微信,找到指定的公众号,并获取对应公众号的文章链接。

我比较倾向于这种方案。虽然自动化操作也要去分析程序控件等逻辑,但适配难度相较于逆向来说还是低很多。而且,这种方式不涉及微信代码的调用、修改等操作,被封号的概率也小的多。

自动化操作的劣势则主要是不稳定,毕竟不是直接调用接口,如果逻辑不完善,很容易在一些边界情况上出错。另外,毕竟是模拟人操作,整个流程不会很快。但就获取公众号链接这个场景来说,速度够了。

微信读书接口

除了微信,微信读书中也可以阅读公众号文章,基于微信读书的接口获取公众号链接并转 RSS 的方案有好几个,比如。

但这两个方案我都不打算使用。

第一个方案中的一些核心接口需要经过作者的服务器转发,具体会有什么逻辑不清楚。另外从一些 issue 反馈来看,微信读书也针对接口被滥用做了一些对抗,目前该方案也没那么的稳定。

第二个方案收费,直接放弃。

微信公众号平台接口

公众号作者都是在「微信公众号平台」上对自己的公众号进行管理的,公众号平台上可以查看到自己公众号的所有历史文章,也包括历史文章的链接。

但公众号平台似乎有 bug,可以使用自己的 token 去获取任意一个公众号的历史文章列表(只需要将接口里的公众号 id 由自己的替换为目标公众号的 id 即可)。因此只有自己有一个公众号账户,就可以获取任意公众号的文章列表了。

「微信公众号平台」是一个网站,其接口的模拟、调用相较于微信、微信读书客户端来说简单很多。

这种方式的一个开源方案是「 we-mp-rss」。

挺佩服作者竟能发现微信的这个 bug。不过既然开源并火起来,估计很快就会被微信官方知道,并将相关接口的 bug 修复掉。因此我也不打算使用这种方案。

其他

除了以上几个方案外,我在 IOS 上也尝试用抓包工具对微信进行了抓包,期望能够通过抓包捕获到公众号文章的链接,但微信的数据在 https 协议之上又有加密,要想解密还是得深入分析,还是算了。

实现

选择了自动化方案后,一开始想的就是自动化操作 Android 微信客户端。恰好有一台闲置的 Android 手机,一直登录着自己的小号。使用小号关注要订阅的微信公众号,然后使用程序定时的自动化获取指定公众号的链接即可。

祭出「VSCode」和「GitHub Copilot」,输入「Prompt 咒语」,整个工程很快就完成了。

大概瞄了下代码,整体基于「UIAutomator」自动化测试框架。逻辑上也很简单,找到公众号的位置,点击进入公众号文章列表,之后再依次获取公众号文章的链接。

但程序基本没法用,原因在于,生成的代码中的控件 ID 并不正确,实际执行起来并不能真正的点击到对应的控件。

在一番研究之后,获取到了正确的控件 ID,但手机又出问题。「UIAutomator」自动化测试框架基于手机的「无障碍」功能,但我在模拟操作几次之后,「无障碍」功能就会失效。手机是刷的「魔趣」系统,不确定是否是系统的问题,也懒得再去刷别的系统折腾。

既然 Android 客户端这条路不好走,于是转向了 Windows 客户端。一开始想着 Windows 上难有好用的自动化操作库,但通过「使用pywinauto驱动微信客户端实现公众号抓取 」这篇文章发现已有人做了相同的事情,自动化的逻辑主要靠「pywinauto」这个 Python 库进行驱动。

研究了下作者的文章和项目,相关的代码已经过时了,看起来还是需要自己从头实现了。

从前面 Android 客户端的经验来看,这类程序并不适合使用 AI 来写,毕竟控件的信息 AI 并不清楚,还是需要靠自己一点点测试研究才行,因此,整个程序最终还是靠「古法编程」完成。

核心的逻辑主要分几步。

  1. 查找微信公众号。
  2. 打开微信公众号文章列表。
  3. 遍历文章列表,并依次打开每篇文章,获取文章的标题,并获取文章的链接。

查找公众号代码。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from pywinauto.application import *
from pywinauto import backend

# 定位到微信窗口
wx_pid = backend.registry.backends["uia"].element_info_class().children(title="微信")[0].process_id
app = Application(backend="uia").connect(process=wx_pid)
main_win = app.window()
main_win.set_focus()

# 定位到搜索框
edit_win = main_win.children(control_type="Group")[0].children()[0].children()[0].children()[2].children()[1].children()[0].children()[0].children()[0].children()[1].set_focus()

# 搜索「折腾往事」公众号
edit_win.type_keys("折腾往事{ENTER}")

获取公众号列表代码。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
from pywinauto.application import *
from pywinauto import backend

# 定位到公众号窗口
gzh_app = Application(backend="uia").connect(title="公众号")
gzh_win = gzh_app.window(title="公众号")
gzh_win.set_focus()

# 获取公众号列表文本控件
text_list = gzh_win.descendants(control_type="Text")

# 遍历控件并点击每个「阅读」文本,打开公众号文章窗口
for text_ctrl in text_list:
	if text_ctrl.element_info.name.find("阅读") == 0:
		text_ctrl.click_input()

获取公众号标题和链接代码。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
from pywinauto.application import *
from pywinauto import backend

# 定位到新打开的公众号窗口
gzh_app = Application(backend="uia").connect(title="公众号")
ww_win = gzh_app.window(title="微信")
doc_ctrl = ww_win.child_window(control_type="Document")

# 获取文章标题
article_title = doc_ctrl.element_info.name

# 获取文章链接
ww_win.child_window(control_type="MenuItem", title="更多").click_input()
ww_win.child_window(control_type="MenuItem", title="复制链接").click_input()
article_url = clipboard.GetData()

以上只是核心逻辑,在实际的执行中还涉及到很多边界等意想不到的情况,导致自动化逻辑出错,我还在不断测试完善中。

由于操作的是 Windows 版微信,因此需要一台电脑长期登录着微信,且最好不要在程序抓取链接时有任何操作。恰好我有一台闲置的 Windows 服务器,可以长期登录着小号的微信,配合这个程序刚好可以每日定时抓取公众号链接并生成 RSS 源。

效果

折腾rss.gif

最后

整个流程倒是能跑通,但还是如前所述,边界情况比较多,总是在这里或那里出错,这也是自动化类方案的弊端,需要一点点的不断完善。

抓取速度上确实不快,不过订阅的公众号也就十几个,每日更新一次的话,十几个公众号轮一遍也就 20 多分钟,完全可以接受,而且应该能够规避掉微信的风控检测。

这里只抓取了公众号文章的链接,文章完整内容的抓取并没有在这个程序中实现,实际使用中可以配合 RSS 软件的全文抓取插件实现。

在实际的体验上,使用 RSS 阅读公众号虽然解决了时间顺序、无广告等问题,但也不是没有任何弊端,最直接的是。

  1. 排版不佳。使用 RSS 阅读需要抓取文章全文,抓取的全文排版没有公众号中那么友好。
  2. 无法看到评论。RSS 天生不支持评论,无法与作者进行互动,这也是 RSS 逐渐消亡的原因之一。对于有些公众号来说,评论区才是精华,就这类公众号来说,还是建议直接在微信中阅读。

如果不在乎时间排序问题,只是不想看到微信公众号里的广告,这里也有一个有代价的办法关闭公众号以及朋友圈的广告,具体可以看我之前写的「去广告杂谈」。