用爬虫监控政府网站更新:政策变动的第一时间掌握 一、为什么选择 RSS 爬虫的混合架构在信息碎片化的今天搭建一个属于自己的新闻聚合器是解决信息过载、高效获取行业动态的经典方案。而在采集层的技术选型上单一方案往往存在明显短板RSS 订阅标准化、低开销、合规性好是正规媒体的首选采集方式但大量新媒体、垂直站点并不提供 RSS 源且 RSS 输出内容常被截断无法获取全文。纯爬虫采集覆盖范围广可抓取任意公开网页但开发维护成本高容易触发反爬且站点结构变动就会导致解析失效长期稳定性差。混合采集方案正是结合两者优势优先使用 RSS 完成主流信源的稳定采集用爬虫补充无 RSS 站点的内容抓取再通过统一的清洗、去重、存储层打通数据链路兼顾了稳定性、覆盖率和维护成本。本文将从零开始完整讲解这套方案的设计与落地。二、整体架构设计整套聚合器采用分层架构从下到上分为采集层、处理层、存储层、服务层与展示层核心数据流如下采集层RSS 采集器 通用爬虫 站点专属爬虫并行执行采集任务调度层统一任务调度控制采集频率处理失败重试避免重复采集处理层HTML 正文提取、内容清洗、结构化标准化、相似度去重、标签分类存储层元数据存关系型数据库正文存文档数据库URL 去重存缓存数据库服务层RESTful API 提供内容查询、订阅管理、分类筛选能力展示层Web 前端 / RSS 输出 / 移动端适配实现内容阅读与管理核心技术栈选型开发语言Python生态完善RSS 解析与爬虫库成熟RSS 解析feedparser爬虫框架Requests BeautifulSoup轻量场景、Scrapy大规模站点、Playwright动态渲染页面任务调度APScheduler单机、Celery Redis分布式数据存储SQLite/PostgreSQL元数据、MongoDB正文内容、RedisURL 去重与缓存正文提取trafilatura通用全文提取前端Vue3 Vite轻量自建或直接使用 TinyTinyRSS / Miniflux 等开源前端三、RSS 采集模块稳定采集的基石RSS 是整个系统的首选采集通道开发成本极低且几乎不会触发反爬策略适合所有提供标准订阅源的站点。3.1 RSS 源的获取与管理首先需要维护一份订阅源清单推荐用数据库表存储核心字段包括源 ID、站点名称、RSS 链接、站点主页采集频率默认 30 分钟可按站点调整上次采集时间、上次采集状态、失败次数内容优先级、分类标签、是否启用全文抓取3.2 基础 RSS 解析实现使用feedparser可以一行代码完成 RSS/Atom 格式的兼容解析无需手动处理 XML 结构差异python运行import feedparser from datetime import datetime def fetch_rss_feed(feed_url: str) - list: 解析单个 RSS 源返回标准化的文章列表 feed feedparser.parse(feed_url) articles [] for entry in feed.entries: article { title: entry.get(title, ).strip(), url: entry.get(link, ), summary: entry.get(summary, ), author: entry.get(author, ), published: parse_publish_time(entry), source: feed.feed.get(title, ), fetch_type: rss } articles.append(article) return articles def parse_publish_time(entry) - datetime: 兼容多种 RSS 时间格式统一转为 datetime for time_field in [published_parsed, updated_parsed, created_parsed]: if hasattr(entry, time_field): time_tuple getattr(entry, time_field) return datetime(*time_tuple[:6]) return datetime.now()3.3 RSS 采集的常见坑与处理编码异常部分老旧站点 RSS 编码不规范需先手动指定编码再解析订阅源失效设置失败次数阈值连续失败 5 次自动降级采集频率并告警内容截断RSS 只输出摘要时标记为 需全文抓取交给后续爬虫模块补全正文增量采集记录上次采集的最新文章发布时间下次只拉取更新内容减少冗余解析四、爬虫采集模块覆盖无 RSS 站点对于不提供 RSS 的站点爬虫是必要的补充。我们将爬虫分为两类通用列表页爬虫适配大多数资讯站专属站点爬虫针对结构特殊的高优先级站点定制。4.1 通用列表页爬虫设计绝大多数资讯站点都遵循 列表页 详情页 的结构通用爬虫只需要配置列表页 URL、文章链接选择器、翻页规则即可完成基础采集python运行import requests from bs4 import BeautifulSoup from urllib.parse import urljoin def fetch_list_page(list_url: str, link_selector: str, base_url: str) - list: 从列表页提取所有文章详情页链接 headers {User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36} resp requests.get(list_url, headersheaders, timeout10) resp.encoding resp.apparent_encoding soup BeautifulSoup(resp.text, html.parser) links [] for a_tag in soup.select(link_selector): href a_tag.get(href, ) full_url urljoin(base_url, href) links.append(full_url) return list(set(links))4.2 正文提取通用方案优先定制规则兜底详情页的正文解析是爬虫的核心难点推荐优先使用通用正文提取库trafilatura它能自动识别页面正文区域过滤广告、导航、评论等噪声准确率远超手写选择器python运行import trafilatura def extract_article_content(html: str) - dict: 通用正文提取返回正文文本与元数据 result trafilatura.bare_extraction(html, include_linksFalse, include_imagesFalse) return { content: result.get(text, ), title: result.get(title, ), author: result.get(author, ), date: result.get(date, ) }对于通用提取效果差的站点再单独编写 CSS 选择器规则配置在站点规则表中形成 通用 定制 的双轨解析机制。4.3 反爬应对策略请求频率控制单站点请求间隔不低于 3 秒随机抖动 0.5-1 秒避免触发频率限制User-Agent 池维护 10 浏览器 UA每次请求随机切换代理池接入针对强反爬站点接入代理 IP 池降低封禁风险动态页面处理JavaScript 渲染的页面使用 Playwright 无头浏览器加载再提取渲染后的 HTML重试机制网络异常、5xx 错误自动重试 3 次指数退避403、429 直接降级暂停采集五、混合调度与去重避免重复与混乱两套采集体系并行最核心的问题是重复内容采集和任务冲突需要统一的调度层来管理。5.1 统一任务调度使用 APScheduler 管理所有采集任务按站点维度配置独立的采集周期主流新闻站点15-30 分钟采集一次垂直博客、小众站点2-6 小时采集一次更新频率极低的站点每日采集一次调度器只负责任务触发实际采集逻辑提交到线程池 / 进程池执行避免单任务阻塞整个调度队列。5.2 URL 级去重方案去重是聚合器的核心能力采用 缓存快速判断 数据库最终校验 的两级方案第一级Redis 布隆过滤器对文章 URL 做哈希后存入布隆过滤器采集前先查询存在则直接跳过内存占用极低查询速度毫秒级适合拦截绝大多数重复请求。第二级数据库唯一索引对文章 URL 建立唯一索引即使布隆过滤器出现误判最终写入数据库时也会触发唯一约束冲突保证数据不重复。5.3 内容级去重补充同一篇新闻常被多个站点转载仅靠 URL 去重无法识别。可以对正文标题 前 200 字计算 SimHash 指纹存入数据库新文章入库前比对指纹汉明距离小于 3 则判定为重复内容合并到同一主题下。六、数据清洗与标准化两套采集链路输出的数据格式差异很大必须经过统一的标准化处理才能保证后续存储和展示的一致性。6.1 字段标准化所有采集到的内容最终都统一为以下标准结构表格字段名类型说明idstring文章唯一 IDURL 哈希生成titlestring文章标题去除首尾空白与特殊字符urlstring原文链接标准化为完整绝对路径sourcestring来源站点名称authorstring作者无则留空summarystring摘要RSS 摘要或正文前 150 字contentstring纯文本正文去除 HTML 标签与广告content_htmlstring带格式的 HTML 正文可选published_atdatetime原文发布时间fetched_atdatetime采集时间tagsarray分类标签fetch_typestring采集类型rss /crawler6.2 内容清洗规则去除正文内的广告代码、推广文案、相关推荐、版权声明统一 HTML 标签过滤 script、style、iframe 等危险标签修复相对路径的图片链接转为绝对路径去除多余空白字符、换行符统一段落格式敏感词过滤与内容安全校验面向公网服务必须添加七、存储方案选型根据数据特性分层存储兼顾查询性能与存储成本。7.1 元数据存储PostgreSQL文章标题、URL、发布时间、来源、标签等结构化元数据存入关系型数据库便于做多条件筛选、排序、统计。核心建立三个索引发布时间倒序索引列表页查询核心来源站点索引按站点筛选URL 唯一索引去重兜底7.2 正文内容存储MongoDB正文属于长文本非结构化数据且不同站点字段差异大存入 MongoDB 更灵活通过文章 ID 与 PostgreSQL 元数据关联。全文检索需求可以直接使用 MongoDB 的文本索引中小规模场景足够使用。7.3 缓存与去重Redis布隆过滤器存 URL 去重指纹缓存热点文章内容减轻数据库压力存储采集任务状态、失败计数等运行时数据八、服务层与前端展示采集处理完成后需要一套接口与界面实现内容的消费。8.1 API 服务用 FastAPI 快速搭建 RESTful 接口核心接口包括GET /articles分页获取文章列表支持按来源、标签、时间范围筛选GET /articles/{id}获取文章详情GET /feeds获取订阅源列表POST /feeds新增订阅源GET /categories获取分类标签同时可以输出标准 RSS 格式支持用其他 RSS 阅读器直接订阅你的聚合器。8.2 前端实现如果不想从零开发前端可以直接对接开源 RSS 阅读器的后端或者用极简方案纯静态页面使用 Vue3 Element Plus实现列表 详情的双栏布局阅读体验优化支持夜间模式、字体大小调整、全文搜索订阅管理可视化新增、编辑、删除订阅源查看采集状态九、部署与运维9.1 单机部署方案个人使用场景下单机部署完全足够推荐使用 Docker Compose 一键编排应用容器Python 采集服务 FastAPI 服务数据库容器PostgreSQL MongoDB Redis反向代理Nginx 处理静态资源与接口转发9.2 运维监控要点采集成功率监控统计每个站点的采集成功率低于 80% 自动告警数据量统计每日新增文章数、重复内容占比、各来源贡献占比异常告警站点连续失败、服务宕机、磁盘占用过高时推送通知定期备份每日备份数据库避免订阅源配置与数据丢失十、进阶优化方向基础版本跑通后可以根据需求逐步扩展能力智能分类与推荐接入大语言模型对文章自动打标签、做摘要基于阅读习惯做个性化推荐全文检索升级引入 Elasticsearch 实现毫秒级全文搜索支持复杂关键词组合查询分布式扩展采集节点横向扩容用消息队列解耦采集、处理、存储流程支撑上万订阅源多端同步接入 Readwise、Instapaper 等稍后读服务支持移动端同步阅读进度订阅源自动发现输入站点主页自动识别 RSS 地址降低手动配置成本十一、总结RSS 爬虫的混合采集方案本质是用 20% 的成本解决 80% 的采集需求RSS 负责主力信源的稳定高效采集爬虫负责补全长尾站点的覆盖再通过统一的调度、去重、清洗层打通数据闭环。对于个人使用者这套方案从零搭建只需要一周左右的开发量就能获得一个完全可控、无广告、定制化的信息聚合入口。对于企业级场景也可以在此架构基础上扩展分布式能力与智能处理模块支撑更大规模的资讯采集与分析需求。