基于Python实现新闻爬取系统
前言「爬一个新闻站」和「做一个新闻爬取系统」的区别在于前者抓一页就结束后者要不停地把「列表页发现链接、详情页抽取正文、结果去重入库」这条流水线跑通而且中途任何一个环节出错都不能让整批任务崩掉。常见误解是「新闻站结构简单写个正则就行」。实际新闻页恰恰是最不规整的一类正文里嵌着广告区块、相关阅读、图片说明标题可能同时存在于title和h1日期格式五花八门。用事件驱动的解析器按标签收文本比用正则稳得多。本文用标准库搭一个最小但完整的新闻爬取系统骨架urllib.request发请求、html.parser抽字段、sqlite3去重入库全程遵守 robots.txt 并限速。示例适用于 Python 3.8 及以上。需要先说清楚合规边界本文的域名是占位示例实际使用必须换成你自己有权抓取的站点并先确认其 robots.txt 与服务条款。新闻内容受著作权保护采集结果只适合做个人研究或趋势统计不要整篇转载。Python 2 已于 2020 年 1 月 1 日停止维护本文只给 Python 3 写法。一、系统的四个部分一个新闻爬取系统本质是四段流水线环节输入输出本文工具发现列表页 HTML文章链接清单HTMLParserurljoin抓取文章链接详情页 HTMLurllib.request抽取详情页 HTML标题、正文HTMLParser子类入库结构化字段数据库行sqlite3分段的好处是哪一段出问题就换哪一段不影响其他环节。抓取失败不该导致已入库的数据丢失抽取规则变了也不该影响链接发现。二、列表页发现文章链接列表页的任务只有一个——把详情页地址找出来。做法和通用爬虫一样覆写handle_starttag对a标签取href再用urljoin补成绝对地址。# 适用于 Python 3.8from html.parser import HTMLParserfrom urllib.parse import urljoin, urlsplitclass LinkParser(HTMLParser):def __init__(self, base):super().__init__(convert_charrefsTrue)self.base baseself.links []def handle_starttag(self, tag, attrs):if tag ! a:returnfor name, value in attrs:if name href and value:self.links.append(urljoin(self.base, value))def list_page_links(html, base, hostNone):parser LinkParser(base)parser.feed(html)parser.close()result []seen set()for link in parser.links:parts urlsplit(link)if parts.scheme not in (http, https):continueif host and parts.hostname ! host:continueif link in seen:continueseen.add(link)result.append(link)return result过滤同站用urlsplit(link).hostname不要用字符串前缀判断否则带相同前缀的其他域名会混进来。三、详情页抽取标题与正文详情页抽取的关键是「只收想要的标签里的文本」。新闻正文通常集中在一连串p标签里标题在h1或title里。用状态标志来决定当前是否处于目标标签内。# 适用于 Python 3.8from html.parser import HTMLParserclass ArticleParser(HTMLParser):从详情页中抽取标题与正文段落。def __init__(self):super().__init__(convert_charrefsTrue)self.title self.paragraphs []self._in_title Falseself._in_h1 Falseself._in_p Falseself._buf []def handle_starttag(self, tag, attrs):if tag in (title, h1):self._in_title tag titleself._in_h1 tag h1self._buf []elif tag p:self._in_p Trueself._buf []def handle_data(self, data):if self._in_title or self._in_h1 or self._in_p:self._buf.append(data)def handle_endtag(self, tag):if tag title and self._in_title:self._in_title Falseself._set_title()elif tag h1 and self._in_h1:self._in_h1 Falseself._set_title()elif tag p and self._in_p:self._in_p Falsetext .join(self._buf).strip()if text:self.paragraphs.append(text)def _set_title(self):if not self.title:self.title .join(self._buf).strip()def body(self):return \n.join(self.paragraphs)三个容易忽略的点convert_charrefsTrue默认值会把amp;之类的实体自动还原成字符属性值可能是None取href时要判空handle_data在标签内外都会被调用靠状态标志过滤。四、入库与去重用url做主键INSERT OR IGNORE天然完成去重同一个地址抓两次也只留一条。# 适用于 Python 3.8import sqlite3def init_db(conn):conn.execute(CREATE TABLE IF NOT EXISTS articles ( url TEXT PRIMARY KEY, title TEXT, body TEXT, fetched_at TEXT))conn.commit()def save_article(conn, url, title, body, fetched_at):conn.execute(INSERT OR IGNORE INTO articles (url, title, body, fetched_at) VALUES (?, ?, ?, ?),(url, title, body, fetched_at),)conn.commit()用参数占位符?传值不要用字符串拼接 SQL——那既容易出错也是 SQL 注入的入口。这里的值虽然来自解析结果而非用户输入但养成参数化习惯没有坏处。实战完整的采集流程把四段串起来加上 robots 检查、异常隔离和限速。# 适用于 Python 3.8import datetimeimport sqlite3import timeimport urllib.errorimport urllib.requestimport urllib.robotparserfrom urllib.parse import urlsplitUSER_AGENT NewsReader/0.1 (contact: meexample.com)HEADERS {User-Agent: USER_AGENT}DB news.dbdef robots_allows(url, user_agent):parts urlsplit(url)rp urllib.robotparser.RobotFileParser()rp.set_url(parts.scheme :// parts.netloc /robots.txt)try:rp.read()except urllib.error.URLError:return False # 拿不到 robots.txt 就保守处理return rp.can_fetch(user_agent, url)def fetch_text(url, timeout10):req urllib.request.Request(url, headersHEADERS)with urllib.request.urlopen(req, timeouttimeout) as resp:raw resp.read()charset resp.headers.get_content_charset() or utf-8return raw.decode(charset, errorsreplace)def main():list_url https://www.example.com/news/host urlsplit(list_url).hostnameif not robots_allows(list_url, USER_AGENT):print(robots.txt 不允许抓取列表页退出)returnhtml fetch_text(list_url)links list_page_links(html, list_url, hosthost)print(发现文章链接:, len(links))conn sqlite3.connect(DB)init_db(conn)for link in links[:5]: # 演示只取前 5 条if not robots_allows(link, USER_AGENT):print(robots 禁止:, link)continuetry:page fetch_text(link)except urllib.error.HTTPError as e:print(HTTP 错误:, e.code, link)continueexcept urllib.error.URLError as e:print(网络错误:, e.reason, link)continueparser ArticleParser()parser.feed(page)parser.close()now datetime.datetime.now().isoformat(timespecseconds)save_article(conn, link, parser.title, parser.body(), now)print(入库:, parser.title or link)time.sleep(1) # 限速conn.close()if __name__ __main__:main()注意main里只保存了links[:5]用于演示。真实系统会分页遍历列表页、把链接推到队列、抽不到正文的页面记录原因以便回头排查。常见坑点用正则抽正文❌re.findall(rp(.*?)/p, html)—— 换行、嵌套标签、属性变化都会漏。 ✅ 用HTMLParser子类按标签收文本状态标志控制范围。标题只认一种来源❌ 只看h1—— 有些页面标题在title里或在h1里带站点后缀。 ✅ 两个都收先到先得再按需做清洗。忘记urljoin补全相对地址❌ 直接把href2024/01-01.html入库后续无法再次访问。 ✅ 用urljoin(base, href)转成绝对地址。用字符串前缀判断同站❌link.startswith(https://www.example.com)—— 前缀相同的其他域名会混入。 ✅ 比较urlsplit(link).hostname。列表页一页到底不去重❌ 同一个链接被多个区块重复引用就抓多遍。 ✅ 入seen集合去重入库用url主键 INSERT OR IGNORE。用字符串拼接 SQL❌INSERT INTO articles VALUES ( url )—— 地址里有引号就出错。 ✅ 用参数占位符conn.execute(sql, (url, ...))。一处失败拖垮整批❌ 不包try第 3 个链接超时后整个脚本退出。 ✅ 逐个链接包异常处理失败就跳过并记录。不查 robots、不限速❌ 循环里无间隔请求列表页和详情页。 ✅ 先can_fetch判断请求之间time.sleep。总结环节关键 API要点发现链接HTMLParser.handle_starttag取href后urljoin补全抽取正文HTMLParser.handle_data用状态标志只收目标标签文本去重入库sqlite3INSERT OR IGNOREurl做主键容错HTTPError/URLError逐个链接隔离失败不中断合规urllib.robotparsertime.sleep先查 robots再限速系统的骨架就是这条流水线把它跑稳之后提升点在于分页遍历、增量抓取和抽取规则的维护。先把「发现—抓取—抽取—入库」四段各自隔离好系统才有长大的空间。

