这类项目最值得先看的不是功能列表,而是它到底解决了什么实际问题。很多人收藏了一堆RSS订阅,但最后还是用手机刷信息流,因为打开阅读器这个动作本身就有门槛。把RSS内容做成电子墨水报,核心价值在于 被动接收 和 低干扰阅读 ——信息像一份真正的报纸一样,定时、自动地呈现在一个不伤眼、不弹通知的设备上,帮你从主动“刷”信息,回归到被动“收”信息。
它适合两类人:一是信息过载,想减少手机使用时间的人;二是喜欢RSS这种自主订阅模式,但希望阅读体验更专注、更护眼的极客或文字工作者。最关键的能力不是显示技术,而是 自动化流程 :如何把分散的订阅源聚合、格式化,并推送到一个常显的电子墨水屏设备上。
下面我会按实际把一个想法落地成可运行系统的顺序,拆解整个过程。我会假设你有一台基础的Linux服务器(树莓派或云主机)和一个支持刷机的电子墨水屏设备(如Kindle、文石,或专门的墨水屏显示器)。
1. 先想清楚:你的“墨水报”需要哪些核心环节
在动手写代码或配置工具前,得先把数据流理清楚。一个完整的“RSS转墨水报”系统,通常包含四个环节,缺一不可:
- 信息获取与聚合 :从多个RSS订阅源抓取最新内容。
- 内容处理与格式化 :清洗HTML、提取正文、统一排版,转换成适合墨水屏显示的格式(如纯文本、简单HTML或PDF)。
- 推送与同步 :将处理好的“报纸”文件发送到墨水屏设备。
- 设备展示 :墨水屏设备接收文件并刷新显示。
每个环节都有多种技术选型,你的选择决定了系统的复杂度和维护成本。
1.1 环节一:选对信息聚合器,别从零开始写爬虫
自己写RSS爬虫解析各种网站格式是个无底洞。直接用成熟的RSS阅读器后端或库,是更稳妥的选择。
-
方案A:使用在线RSS服务(如Feedbin、Inoreader)的API
- 优点 :稳定、省心。这些服务已经帮你处理了订阅源抓取、去重、内容缓存和API输出。
- 缺点 :通常是付费服务,且你的数据在第三方。
- 关键点 :调用它们的API获取文章列表和内容。你需要一个API Key。
-
方案B:自建RSS聚合服务(如FreshRSS、Miniflux)
- 优点 :数据完全自主,免费,可定制性强。
- 缺点 :需要自己维护一个服务,处理订阅源的更新和解析。
- 关键点 :在服务器上通过Docker或包管理工具安装。之后,你的“墨水报”程序就从这个自建服务的数据库或API里取数据。
-
方案C:使用本地RSS库(如Python的
feedparser)- 优点 :最轻量,不依赖外部服务,适合订阅源不多、变化不频繁的场景。
- 缺点 :需要自己处理网络请求、错误重试、内容缓存,稳定性需要更多代码保障。
-
关键点
:写一个定时脚本,循环遍历你的订阅源URL列表,用
feedparser解析。
我的建议 :如果只是个人使用,订阅源在几十个以内,从 方案C 开始最简单。如果想一劳永逸,并且不介意付费, 方案A 的Feedbin是非常好的选择,它的API清晰稳定。 方案B 适合技术控和隐私要求极高的用户。
1.2 环节二与三:格式转换与推送,决定最终体验
墨水屏设备千差万别,推送方式决定了你每天要花多少时间在维护上。
-
格式选择 :
- 纯文本 (.txt) :兼容性最强,任何能显示文本的设备都支持。但会丢失所有格式和图片。
- HTML单文件 :可以保留简单的排版(标题、段落、列表)。需要墨水屏设备的内置浏览器支持。
- PDF :排版固定,跨设备显示效果一致。但生成PDF对服务器资源要求稍高,且文件体积大。
- 图片 :将排版好的内容渲染成图片(如PNG)。兼容性极好,但无法选中文字,且文件体积最大。
-
推送方式 :
- 邮件推送 :最通用。让墨水屏设备(如Kindle)绑定一个专属邮箱,服务器端将生成的“报纸”作为附件发送到这个邮箱,设备会自动收取并显示。这是与Kindle配合的经典方案。
- Web服务器同步 :在服务器上运行一个简单的HTTP服务,提供“报纸”文件的下载链接。在墨水屏设备上写一个脚本,定时去拉取这个文件。这需要你的墨水屏设备能运行脚本(如刷了Kobo系统的设备)。
- 物理传输 :通过USB连接设备,用脚本自动拷贝文件。这要求设备一直连着服务器,不适合远程场景。
我的建议 : 邮件推送 + PDF格式 是平衡效果和便利性的最佳组合。PDF能保证排版,邮件推送几乎支持所有带3G/4G或Wi-Fi的电子书阅读器,实现真正的“云端推送,自动接收”。
2. 环境准备:搭建一个能7x24小时运行的核心服务器
你的“编辑部”需要一台不关机的服务器。树莓派放在家里,或者一台最基础的云服务器(如腾讯云/阿里云的轻量应用服务器)都可以。
2.1 基础系统与包管理
以Ubuntu/Debian系统为例。首先确保系统更新,并安装必要的工具。
sudo apt update && sudo apt upgrade -y
sudo apt install -y python3-pip git curl wget
如果你在macOS上开发,可能会用到Homebrew来安装一些依赖。但在生产服务器上,通常直接用系统包管理器(
apt
)或Python的
pip
就够了。Homebrew主要用于macOS桌面环境,不要在生产Linux服务器上强行安装。
2.2 核心Python环境与依赖
我们的处理脚本主要用Python写。建议使用虚拟环境隔离依赖。
# 安装虚拟环境工具
sudo apt install -y python3-venv
# 创建项目目录并进入
mkdir ~/rss2paper && cd ~/rss2paper
# 创建Python虚拟环境
python3 -m venv venv
# 激活虚拟环境
source venv/bin/activate
激活后,命令行提示符前会出现
(venv)
字样。接下来安装核心Python库:
pip install feedparser beautifulsoup4 requests reportlab markdown
-
feedparser: 用于解析RSS/Atom订阅源。 -
beautifulsoup4: 用于从HTML内容中清洗和提取纯文本或特定内容。 -
requests: 用于网络请求(如果某些订阅源需要)。 -
reportlab: 一个强大的PDF生成库。 -
markdown: 如果你希望支持Markdown格式的订阅源,可以用它来转换。
2.3 邮件服务配置(如果选择邮件推送)
你需要一个发件邮箱。 强烈建议使用专门的邮件发送服务(如SendGrid、Mailgun)或你的域名邮箱服务商提供的SMTP ,而不是直接用个人QQ、163邮箱的SMTP,后者容易被封且有限制。
以配置SendGrid为例(它有免费额度):
- 注册SendGrid账号,创建一个API Key。
-
在服务器上,不需要额外安装软件,Python的
smtplib和email库是内置的。你只需要在脚本里配置好SMTP服务器地址、端口、账号和API Key(或密码)即可。
3. 核心实现:从RSS到PDF报纸的生成脚本
这是整个系统的“排版车间”。我们采用
方案C(本地
feedparser
) + PDF格式 + 邮件推送
的路径来演示。
3.1 第一步:编写RSS抓取与内容提取脚本
创建一个文件
fetch_rss.py
:
#!/usr/bin/env python3
import feedparser
from bs4 import BeautifulSoup
import html2text
from datetime import datetime
import time
# 配置你的RSS订阅源列表
FEED_URLS = [
'https://rsshub.app/zhihu/daily',
'https://sspai.com/feed',
'https://feeds.feedburner.com/ruanyifeng',
# 添加更多你喜欢的订阅源
]
def fetch_feeds():
all_articles = []
h = html2text.HTML2Text()
h.ignore_links = False
h.body_width = 0 # 不自动换行
for url in FEED_URLS:
print(f"正在抓取: {url}")
try:
feed = feedparser.parse(url)
if feed.entries:
for entry in feed.entries[:5]: # 每个源只取最新5条,避免报纸过长
# 提取标题、链接、发布时间和内容
title = entry.get('title', '无标题')
link = entry.get('link', '#')
published = entry.get('published_parsed', entry.get('updated_parsed', None))
if published:
pub_date = time.strftime('%Y-%m-%d %H:%M', published)
else:
pub_date = '日期未知'
# 处理内容,优先使用summary或description
content_html = entry.get('summary', entry.get('description', ''))
# 使用BeautifulSoup清理一些不必要的标签
soup = BeautifulSoup(content_html, 'html.parser')
for tag in soup(['script', 'style', 'iframe', 'ins', 'ads']):
tag.decompose()
cleaned_html = str(soup)
# 将HTML转换为纯文本,保留基本格式
content_text = h.handle(cleaned_html)
article = {
'title': title,
'link': link,
'date': pub_date,
'source': feed.feed.get('title', url),
'content': content_text[:1000] # 限制内容长度,避免PDF过大
}
all_articles.append(article)
except Exception as e:
print(f"抓取 {url} 时出错: {e}")
time.sleep(1) # 礼貌性延迟,避免请求过快
# 按发布时间排序(最新的在前)
all_articles.sort(key=lambda x: x['date'], reverse=True)
return all_articles
if __name__ == '__main__':
articles = fetch_feeds()
print(f"共抓取到 {len(articles)} 篇文章。")
# 这里可以先打印几篇看看效果
for a in articles[:2]:
print(f"\n---\n来源: {a['source']}\n标题: {a['title']}\n日期: {a['date']}\n")
运行这个脚本
python fetch_rss.py
,确认能正常抓取到文章标题和部分内容。
这是第一个验证点
:确保你的订阅源地址有效,网络通畅,并且
feedparser
能正确解析。
3.2 第二步:编写PDF生成脚本
创建一个文件
generate_pdf.py
:
#!/usr/bin/env python3
from reportlab.lib.pagesizes import A4
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, PageBreak
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_CENTER
from reportlab.lib import colors
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from datetime import datetime
import fetch_rss # 导入我们刚才写的抓取模块
# 注册中文字体(关键!否则中文显示为空白)
# 你需要准备一个中文字体文件,如`simsun.ttc`(宋体),放到项目目录下
try:
pdfmetrics.registerFont(TTFont('SimSun', 'SimSun.ttf'))
CHINESE_FONT = 'SimSun'
except:
print("警告:未找到中文字体,将使用默认字体,中文可能显示异常。")
CHINESE_FONT = 'Helvetica'
def create_paper(articles, filename='daily_paper.pdf'):
doc = SimpleDocTemplate(filename, pagesize=A4,
rightMargin=72, leftMargin=72,
topMargin=72, bottomMargin=72)
styles = getSampleStyleSheet()
# 创建自定义样式
styles.add(ParagraphStyle(name='ChineseTitle',
parent=styles['Heading1'],
fontName=CHINESE_FONT,
fontSize=16,
spaceAfter=12,
alignment=TA_CENTER))
styles.add(ParagraphStyle(name='ChineseSource',
parent=styles['Heading2'],
fontName=CHINESE_FONT,
fontSize=10,
textColor=colors.grey,
spaceAfter=6))
styles.add(ParagraphStyle(name='ChineseBody',
parent=styles['BodyText'],
fontName=CHINESE_FONT,
fontSize=10,
leading=14, # 行距
spaceAfter=12,
alignment=TA_LEFT))
story = []
# 报纸头版标题
today = datetime.now().strftime('%Y年%m月%d日')
story.append(Paragraph(f"每日墨水报<br/>{today}", styles['ChineseTitle']))
story.append(Spacer(1, 24))
for i, article in enumerate(articles):
# 文章标题
story.append(Paragraph(article['title'], styles['ChineseTitle']))
# 来源和日期
source_line = f"来源: {article['source']} | 发布时间: {article['date']}"
story.append(Paragraph(source_line, styles['ChineseSource']))
# 文章内容
# 将纯文本中的换行符替换为HTML的<br/>,以便Paragraph识别
content_with_br = article['content'].replace('\n', '<br/>')
story.append(Paragraph(content_with_br, styles['ChineseBody']))
story.append(Spacer(1, 12))
# 每5篇文章后分页,避免单篇文章跨页太多
if (i + 1) % 5 == 0:
story.append(PageBreak())
doc.build(story)
print(f"PDF报纸已生成: {filename}")
return filename
if __name__ == '__main__':
articles = fetch_rss.fetch_feeds()
if articles:
pdf_file = create_paper(articles)
print(f"成功生成报纸,包含 {len(articles)} 篇文章。")
else:
print("未抓取到任何文章,无法生成报纸。")
关键点 :
-
中文字体
:这是PDF生成最大的坑。ReportLab默认不支持中文。你必须将一个中文字体文件(如
.ttf或.ttc)放在项目目录,并在代码中注册。你可以从系统字体目录(如/usr/share/fonts)复制一个,或使用开源字体。 -
内容长度
:在
fetch_rss.py中我们对文章内容做了截断(content_text[:1000]),这是为了防止某篇文章过长导致PDF体积爆炸。你可以根据需求调整。 -
样式调整
:
leading(行距)、spaceAfter(段后间距)等参数需要根据你的墨水屏尺寸和分辨率微调,以达到最佳阅读效果。
运行
python generate_pdf.py
,如果一切正常,会在当前目录生成一个
daily_paper.pdf
文件。
这是第二个验证点
:用PDF阅读器打开,检查中文是否正常显示,排版是否清晰。
3.3 第三步:编写邮件推送脚本
创建一个文件
send_email.py
:
#!/usr/bin/env python3
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
import os
from datetime import datetime
# ====== 配置区 ======
SMTP_SERVER = 'smtp.sendgrid.net' # 以SendGrid为例
SMTP_PORT = 587
SENDER_EMAIL = 'your_sender_email@yourdomain.com' # 发件人邮箱
SENDER_PASSWORD = 'your_sendgrid_api_key' # 这里是API Key,不是邮箱密码
RECEIVER_EMAIL = 'your_kindle_email@kindle.com' # 你的Kindle接收邮箱
# ====================
def send_paper_via_email(pdf_file_path):
if not os.path.exists(pdf_file_path):
print(f"错误:PDF文件不存在 {pdf_file_path}")
return False
msg = MIMEMultipart()
msg['From'] = SENDER_EMAIL
msg['To'] = RECEIVER_EMAIL
today = datetime.now().strftime('%Y-%m-%d')
msg['Subject'] = f'每日墨水报 {today}'
# 邮件正文
body = f"""
您好,
这是您订阅的每日墨水报 ({today}),已生成PDF附件。
此邮件由自动化系统发送,请勿直接回复。
"""
msg.attach(MIMEText(body, 'plain'))
# 添加PDF附件
with open(pdf_file_path, 'rb') as f:
pdf_attachment = MIMEApplication(f.read(), _subtype='pdf')
pdf_attachment.add_header('Content-Disposition', 'attachment',
filename=os.path.basename(pdf_file_path))
msg.attach(pdf_attachment)
try:
server = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
server.starttls() # 启用TLS加密
server.login(SENDER_EMAIL, SENDER_PASSWORD)
server.send_message(msg)
server.quit()
print(f"邮件已成功发送至 {RECEIVER_EMAIL}")
return True
except Exception as e:
print(f"邮件发送失败: {e}")
return False
if __name__ == '__main__':
# 假设PDF文件名为 daily_paper.pdf
pdf_file = 'daily_paper.pdf'
if os.path.exists(pdf_file):
send_paper_via_email(pdf_file)
else:
print("请先运行 generate_pdf.py 生成PDF文件。")
关键配置 :
-
将
SENDER_EMAIL,SENDER_PASSWORD,RECEIVER_EMAIL替换成你自己的信息。 -
SENDER_PASSWORD对于SendGrid是API Key,对于其他服务商可能是专用密码或授权码。 -
Kindle用户注意
:你需要将发件邮箱(
SENDER_EMAIL)添加到亚马逊Kindle的“已认可的发件人电子邮箱列表”中,否则Kindle不会接收附件。这个设置在亚马逊官网的“管理我的内容和设备” -> “首选项” -> “个人文档设置”里。
运行
python send_email.py
测试邮件发送。
这是第三个验证点
:检查你的邮箱(或Kindle)是否成功收到带附件的邮件。
4. 自动化与部署:让系统自己运转起来
手动运行三个脚本太麻烦。我们需要一个总控脚本,并用系统的定时任务让它每天自动执行。
4.1 创建总控脚本
创建一个文件
main.py
,按顺序调用所有功能:
#!/usr/bin/env python3
import subprocess
import sys
import os
from datetime import datetime
def run_script(script_name):
"""运行指定的Python脚本,并检查其返回值"""
try:
result = subprocess.run([sys.executable, script_name], check=True, capture_output=True, text=True)
print(f"[OK] {script_name} 执行成功。")
print(result.stdout)
if result.stderr:
print(f"[STDERR] {result.stderr}")
return True
except subprocess.CalledProcessError as e:
print(f"[FAILED] {script_name} 执行失败,返回码: {e.returncode}")
print(f"标准输出:\n{e.stdout}")
print(f"标准错误:\n{e.stderr}")
return False
def main():
print(f"=== 开始生成每日墨水报 {datetime.now().strftime('%Y-%m-%d %H:%M')} ===")
# 1. 抓取RSS
if not run_script('fetch_rss.py'):
print("RSS抓取失败,终止流程。")
return
# 2. 生成PDF
if not run_script('generate_pdf.py'):
print("PDF生成失败,终止流程。")
return
# 3. 发送邮件
pdf_file = 'daily_paper.pdf'
if os.path.exists(pdf_file):
# 这里直接导入发送模块的功能
from send_email import send_paper_via_email
if send_paper_via_email(pdf_file):
print("=== 流程执行完毕 ===")
else:
print("邮件发送失败。")
else:
print(f"错误:未找到PDF文件 {pdf_file}")
if __name__ == '__main__':
main()
4.2 设置Linux定时任务(Cron)
我们希望每天早晨7点自动生成并发送报纸。
-
首先,确保你的总控脚本有执行权限:
chmod +x main.py -
编辑当前用户的cron任务:
crontab -e -
在打开的编辑器中添加一行(假设你的项目在
/home/pi/rss2paper, 且使用虚拟环境):0 7 * * * cd /home/pi/rss2paper && /home/pi/rss2paper/venv/bin/python /home/pi/rss2paper/main.py >> /home/pi/rss2paper/cron.log 2>&1-
0 7 * * *表示每天7:00执行。 -
cd /home/pi/rss2paper切换到项目目录。 -
&& /home/pi/rss2paper/venv/bin/python使用虚拟环境中的Python解释器。 -
>> /home/pi/rss2paper/cron.log 2>&1将脚本的所有输出(包括错误)追加到日志文件,方便排查。
-
-
保存并退出。Cron会自动加载新任务。
验证定时任务
:你可以将时间改为几分钟后,或者手动运行
python main.py
来测试整个流程。查看生成的
cron.log
文件,确认每一步都执行成功。
5. 进阶优化与问题排查
系统跑起来只是第一步,长期稳定运行需要处理更多细节。
5.1 内容优化:让“报纸”更易读
-
去重
:不同订阅源可能转载同一篇文章。可以在
fetch_rss.py中根据文章标题或链接进行简单的去重。 - 摘要生成 :对于过长的文章,可以尝试用简单的算法(如提取前N句)或调用摘要API(注意API调用成本和稳定性)生成摘要,而不是粗暴截断。
- 分类排版 :在生成PDF前,将文章按来源或你定义的分类(科技、生活、财经)分组,在报纸中分栏或分章节显示。
- 添加目录 :如果文章很多,可以在PDF开头生成一个带页码的目录。
5.2 稳定性保障:应对网络波动和失败
-
重试机制
:在
fetch_rss.py的抓取环节,对每个订阅源添加重试逻辑(如try...except包裹,失败后重试2次)。 -
超时设置
:使用
requests库时,为请求设置超时(如timeout=10),避免某个源卡住整个流程。 -
失败通知
:如果整个流程失败,可以发送一封报警邮件到你的个人邮箱,而不是Kindle。可以在
main.py的try...except最外层捕获异常,并调用一个发送错误邮件的函数。 - 日志记录 :除了cron的日志,在关键步骤(开始抓取、抓取成功/失败、生成PDF、发送邮件)添加更详细的日志记录到单独的文件,方便追溯。
5.3 设备端适配:不只是Kindle
-
其他电子书阅读器
:如果设备不支持邮件推送,但支持Wi-Fi和脚本(如刷了Kobo系统的设备),可以在设备上写一个简单的Shell脚本,定时从你的服务器Web服务(可以用Python的
http.server快速搭建)下载PDF文件,并用内置阅读器打开。 - 墨水屏显示器/相框 :这类设备通常运行完整Linux系统。你可以将生成PDF的步骤直接放在设备上,或者让设备从服务器拉取渲染好的图片。需要根据设备的具体API或显示方式进行适配。
-
纯文本显示
:对于只能显示文本的极简设备,可以跳过PDF生成,直接生成一个
.txt文件,通过scp或rsync同步到设备上。
5.4 常见问题排查清单
当你的“墨水报”没有按时出现时,按这个顺序检查:
-
看日志
:首先检查
cron.log文件。错误信息通常就在这里。 - 查权限 :确认cron任务使用的用户有权限执行脚本、写入当前目录、访问网络。
-
验环境
:在cron任务命令中,Python路径和虚拟环境激活是关键。最稳妥的方法是,在cron中直接使用虚拟环境Python的绝对路径(如我们上面做的),而不是先
source activate。 -
测网络
:手动在服务器上运行
python fetch_rss.py,看是否能正常抓取订阅源。可能是服务器网络问题,或某个订阅源地址失效。 - 查邮件 :登录你的发件邮箱SMTP服务商后台(如SendGrid控制台),查看邮件发送日志,是否有被拒绝、退信等情况。
-
查订阅源
:个别订阅源结构变化可能导致
feedparser解析失败。尝试单独解析那个源的URL,检查返回的数据结构。 - 查存储 :服务器磁盘是否满了?导致无法生成PDF。
这个方案的优势在于,所有组件都是可控、可修改的。你可以从最简单的文本PDF开始,逐步增加分类、摘要、更好的排版,甚至加入天气预报、待办事项等内容,打造一份真正属于你自己的个性化数字报纸。它最大的回报不是省下了看手机的几分钟,而是重新找回了对信息流的主动权。



2111

被折叠的 条评论
为什么被折叠?



