尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Python爬虫实战:Requests+Xpath爬取新闻并Flask+Echarts可视化
简介这份资源是一套基于Python、Flask与Echarts构建的观察者新闻网爬虫项目源码面向具备一定Python基础、希望学习网络爬虫与数据可视化开发的进阶学习者。项目通过Requests发送请求结合etree解析HTML并利用Xpath精准定位新闻信息覆盖首页与更多新闻页面两部分爬取逻辑爬取结果经清洗后持久化存储并借助Echarts将新闻分布与热度等指标以图表形式直观呈现形成采集、处理、展示的完整闭环。压缩包共130个文件约9.29MB包含7个py核心脚本、1个sql建表文件、1个md说明文档以及大量js、css、html、jpg、png等前端与静态资源另有pyc编译文件与woff2、ttf等字体文件结构完整便于直接运行与二次开发。目前已有45人学习下载适合作为新闻研究、市场分析的数据支撑工具也可作为掌握Flask与爬虫技术的实践案例同时提醒开发者关注robots.txt规则与数据合规使用。1. 观察者新闻网爬虫从 Requests 到 Echarts 的一条完整数据链路很多人做爬虫练手卡在爬下来一堆 JSON 不知道给谁看。这个项目的价值不在于爬虫本身有多难而在于它把一条完整链路串起来了用 Requests 发请求、用 etree 加 Xpath 解析 HTML、把新闻落库存储、再用 Flask 把数据吐给 Echarts 做可视化。观察者网这类新闻站点的首页和更多新闻列表页结构规整、分页清晰是练手 RequestsXpath 组合的理想靶子比一上来啃反爬严重的电商站友好得多。这套方案适合三类人刚学完 python 基础想找个能跑通的实战项目的新手想搞明白 flask 开发网站和前后端数据对接的入门者以及需要一套爬取—存储—展示最小闭环模板的开发者。读完你应该能自己把首页和更多新闻页爬下来、存进数据库、用 Echarts 饼图和柱状图把新闻分类分布画出来。下面按先立原理、再动手、最后避坑的顺序拆开讲。2. 请求与解析Requests 发请求、etreeXpath 取字段的完整写法2.1 为什么是 Requests 配 lxml.etree而不是别的组合Python 爬虫的请求库主流就两个Requests 和 httpx。Requests 同步、API 直观、文档厚对新闻站这种请求量不大、结构稳定的场景完全够用没必要上异步。解析侧常见的是 BeautifulSoup、正则、lxml。BeautifulSoup 容错好但慢正则写新闻列表容易翻车lxml 的 etree 配合 Xpath 在速度和表达力上最平衡——Xpath 能直接按 DOM 层级定位比 CSS 选择器更适合处理列表里嵌套标题、时间、链接这种结构。这里要区分一个高频混淆点浏览器里用的 Xpath 和 lxml 里的 Xpath 有差异。浏览器能用的//div[classnews]/a在 lxml 里通常也能用但浏览器会自动补全 tbody、会执行 JS 渲染而 Requests 拿到的是原始 HTML没有 JS 执行结果。所以定位元素前一定先用requests.get把源码打出来看别直接照抄浏览器审查元素里的路径。2.2 最小可运行抓一页首页并解析出新闻列表先装依赖再写第一版脚本。注意 python 安装和库安装是两回事装完 python 后用 pip 装库pip install requests lxml flaskimport requests from lxml import etree # 观察者网首页实际以你抓取时的域名为准 URL https://www.guancha.cn/ HEADERS { User-Agent: ( Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36 ) } def fetch(url): resp requests.get(url, headersHEADERS, timeout10) resp.encoding resp.apparent_encoding # 中文站点防乱码 resp.raise_for_status() return resp.text def parse_home(html): tree etree.HTML(html) items [] # 下面这条 Xpath 需按实际 DOM 调整先用浏览器确认结构 for node in tree.xpath(//div[contains(class,news-item)]): title node.xpath(.//a/text()) link node.xpath(.//a/href) if title and link: items.append({ title: title[0].strip(), url: link[0] if link[0].startswith(http) else https://www.guancha.cn link[0], }) return items if __name__ __main__: html fetch(URL) for it in parse_home(html)[:5]: print(it)逻辑说明fetch负责发请求apparent_encoding让 Requests 根据内容猜编码中文站不加这行经常出现乱码raise_for_status让 4xx/5xx 直接抛异常避免拿到错误页还继续解析。parse_home里etree.HTML把字符串转成可 Xpath 的树contains(class,...)比class...更稳因为 class 里常带多个值。参数说明timeout10是必须的不设超时遇到慢响应会一直挂住HEADERS里的 User-Agent 是基础伪装不带它部分站点直接返回 403。Xpath 里的.//表示当前节点下任意层级在循环里必须用.开头否则会从整个文档根节点重新找这是新手最常见的定位错误。2.3 翻页抓更多新闻URL 规律与循环控制首页只有精选真正量大的是更多新闻列表页。这类页面通常有两种分页形式URL 带页码参数如?page2或路径式如/news/list_2.html。先手动翻两三页对比 URL 差异找出规律再写循环。import time BASE https://www.guancha.cn/news/list_{}.html def crawl_pages(start1, end5): all_news [] for page in range(start, end 1): url BASE.format(page) try: html fetch(url) except requests.RequestException as e: print(fpage {page} failed: {e}) continue items parse_home(html) print(fpage {page} got {len(items)} items) all_news.extend(items) time.sleep(1.5) # 控制频率别把人家站点打崩 return all_news逻辑说明把页码拼进 URL 模板循环抓取单页失败用continue跳过而不是中断整个任务这是长任务的基本容错。time.sleep(1.5)是礼貌爬取也是降低被封概率最有效的手段之一。参数说明start/end控制抓取范围调试时先设end2验证解析对不对再放大。sleep时间别低于 1 秒新闻站对高频请求敏感热词里那个429 too many requests和exceeded retry limit基本都是请求太密触发的限流后面避坑章会细讲。3. 存储与数据建模新闻表怎么设计、去重怎么做3.1 表结构一张 news 表够不够练手项目用 SQLite 最省事零配置、单文件、Flask 直接能读。字段设计上标题、链接、来源、发布时间、分类这几列是刚需。链接建议加唯一索引用来做去重——新闻站列表页经常重复推荐同一条不去重数据库很快就脏了。import sqlite3 def init_db(pathnews.db): conn sqlite3.connect(path) conn.execute( CREATE TABLE IF NOT EXISTS news ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, url TEXT UNIQUE NOT NULL, category TEXT, publish_time TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ) conn.commit() return conn def save_news(conn, items): sql INSERT OR IGNORE INTO news (title, url, category, publish_time) VALUES (?, ?, ?, ?) rows [(i[title], i[url], i.get(category), i.get(publish_time)) for i in items] conn.executemany(sql, rows) conn.commit()逻辑说明url TEXT UNIQUE是去重的核心配合INSERT OR IGNORE重复链接会被静默跳过不用先查再插省一次查询。executemany批量插入比循环单条快得多几千条数据差距明显。参数说明category和publish_time允许为空因为列表页不一定每条都带分类和时间硬要求非空会导致大量数据插不进去。created_at用数据库默认值记录入库时间方便后续排查哪批数据是什么时候抓的。3.2 分类字段从哪来Xpath 的 text 函数与属性提取分类信息通常藏在列表项的标签文字或链接路径里。Xpath 取文本用text()取属性用href、title。如果分类在链接里如/tech/xxx可以用字符串切分提取。def parse_with_category(html): tree etree.HTML(html) result [] for node in tree.xpath(//div[contains(class,news-item)]): title node.xpath(.//a/text()) link node.xpath(.//a/href) cat node.xpath(.//span[contains(class,tag)]/text()) if not (title and link): continue url link[0] if link[0].startswith(http) else https://www.guancha.cn link[0] # 分类兜底从 URL 路径里猜 category cat[0].strip() if cat else url.split(/)[3] if len(url.split(/)) 3 else unknown result.append({title: title[0].strip(), url: url, category: category}) return result逻辑说明text()返回的是列表取[0]才是字符串这是 Xpath 新手最容易忘的点。分类做了两级兜底优先取标签文字取不到就从 URL 路径切保证字段尽量不空。参数说明url.split(/)[3]依赖 URL 结构不同站点层级不同用之前先打印几条 URL 确认下标。contains(class,tag)里的tag要换成实际类名别照抄。4. Flask 接口与 Echarts 对接把数据喂给前端图表4.1 三个接口列表、分类统计、时间分布Flask 在这里的角色是数据中转前端 Echarts 通过 fetch 拿 JSON 再渲染。接口不用多三个够用新闻列表、按分类聚合的数量、按日期聚合的数量。分类聚合直接喂饼图日期聚合喂折线或柱状图。from flask import Flask, jsonify, render_template import sqlite3 app Flask(__name__) DB news.db def query(sql, args()): conn sqlite3.connect(DB) conn.row_factory sqlite3.Row rows conn.execute(sql, args).fetchall() conn.close() return [dict(r) for r in rows] app.route(/) def index(): return render_template(index.html) app.route(/api/news) def api_news(): return jsonify(query(SELECT title, url, category FROM news ORDER BY id DESC LIMIT 50)) app.route(/api/category) def api_category(): return jsonify(query( SELECT category AS name, COUNT(*) AS value FROM news GROUP BY category ))逻辑说明row_factory sqlite3.Row让查询结果能按列名取值转 dict 后jsonify直接输出标准 JSON。分类统计用GROUP BY在数据库层算好前端拿到就是 Echarts 饼图要的{name, value}格式省得在 JS 里再聚合。参数说明LIMIT 50防止一次返回太多拖慢页面需要全量再调大。ORDER BY id DESC保证最新在前。生产环境记得给category加索引数据量大时GROUP BY会慢。4.2 Echarts 饼图与柱状图从接口到渲染前端页面用 Echarts 官方 CDN 引入饼图展示分类占比柱状图展示各分类数量对比。下面是最小渲染逻辑// 分类饼图 fetch(/api/category) .then(res res.json()) .then(data { const chart echarts.init(document.getElementById(pie)); chart.setOption({ tooltip: { trigger: item }, series: [{ type: pie, radius: 60%, data: data, emphasis: { itemStyle: { shadowBlur: 10 } } }] }); });逻辑说明fetch拿到后端 JSON 后直接塞进series.data因为后端已经按{name, value}组织好了。radius: 60%控制饼图大小想做成环形图就写[40%, 60%]。参数说明tooltip.trigger: item让鼠标悬停显示单项数值。如果饼图中间要放文字热词里提到的echarts pie 中间的字用title组件定位到中心或加graphic文本别硬塞进 series。5. 避坑与排查爬虫和可视化最容易翻车的五个点5.1 现象请求返回 429 或 exceeded retry limit原因请求频率过高被站点限流。热词里429 too many requests和exceeded retry limit, last status: 429说的就是这件事通常是循环里没加延时或并发开太猛。解决单线程加time.sleep(1~3)把抓取范围分批跑遇到 429 不要立刻重试退避等待如等 30 秒再试必要时换 IP 或降低频率。别用多线程硬刚新闻站封起来很快。5.2 现象Xpath 在浏览器能定位在代码里返回空列表原因浏览器执行了 JSDOM 是渲染后的Requests 拿到的是原始 HTML结构不一样。或者 Xpath 用了绝对路径页面稍有变动就失效。解决先print(resp.text)看真实源码用相对路径加contains(class,...)定位别用/html/body/div[3]/...这种绝对路径。定位不准时把 Xpath 单独拿出来在 lxml 里测。5.3 现象中文标题乱码原因Requests 默认按响应头猜编码部分站点响应头不准导致resp.text乱码。解决显式设resp.encoding resp.apparent_encoding或直接resp.content.decode(utf-8)。存库前确认一遍乱码数据入库后很难救。5.4 现象数据库里同一条新闻出现多次原因列表页重复推荐或翻页时页码重叠没做去重。解决url字段加 UNIQUE 约束插入用INSERT OR IGNORE。如果已经脏了用DELETE FROM news WHERE id NOT IN (SELECT MIN(id) FROM news GROUP BY url)清理。5.5 现象Echarts 图表空白控制台报数据格式错原因接口返回的不是数组或字段名不是name/valueEcharts 认不出来。解决浏览器 Network 面板看接口原始返回确认是[{name, value}]结构。后端jsonify前打印一次前端console.log(data)再确认一次两头对齐就不会空。6. 进阶技巧让这套爬虫可视化真正能长期跑把项目跑通只是第一步能不能长期用取决于两件事抓取是否稳定、数据是否可增量。我一般会给爬虫加一个上次抓到哪的记录用数据库里最大 id 或最新发布时间做水位线每次只抓新内容避免全量重爬。翻页循环里加一个连续三页没有新数据就停的判断能省掉大量无效请求。验证抓取质量有个笨办法但很管用随机抽 20 条入库数据手动点开链接核对标题和分类是否对得上。Xpath 写错时往往不会报错只会静默抓错字段靠肉眼抽查比任何断言都直接。分类统计接口跑出来后用SELECT category, COUNT(*) FROM news GROUP BY category ORDER BY COUNT(*) DESC对一遍看饼图数据和 SQL 结果是否一致不一致就是接口或前端聚合逻辑有问题。Echarts 这边饼图分类超过 8 个会挤成一团建议后端只返回 Top 8其余归为其他。柱状图如果分类名太长把xAxis.axisLabel.rotate设成 30 度或改用横向柱状图。这些细节不影响功能但决定图表能不能看。最后说个血泪经验别在爬虫脚本里直接写死域名和 Xpath抽成配置项。新闻站改版是常态改版后你只需要改配置不用翻遍代码。我吃过这个亏一个路径写死在五个文件里站点一改版改到怀疑人生。把请求头、URL 模板、Xpath 表达式集中到一个 config 里是这套方案能不能维护下去的分水岭。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