相关新闻

Linux 8.5安装Oracle 21C单实例:完整步骤与避坑指南

Linux 8.5安装Oracle 21C单实例:完整步骤与避坑指南

简介:面向需要在Linux 8.5上部署Oracle 21C数据库的运维与DBA人员,这份PDF文档完整记录了单实例环境从零到可用的全过程。内容涵盖软件准备、部署规划、虚拟机创建、操作系统安装及系统参数优化等核心环节,尤其对hosts配置、SELinux调整、防火…

2026/10/11 19:24:26 阅读更多 →
微信聊天记录导出全攻略:解密SQLite数据库并生成HTML/Word/CSV

微信聊天记录导出全攻略:解密SQLite数据库并生成HTML/Word/CSV

简介:这款微信聊天记录提取与分析工具,专门面向有长期备份与复盘需求的个人用户,以及希望研究聊天数据解析和可视化的开发者。它支持将微信聊天导出为HTML、Word、CSV等常用格式,便于永久存档、跨平台查阅与二次编辑;并…

2026/10/11 19:24:26 阅读更多 →
Android文件管理器源码详解:分区存储与权限适配实战

Android文件管理器源码详解:分区存储与权限适配实战

简介:这份资源是面向Android开发初学者与进阶者的文件管理器完整项目源码,基于Android Studio 4.2.1开发,可直接运行于近两年官网下载的AS版本。项目围绕SD卡目录管理展开,涵盖新建、查看、搜索、删除文件或文件夹以及返回根目录等…

