RSS | RSSHub搭配Miniflux,实现订阅自由

3945字

artist 本文作者没有任何代码基础,所有说明性文字主要靠连蒙带猜兼灵光一现,参考时请务必注意,欢迎提出意见和给出建议~

总之先来预览(炫耀)一下最终效果:

忽略掉不知道为什么截图显得这么糊,效果还是很不错的嘛!


RSS是什么,有什么用?

RSS(英文全称 Site Summary 或 Really Simple Syndication,中文译作简易资讯聚合,也称聚合内容,是一种讯息来源格式规范,用以聚合多个网站更新的内容并自动通知网站订阅者。使用 RSS 后,网站订阅者便无需再手动检视网站是否有新的内容,同时 RSS 可将多个网站更新的内容进行整合,以摘要的形式呈现,有助于订阅者快速获取重要资讯,并选择性地点阅检视。

以上定义来自维基百科,以我个人的理解来说,RSS是一种与算法推荐相对的信息获取方式:自主选择信息源,并将内容按照时间顺序更新到一个单独的阅读器当中。在这个过程中,没有“猜你喜欢”,订阅者可以重新拿回获取信息的主动权,一定程度上避免信息过载。

少数派有一篇文章详细介绍了RSS,可以扩展阅读:高效获取信息,你需要这份 RSS 入门指南


获取订阅源:RSSHub

实际上,由于各社交媒体的兴起和互联网环境的逐渐封闭,RSS这一信息获取方式早已式微(这一式微趋势甚至可以追溯到2013年Google关闭Google Reader始,不过RSS似乎也从来没有主流过),出于绑住用户等考虑,微博、知乎、微信公众号等信息获取渠道均不支持RSS,通过RSS来获取信息,变成了一件“看起来很美”的事情。

但式微不代表消失,没有官方提供的订阅源,我们可以自己制作,这就是RSSHub项目。通过项目作者及开源社区的一系列努力,自建或使用官方提供的演示地址,我们“可以给任何奇奇怪怪的内容生成 RSS 订阅源”。


通过Vercel搭建RSSHub

RssHub官方给出了很多种自建RSSHub的办法,我用了比较简单熟悉的一种:通过Vercel搭建。

(官方给出了Vercel的一键搭建方式,但这里我用了另一种搭建方式,将RSSHub的仓库复制到自己的Github仓库中)

  1. 点击RSSHub的仓库地址,将仓库Fork到自己的仓库中。
  2. 打开Vercel,新建一个项目,将刚刚Fork的RSSHub仓库导入到项目当中。
  3. 什么也不改动,点击部署,大概两分钟后部署成功。
  4. 如果有域名,就去设置里套一个域名,没有的话就可以直接用Vercel提供的二级域名。

RSSHub搭建成功

Roelxy写了另一种利用Docker-compose搭建RSSHub的方式,可以参考这篇文章


如何使用RSSHub

参考官方文档,根据说明替换示例地址为自己的自建地址即可。

此外还可以使用RSSHub的一些衍生项目,例如RSSHub Radar ,通过浏览器扩展的形式自动检测当前页面有没有 RSS 和 RSSHub 支持。还比如RSSBud,一个 RSSHub的辅助 iOS App,支持 使用RSSHub 的通用参数实现过滤、获取全文等功能,安卓则可以使用RSSAid


开始阅读:Miniflux

搭一个RSS阅读器的想法则由来已久:原因很简单,因为大部分RSS阅读器都(对我来说)不好用。

是,Feedly是很好,但收费,又被墙了。Inoreader也很好用,还有个没被墙的国内地址,但非常不稳定,时不时我就没办法打开。Reeder5非常好,是我见过最漂亮的阅读器,甚至没有之一,我也没有跨区下载的障碍,但试验了半个月后,我还是更习惯在网页上进行阅读——大批IOS阅读器也随之被毙了。

最后我发现,除了自建,我竟然没有选择。

——其实自建也没有太多选择。

搭建前花了点时间来找有什么支持Docker搭建的项目比较合适:Tiny Tiny RSSFreshRSS蚁阅RSSMiniflux RSS。挑一下:FreshRSS界面相对传统,我不是很喜欢,可以作为备用;蚁阅我用过,作者提供了开箱即用和自主搭建两个选项,开箱即用按订阅制收费,试用30天后30元/年,相对简单清爽很多,但每次点开一篇文章要先显示摘要,再点一次才显示全文,没什么必要。实际搭建过Tiny Tiny RSS后,发现反应速度极慢,也臃肿。最后只剩下Miniflux,作者倡导极简主义并提供custom css选项,渐进式网络应用程序,响应式设计,可以发送到手机桌面来视为APP使用。

好!我喜欢!就它了!


搭建Miniflux

这里完全参考Roelxy的搭建文章

安装Docker&Docker compose后创建目录,我把目录命名为MinifluxRss,实际搭建可以换成任意名字。

mkdir MinifluxRss
cd MinifluxRss

编辑配置文件docker-compose.yml

nano docker-compose.yml
#内容和注释均参考自Roelxy文章
version: '3'
services:
  miniflux:
    image: miniflux/miniflux:latest
    ports:
      - "80:8080"   #80是默认对外开放的端口,建议改为其他任意未被占用的端口,我改成了8080:8080
    depends_on:
      - db
    environment:
      - DATABASE_URL=postgres://miniflux:secret@db/miniflux?sslmode=disable   #数据库url,secret需要修改为数据库密码
      - RUN_MIGRATIONS=1
      - POLLING_FREQUENCY=60   #抓取feed的时间间隔(单位为分钟)
      - CREATE_ADMIN=1
      - ADMIN_USERNAME=admin   #管理员帐号用户名
      - ADMIN_PASSWORD=test123   #管理员帐号密码,用户名与密码之后可以在网页中进行修改
    restart: unless-stopped

  db:
    image: postgres:10.1
    environment:
      - POSTGRES_USER=miniflux    #数据库用户名
      - POSTGRES_PASSWORD=secret   #数据库密码,默认secret
    volumes:
      - ./miniflux-db:/var/lib/postgresql/data

之后docker-compose up -d让容器上线,镜像被下载后就可以通过https:<服务器IP>:<端口号>来打开Miniflux并使用管理员账号登录使用了。


遇到的报错

碰到了两个玄学鬼故事:容器上线了,但是显示没有容器:docker-compose up -d显示容器上线,但服务不能成功连接,docker ps后不显示容器。

这个好像是因为配置文件写错了,不确定是不是因为我改动了postgres的版本,重新写过配置文件后修复了。

另一个鬼故事是miniflux镜像不断重启。

试了几个解决办法,应该是下面这个方法解决了:

docker-compose down #让docker-compose下线
docker images #展示镜像ID
docker rmi 镜像ID #删除对应镜像
rm -rf miniflux-db #删除持久化数据
docker-compose up -d #重新上线docker-compose

利用Nginx进行反代

简而言之,给它一个域名:记得事先在CF或者域名商处添加二级域名的A记录,IP指向服务器IP。

apt install nginx -y #安装Nginx

之后编辑配置文件:nano /etc/nginx/conf.d/miniflux.conf

server {
    listen 80;
    server_name  ;

    location / {
        proxy_pass http://127.0.0.1:8080;    
        proxy_redirect off;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

之后试运行一下:nginx -t

#返回以下结果即为成功
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

之后运行Nginx:

nginx -s reload
systemctl start nginx
systemctl enable nginx

并签发SSL证书,按照提示输入即可:

certbot --nginx

对Miniflux进行美化

Miniflux支持custom css,可以自己编写主题,使用方式非常简单,将css代码填入custom css框中即可。

我的主题参考自Roni Laukkarinen的暗色主题,更改为明亮配色方案使之更适合我自己的阅读习惯。

最终CSS设置如下:

@import url("https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap");@import url("https://fonts.googleapis.com/css2?family=Merriweather:wght@700;900&display=swap");@import url(""https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@300;700&display=swap";");:root{--color-white:#000;--width-body:900px;--item-title-link-line-height:1.23;--entry-content-line-height:1.5;--item-title-link-font-size:28px;--font-size-smaller:16px;--font-size-meta:var(--font-size-smaller);--font-size-larger:20px;--font-size-body:14px;--item-meta-color:#000;--item-meta-li-color-hover:rgb(43,19,19);--entry-date-color:#000;--input-placeholder-color:#000;--entry-header-border-color:transparent;--pagination-border-color:transparent;--font-family:"Inter",system-ui,-apple-system,"Segoe UI",roboto,"Helvetica Neue",arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--body-color:#888;--body-background:#f6f6f6;--hr-border-color:#555;--title-color:var(--color-white);--link-color:var(--color-white);--link-focus-color:#ddd;--link-hover-color:#ddd;--header-list-border-color:#333;--header-link-color:#000;--header-link-focus-color:rgba(82,168,236,0.85);--header-link-hover-color:rgba(82,168,236,0.85);--header-active-link-color:#9b9494;--page-header-title-color:var(--color-white);--page-header-title-border-color:#333;--logo-color:#bbb;--logo-hover-color-span:#bbb;--table-border-color:#555;--table-th-background:#fff;--table-th-color:var(--color-white);--table-tr-hover-background-color:#333;--table-tr-hover-color:var(--color-white);--button-primary-border-color:transparent;--button-primary-background:#333;--button-primary-color:#f8f8f8;--button-primary-focus-border-color:#888;--button-primary-focus-background:#555;--input-border:0;--input-background:#e0e0e0;--input-color:#000;--input-focus-color:#000;--input-focus-border-color:rgba(82,168,236,0.8);--input-focus-box-shadow:0;--alert-color:#000;--alert-background-color:transparent;--alert-border-color:transparent;--alert-success-color:#2eb54d;--alert-success-background-color:transparent;--alert-success-border-color:transparent;--alert-error-color:#efefef;--alert-error-background-color:#333;--alert-error-border-color:#888;--alert-info-color:#efefef;--alert-info-background-color:transparent;--alert-info-border-color:transparent;--panel-background:#f8f8f8;--panel-border-color:#555;--panel-color:#9b9b9b;--modal-background:#333;--modal-color:#efefef;--modal-box-shadow:0 0 10px rgba(82,168,236,0.6);--pagination-link-color:var(--color-white);--category-color:#efefef;--category-background-color:#333;--category-border-color:#888;--category-link-color:#999;--category-link-hover-color:var(--color-white);--item-border-color:transparent;--item-padding:10px;--item-title-link-font-weight:700;--item-status-read-title-link-color:#aaa;--item-status-read-title-focus-color:rgba(82,168,236,0.6);--item-meta-focus-color:var(--color-white);--item-meta-li-color:#888;--current-item-border-width:2px;--current-item-border-color:transparent;--current-item-box-shadow:0 0 8px rgba(82,168,236,0.6);--entry-header-title-link-color:var(--color-white);--entry-content-color:var(--color-white);--entry-content-code-color:var(--color-white);--entry-content-code-background:#e0e0e0;--entry-content-code-border-color:transparent;--entry-content-quote-color:#777;--entry-content-abbr-border-color:#777;--entry-enclosure-border-color:#333;--parsing-error-color:#eee;--feed-parsing-error-background-color:transparent;--keyboard-shortcuts-li-color:#9b9b9b;--counter-color:#bbb;--feed-has-unread-background-color:#f8f8f8;--feed-has-unread-border-color:#f6f6f6}@media (max-width:600px){:root{--item-title-link-font-size:24px}}@media (max-width:600px){:root{--item-padding:15px}.logo{color:var(--color-white);font-family:'Merriweather',serif;font-weight:1200;margin-left:0;position:relative;font-size:80px;}}.logo{color:var(--color-white);font-family:'Merriweather',serif;font-weight:900;font-size:18px;margin-left:0;position:relative}@media (max-width:600px){.logo{margin-bottom:40px}}.logo a,.logo a span{color:var(--color-white);font-family:'Merriweather',serif;font-weight:900;letter-spacing:0;transition:opacity 0.5s cubic-bezier(0.19,1,0.22,1),background-color 0.2s cubic-bezier(0.19,1,0.22,1)}.logo a:hover,.logo a:hover span,.logo a:focus,.logo a:focus span{color:var(--color-white);opacity:.7}body{font-size:var(--font-size-body);-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;max-width:var(--width-body);text-rendering:geometricPrecision}body main,body .header{padding-bottom:var(--item-padding);padding-left:var(--item-padding);padding-right:var(--item-padding);padding-top:var(--item-padding)}.header ul,.header li{list-style:none}.flash-message,.alert-success{padding-left:5px;padding-right:5px}.page-header>h1{padding-bottom:10px}.page-header+p.alert,.page-header+p.alert.alert-info{background-image:url("https://www.rollemaa.fi/done.svg");background-position:center bottom;background-repeat:no-repeat;background-size:400px auto;border-radius:4px;height:400px;overflow:auto;text-align:center}@media (max-width:600px){.page-header+p.alert,.page-header+p.alert.alert-info{background-size:230px auto;height:250px}}.page-header>ul{list-style:none;margin-left:0;margin-top:10px;padding-left:0}.page-header>ul a{font-size:var(--font-size-larger);opacity:.5;text-decoration:none;transition:opacity 0.5s cubic-bezier(0.19,1,0.22,1),background-color 0.2s cubic-bezier(0.19,1,0.22,1)}.page-header>ul a:hover,.page-header>ul a:focus{opacity:1}.items{overflow:visible}body .item{display:grid;margin-bottom:20px;margin-top:30px;overflow:visible;padding-left:0;padding-right:0}.item-title img{display:none}.item-title{display:block;font-size:var(--item-title-link-font-size);line-height:var(--item-title-link-line-height)}.category{background-color:transparent;border:0;border-radius:0;clear:left;display:inline-block;float:left;font-size:inherit;margin-bottom:10px;margin-left:0;margin-right:10px;margin-top:10px;padding:0;white-space:normal}.category a{border:1px solid transparent;border-radius:2em;color:var(--category-color);font-size:12px;font-weight:500;line-height:18px;padding:2px 7px}.category a[href="/category/1/entries"]{background-color:#2e363e;border-color:#5b6876;color:#f8f8f8}.category a[href="/category/2/entries"]{background-color:#2e363e;border-color:#5b6876;color:#f8f8f8}.category a[href="/category/3/entries"]{background-color:#2e363e;border-color:#5b6876;color:#f8f8f8}.category a[href="/category/4/entries"]{background-color:#2e363e;border-color:#5b6876;color:#f8f8f8}.category a[href="/category/5/entries"]{background-color:#2e363e;border-color:#5b6876;color:#f8f8f8}.category a[href="/category/6/entries"]{background-color:#2e363e;border-color:#5b6876;color:#f8f8f8}.category a[href="/category/7/entries"]{background-color:#2e363e;border-color:#5b6876;color:#f8f8f8}.category a[href="/category/8/entries"]{background-color:#2e363e;border-color:#5b6876;color:#f8f8f8}.category a[href="/category/9/entries"]{background-color:#2e363e;border-color:#5b6876;color:#f8f8f8}.category a[href="/category/10/entries"]{background-color:#2e363e;border-color:#5b6876;color:#f8f8f8}.item-meta li,.item-meta a{color:var(--item-meta-li-color)}.item-meta a{transition:color 0.5s cubic-bezier(0.19,1,0.22,1),background-color 0.2s cubic-bezier(0.19,1,0.22,1)}.item-meta a:hover,.item-meta a:focus{color:var(--item-meta-li-color-hover)}.item-meta .item-meta-info{margin-top:5px}.item-meta .item-meta-icons{clear:both;float:left;opacity:.7;transition:opacity 0.5s cubic-bezier(0.19,1,0.22,1),background-color 0.2s cubic-bezier(0.19,1,0.22,1)}.item-meta .item-meta-icons:hover,.item-meta .item-meta-icons:focus{opacity:1}.entry{padding:0}.entry-content{line-height:var(--entry-content-line-height)}body .entry-header h1{margin:80px 0 20px}.entry-header{margin-bottom:40px;margin-top:60px;position:relative}.entry-header .entry-date{font-size:16px;font-style:normal;margin-bottom:-30px;margin-top:0;opacity:.4;text-align:right}.entry-header .entry-meta{display:flex;flex-wrap:wrap;margin-bottom:10px}.entry-header .entry-meta *{font-size:var(--font-size-meta)}.entry-header .entry-meta .category{float:none;margin-bottom:20px;margin-top:0;order:1;width:100%}.entry-header .entry-meta .category a{font-weight:500;line-height:18px;padding:4px 12px}.entry-header .entry-meta .entry-website{filter:grayscale(100%);order:2;position:absolute;right:0}.entry-header .entry-meta .entry-author{margin-left:0;margin-top:25px;order:3;position:absolute;right:0}@media (max-width:600px){.entry-header,body .entry-header h1{margin-top:0}}.entry header h1 a:hover,.entry header h1 a:focus{color:var(--color-white)}.entry-website,.entry-author,.entry-date{color:var(--item-meta-color);opacity:.8}.entry-website a,.entry-author a,.entry-date a{color:var(--item-meta-color);opacity:1}.item-meta .icon,.entry-actions .icon{margin-right:5px}a:focus{outline:.1em dashed currentColor;outline-offset:.35em}@media (prefers-reduced-motion:no-preference){.item.current-item,.item,:focus,*:focus,[data-whatinput="keyboard"]:focus,[data-whatinput="keyboard"] a:focus{outline-offset:5px;transition:outline-offset .25s ease}}.pagination-top{display:none}.pagination{padding:var(--item-padding)}body .pagination-bottom{margin-bottom:60px}body .pagination-bottom a{font-size:var(--font-size-smaller);text-decoration:none}.page-footer{margin-bottom:30px;margin-top:30px;padding-left:0;padding-right:0}.page-footer a{font-size:var(--font-size-larger);opacity:.5;text-decoration:none;transition:opacity 0.5s cubic-bezier(0.19,1,0.22,1),background-color 0.2s cubic-bezier(0.19,1,0.22,1)}.page-footer a:hover,.page-footer a:focus{opacity:1}.page-header li,.page-footer li{list-style:none}body .page-header ul,body .page-footer ul{margin-left:0}.entry-content pre,.entry-content code{border-radius:6px}.entry-content pre code{font-size:var(--font-size-smaller)}.entry-content pre{line-height:1.33;padding:15px}.feed-entries-counter{font-size:var(--font-size-smaller);margin-left:5px;opacity:.5;position:relative;top:12px}input[type="search"],input[type="url"],input[type="password"],input[type="text"],input[type="number"],select,textarea{background-color:var(--input-background);border:0;border-radius:0;color:var(--input-color);padding:10px}.button-primary{border:0;border-radius:0;padding:10px 20px}article.feed-parsing-error{border-color:transparent}.parsing-error{margin-top:10px}.item.current-item{border:1px dotted var(--item-border-color);box-shadow:none;display:grid;margin-bottom:20px;margin-top:30px;outline:.2em dashed #3df57f;outline-offset:20px;overflow:visible;padding-bottom:var(--item-padding);padding-left:0;padding-right:0;padding-top:var(--item-padding)}.item.current-item *:focus{outline:none}*::selection{color:#fff;background-color:#2e363e;}

合并阅读渠道:Kill the Newsletter

在阅读器中阅读Newsletter

Newsletter,中文翻译为(机构定期寄发给成员的)通讯、简报,或者电子报。与RSS类似,订阅者主动选择自己感兴趣的作者与内容进行订阅,自主筛选接受的信息源,而对于作者来说,区别于“喊话”式地在平台发布文章,每一封Newsletter都能精准地抵达订阅者手中,是一种有明确写作对象的写作方式。

我平常也订阅Newsletter,有一些相当喜欢的作者,之前一直使用Instapaper的邮件订阅功能,但既然有了自己的RSS阅读器,将Newsletter和RSS阅读区分开无疑很不方便,于是找了找有没有比较好的解决办法。

答案是:Kill the Newsletter(。

这是个蛮有意思的服务,点开网址,随意输入一个收件箱名称,Kill the Newsletter会生成虚拟收件地址和对应的RSS订阅源,在Newsletter订阅中填入虚拟收件地址,在RSS阅读器中填入订阅源,就可以实现用RSS来订阅Newsletter

注意:虚拟邮箱生成是一次性的,因此一定要自己保存好。

简单、方便、实用、甚至开源,好耶!


另一种邮件转为RSS的方法

2022年2月2日更新:实际使用Kill the Newsletter两个月后,我发现这个服务很不稳定,短短两个月就报废了两个订阅地址:无一例外提示为无法解析。

反复开新的邮件地址来进行订阅显然很不实际又麻烦,翻了翻发现还有一种轮子可以用,就是用Cloudflare Workers搭配Testmail.app。

具体操作方法参考这篇文章:mail2rss


我的Newsletter

我最近也开了一个Newsletter,如果有人有缘分看到这里,点击订阅链接即可订阅。

(以下内容来自于之前在Mastodon的嘟嘟)

写(折腾)个人博客115天,我开始想要一些新玩具:相对更轻松的写作体验,相对更直接地看到文章“被阅读”,以及狡兔三窟,为博客添加更多的阅读渠道。于是我开通了我的个人Newsletter:如果你对我感兴趣,点击:订阅链接 ,并点击邮件/微信订阅,我新写的文章就会以服务号/邮件的形式直接推送到你的微信/邮箱当中。 (目前还没有内容,之前写的文章可以参考个人博客)

我的Newsletter会以稳定两周/封,中间随机插入更新的频率进行推送,包括个人生活双周报、游戏、书影音感想、阅读收获和随机侃侃等内容。写个人博客是非常兴趣所向的事情,虽然没有人看我也会继续写——但如果看到这篇嘟嘟的朋友愿意订阅的话,我会非常开心并发送被鼓励到的谢意!

(此外,如果你想订阅我的内容,但不愿意用Newsletter的形式,我的博客也支持RSS订阅。)