分享 在 2019年 用 Rails 和 Python 做一款 RSS 阅读器 + 小圈子社交 (的服务端)

pc9527 · 2019年08月14日 · 最后由 pc9527 回复于 2019年08月15日 · 4250 次阅读

作为一个 20 年 Ruby 老粉,这篇文章主要分享一下我一开始觉得 4 周就能搞定的项目,是如何拖延到 4 个月的一些经验和教训。如果对产品有兴趣,请看文末的链接。

美好的规划

系统分析时,后端分 Web Server 和爬虫两块,作为 RSS 服务,对标 Feedly free plan 无可挑剔;社交部分对标 weibo,那就包括了:

数据 model 如下:

  • 用户
  • 评论
  • tag
  • 主题
  • 若干 HABTM 关联

Web Server 这块也就是这些 model 的 CRUD 嘛,对于一个 CRUD 老男孩来说这有何难?

爬虫这几年积累了好几种,分别是:

  • 使用 Python/feedparser 对应 RSS
  • 基于 Headless Chrome 的 Puppeteer,用 JavaScript 来对付使用 JavaScript 进行数据加载的 xx 新闻类网站
  • 对于成名新闻网站直接扒的 newspaper3K
  • 基于 Huginn 的监视一般 index/detail 类型的新闻网站更新

逻辑就是爬完了用统一的 http post 发到 Web Server 的接收 api 就齐活儿。

问题来了:

诸多异构爬虫的糟糕数据结构

Python/JavaScript/Ruby的爬虫都使用JSON进行数据汇总,基本的参数比如title/created_at/content/html/summary/author都没什么问题,一旦Web Server 端进行了 migration——比如 Atom 和 RSS 格式有用 published_at 的,newspaper3K 也有支持,表示文章的原始发布时间,更多爬来的文章没有这一项,会 fallback 到 created_at 上。published_at 这 column 本来没有,可以 migrate 一下吧?

Migrate 之后,发现要去 4 个爬虫进行修改,还是语言异构的——简直求生不得——直到某天一位爬虫界高人一语点醒:不应该使用任何 Headless Chrome 类的工具,如果是无需登录的资源,必然可以通过分析页面,而获得爬取方法。

干掉 Headless Chrome

我尝试了一下,某些要先下载 js 执行后再渲染的网站,手工进行 requests.get 和 parse 也很方便,这一步还可以数据化,在配置文件里写一段代码进行邪恶的 eval(配置文件里的代码都是自己准备的,不会有被黑的风险,没那么邪恶...吧)

关键是:省内存啊!为了无脑进行 DOM 生成和渲染需要加载 Headless Chrome 的 V8 引擎,连带这网站上所有的广告——最多我见过消耗 300M 内存的 Chrome 进程,而且如果不进行超时限制,4G 内存的豪华服务器也会被几个僵尸 worker 消耗到崩溃;设了超时吧,有些内容又加载不上——好吧,又得到人生一课:Headless Chrome 就不是拿来做生产爬虫用的。

干掉 Huginn

我的 Huginn 是跑在 Docker 里,只负责监视 URL,有动静了就按照预定义的规则扒下来 post 出去——用 Python 改造一下也可以嘛!那些 Huginn 自带的 DOM 操作,Python 用 selectolax.parser 也可以做。干掉 Huginn 之后,省了不少装 Docker Image 的硬盘空间。

因为要 NLP 所以终于同构了

每篇文章都要生成 summary 和 tags,原来的 Ruby 和 JavaScript 爬虫不能做,得靠 crontab 从 Web Server 里拿出来进行二次处理 (蛋疼到无以复加),把异构的爬虫重构以后,现在同构了,都遵循如下步骤:

  1. 从网站配置文件找 index-detail
  2. 看是否爬过了(使用本地的 sqlite3 存已经 post 成功的 url 和 title)
  3. 爬取文章本身
  4. 就地 NLP
  5. 连 summary/tags 一起 post 到 Web Server,一次成功

好像也不需要那么久啊?

只考虑 API only 的 Rails 服务和上述爬虫的确如此,但涉及到洗爬来的数据和反复重构,理论上的活儿也不少——还没到实战呢:

基本功能上线后,先拿 V2EX 月经贴:“诸位大佬平时写博客吗”里收集来的博客地址进行测试。

实战开始

为了易用性,可以直接输入博客的首页 HTML 地址而不是 RSS URL 添加源,这就需要在 Rails 里用 Ruby 对 HTML/RSS 进行预处理,一百多个博客录入下来:有超时的,有没提供 RSS/Atom 的,有拒绝除了 Webkit 之外 Agent 的(那你干嘛还出 RSS?),还有输出 RSS 格式不对的......还好 Feedjira 和 Nokogiri 够给力!

那也不到 4 个月吧?

作为全(hen)周(cha)期(qian)开发者,锅当然推到前端的 Vue/Element UI/Buefy一家三口身上了!不过那就是另一个故事了...

成品

阅读器已经做好上线了,产品逻辑在这篇文章里说了,有兴趣可以去体验一下:

移动版

桌面版

你这个爬取逻辑好复杂啊。我也想过做一个类似的,其实好像接 RSSHub 作为那些没有 RSS 源网站的补充就可以了,RSS Ruby 自己有包的,参考 Stringer。

ecnelises 回复

谢谢提供的新思路😄 RSSHub 的确是神器,以后就靠它了。 自己造 RSS 阅读器轮子,都想做点特色——ReadCog 的小圈子社交和 Stringer 标榜的 Anti-Social 八字不合......

需要 登录 后方可回复, 如果你还没有账号请 注册新账号