2026/10/11 19:24:26 阅读更多 →

最新新闻

地下2米土壤墒情监测:管式监测仪如何改变灌溉决策

地下2米土壤墒情监测:管式监测仪如何改变灌溉决策

这大概是不少果园主、农场主都遇到过的怪事:叶片中午蔫下去,你赶紧浇水,浇了一小时,第二天反而更蔫。挖开土一看,表层10厘米明明是湿的,可往下翻到30厘米,手指甲都掐不进去的干土块,…

2026/10/11 20:13:02 阅读更多 →
ONNXRuntime 部署 PP-MattingV2 实时人像抠图:Python 与 C++ 推理实战

ONNXRuntime 部署 PP-MattingV2 实时人像抠图:Python 与 C++ 推理实战

简介:这份资源面向深度学习部署与计算机视觉方向的开发者,提供在ONNXRuntime上运行PaddleSeg实时人像抠图模型PP-MattingV2的完整实践材料,可用于社交媒体、视频编辑、虚拟现实等场景中发丝级人像分离的落地验证。压缩包共7个文件&#xff0c…

2026/10/11 20:13:02 阅读更多 →
AI File Sorter 文档分析指南:5 个步骤让 PDF/Office 文件自动获得清晰文件名

AI File Sorter 文档分析指南:5 个步骤让 PDF/Office 文件自动获得清晰文件名

AI 应用大模型本地部署桌面应用 【免费下载链接】ai-file-sorter Cross-platform desktop application for content-aware file organization and renaming. Supports local and remote LLMs, preview-based workflows, and fully user-controlled changes. 项目地址&#xff1…

2026/10/11 20:13:01 阅读更多 →
5分钟上手Minari:离线RL数据集安装、下载与加载快速入门

5分钟上手Minari:离线RL数据集安装、下载与加载快速入门

【免费下载链接】Minari A standard format for offline reinforcement learning datasets, with popular reference datasets and related utilities 项目地址: https://gitcode.com/gh_mirrors/mi/Minari 点击查看 免费下载 Minari 是一个专为**离线强化学习&…

2026/10/11 20:13:01 阅读更多 →
openJiuwen agent-core 文档解析器基类 Parser 深度解析:从抽象接口到多格式自动路由的完整实现

openJiuwen agent-core 文档解析器基类 Parser 深度解析:从抽象接口到多格式自动路由的完整实现

人工智能AI AgentAgent 框架大模型工具调用RAG提示工程强化学习 【免费下载链接】agent-core openJiuwen agent-core可提供AI Agent开发、运行、调优与演进相关的全套SDK能力 项目地址: https://gitcode.com/openJiuwen/agent-core 点击查看 免费下载 导读 Parser…

2026/10/11 20:13:01 阅读更多 →
Flink/PyFlink CSV读写实战:Schema声明与参数配置避坑

Flink/PyFlink CSV读写实战:Schema声明与参数配置避坑

先说个我上个月接手的真实任务:一批传感器历史数据以 CSV 文件存在对象存储里,需要灌进 Flink 流作业做实时指标计算。文件不大,三十来个分区,每分区几万行,字段也就四五个。我当时觉得这是最没技术含量的一步&#xf…

2026/10/11 20:12:01 阅读更多 →

日新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:54 阅读更多 →