营销技能全景拆解:从用户洞察到数据闭环的实战方法论

营销技能全景拆解:从用户洞察到数据闭环的实战方法论

干了十几年市场,看过无数简历上写着"精通营销"的人,也带过不少真正靠营销技能撑起增长的项目。说实话,营销这个行当门槛很低,低到谁都能发两条内容、投一笔预算;但天花板又极高,高到大多数团队卡…

📅 2026/10/27 9:02:37
AI+生物学论文写作:科研表达能力的底层重建

AI+生物学论文写作:科研表达能力的底层重建

1. 这不是“AI改写”,而是科研写作的底层能力重建“谷粉学术课堂:AI生物学学术论文写作润色”——这个标题里藏着三个被严重低估的关键信号:“谷粉”不是泛指读者,而是特指在高校与科研院所一线挣扎的青年生物学者;“学…

📅 2026/10/27 9:02:37
C#与YOLO26结合实现高效工业视觉检测

C#与YOLO26结合实现高效工业视觉检测

## 1. 项目概述:当C#遇上YOLO26的化学反应去年在开发一个工业质检系统时,我遇到了传统算法在复杂场景下的准确率瓶颈。经过多轮技术选型,最终决定采用YOLO26C#的技术路线,实测在.NET 8环境下将检测速度提升到传统方法的17倍。这个…

📅 2026/10/27 9:02:37
MORE NEWS

更多资讯

📰

基于Springboot敬老院管理系统【附源码+文档】

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

📰

基于Springboot高校创业竞赛路演系统【附源码+文档】

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

📰

基于Springboot高校服装租赁平台【附源码+文档】

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

📰

基于Springboot高校学位资格审核系统【附源码+文档】

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

📰

Sno Station Skills发布流水线揭秘:自测门禁、PUBLISHED.json与registry.yaml的质量保障原理

【免费下载链接】sno-station-skills Sno Station Skills: 31 tested skills that turn Claude Code and Codex into one working team. Cross-vendor peer review, automatic handoff when an agent hits its quota, a one-page morning brief, work orders with proof, and a…

📰

心愿清单的记忆应该可见、可撤回

心愿助手最吸引人的地方,是它好像记得“我想做什么”。最容易令人失望的地方,也在这里:页面刷新后内容消失,演示标签重新出现,却仍写着“长期记忆”。我会先把普通存储做诚实,再考虑模型怎样理解心愿。 记…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