项目七 综合实训:校园新闻动态采集与分析系统
把前六个项目串成一条完整链路:采集 → 解析 → 清洗 → 统计 → 输出,并达到验收标准。
本项目学习目标
- 能独立完成“取数 → 清洗 → 分析 → 出结果”全流程
- 代码按模块拆分、可重复运行
- 能解释每一步在做什么、为什么这么做
- 能按验收标准自检并交付
本项目任务导览
01
综合实训:校园新闻动态采集与分析系统
完成六个项目学习后的综合任务。以"校园新闻"为例,把 采集 → 解析 → 预处理 → 统计 串成一条完整的数据流水线,独立完成一个小型实例项目。
- 一、项目背景与目标
- 二、技术架构
- 三、实训步骤(Step 1-4)
- 四、完整代码 main.py
- 五、验收标准
- 六、拓展任务
02
项目背景与目标
六个项目分别讲授了采集、解析、框架、日志与预处理,本实训把它们组合成一个真实可运行的小系统。
📌 项目任务
开发一个"校园新闻动态采集与分析系统":从新闻列表页抓取数据 → 解析出标题、时间、来源 → 存入表格 → 清洗预处理 → 分组统计输出结果。系统面向老师或宣传人员,用于快速汇总各渠道新闻情况。
🎯 学习目标
- 能独立完成"页面分析 → 代码编写 → 运行调试 → 结果验证"的完整流程;
- 能根据实际页面灵活选择采集工具与解析方式;
- 能对采集数据进行清洗、去重、类型转换与统计;
- 理解真实项目中各环节如何衔接、数据如何流转。
03
技术架构
本实训是一条"轻量级"数据流水线,把课程核心知识点按真实顺序串起来。
知识点映射表
| 实训环节 | 对应课程项目 | 用到的主要知识点 |
|---|---|---|
| 页面请求 | 项目二:动态网页数据采集 | Requests 的 get()、请求头、异常处理 |
| 数据解析 | 项目三:动态网页数据解析 | XPath 路径定位、text()/@href 提取 |
| 数据组织 | 项目一 / 项目四 | 数据格式认识、Item/字段思想、CSV 存储 |
| 数据预处理 | 项目六:动态网页数据预处理 | Pandas 读取、缺失值、去重、类型转换 |
| 统计输出 | 项目六 | groupby 分组聚合、排序、格式化输出 |
| 扩展方向 | 项目四 / 五 / 六 | Scrapy 框架、Flume+Kafka 日志、ELK 可视化 |
04
实训步骤
按 Step 1 → 4 逐步实现,每步都有独立可运行的小代码,最后整合为完整项目。
Step 1:环境准备
安装 Python 第三方库并验证版本。
# 打开终端,安装所需库
pip install requests beautifulsoup4 lxml pandas
# 验证安装
python -c "import requests, bs4, lxml, pandas; print('环境就绪')"Step 2:采集——获取页面内容
目标:模拟"Requests 采集"环节,得到新闻列表页的 HTML 文本。
# step2_fetch.py
import requests
# 真实场景:请求新闻网站
url = "https://www.example.com/news/index.html"
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"}
resp = requests.get(url, headers=headers, timeout=10)
resp.encoding = "utf-8" # 指定编码,避免乱码
html = resp.text # 页面源码 → 下一步解析的输入
print("状态码:", resp.status_code, "页面长度:", len(html))| 代码 | 白话解释 |
|---|---|
| import requests | 导入网络请求库 requests(需提前 pip install)。 |
| headers = {"User-Agent": "Mozilla/5.0 ..."} | 构造请求头字典:声明"我是浏览器"。很多网站只欢迎浏览器访问。 |
| resp = requests.get(url, headers=headers, timeout=10) | 带请求头发起 GET 请求,10 秒超时限制,响应存进 resp。 |
| resp.encoding = "utf-8" | 手动指定解码编码。如果不设置,Requests 可能猜错编码导致中文乱码。 |
| html = resp.text | 把响应内容转为文本(网页源代码),html 就是下一步解析程序的"输入原料"。 |
Step 3:解析——提取新闻字段
目标:用 XPath 从 HTML 中批量提取"标题、时间、来源",整理为字典列表。
# step3_parse.py
from lxml import etree
# 课堂演示:内置一段新闻列表 HTML(模拟采集结果)
html = """
<div class="news-list">
<div class="item"><a href="/n/1.html">学校召开教学改革会议</a><span>2023-02-10</span><em>官方发布</em></div>
<div class="item"><a href="/n/2.html">春季运动会圆满落幕</a><span>2023-02-11</span><em>校园新闻</em></div>
<div class="item"><a href="/n/3.html">新图书馆正式开放</a><span>2023-02-12</span><em>校园新闻</em></div>
<div class="item"><a href="/n/4.html">人工智能专业获批</a><span>2023-02-13</span><em>官方发布</em></div>
<div class="item"><a href="">标题缺失的新闻</a><span>2023-02-14</span><em>行业资讯</em></div>
</div>
"""
root = etree.HTML(html)
items = []
for item in root.xpath('//div[@class="item"]'):
title = item.xpath('a/text()')[0] if item.xpath('a/text()') else ""
link = item.xpath('a/@href')[0] if item.xpath('a/@href') else ""
time = item.xpath('span/text()')[0] if item.xpath('span/text()') else ""
src = item.xpath('em/text()')[0] if item.xpath('em/text()') else ""
items.append({"标题": title, "链接": link, "发布时间": time, "来源": src})
print("解析出新闻", len(items), "条")
for it in items:
print(it)| 代码 | 白话解释 |
|---|---|
| from lxml import etree | 导入 lxml 的 etree 模块,它能把 HTML 字符串变成可以"按路径查找"的节点树。 |
| root = etree.HTML(html) | 把 Step 2 得到的页面源码 html 解析成树,root 是树根。html 里所有标签变成树上的节点。 |
| root.xpath('//div[@class="item"]') | 找出所有 class 为 item 的 div 节点(每条新闻一个),返回节点列表。XPath 路径读法:// 任意位置,div 标签名,[...] 条件筛选。 |
| if item.xpath('a/text()') else "" | 取该 div 里 a 标签的文字(标题)。如果取不到(空列表),用空字符串兜底,避免程序报错——这就是"脏数据防护"。 |
| item.xpath('span/text()') / item.xpath('em/text()') | 分别取 span(发布时间)和 em(来源)的文字。注意 XPath 是从当前 item 节点继续向下找的。 |
| items.append({...}) | 把这条新闻的四个字段打包成字典,追加到 items 列表。最终 items 就是"半结构化数据"列表。 |
Step 4:预处理与统计——Pandas 清洗 + 分组
目标:把解析结果转成 DataFrame,去重、填充、类型转换,再按"来源"分组统计。
# step4_pandas.py
import pandas as pd
# 上一步解析得到的 items 列表 → DataFrame
df = pd.DataFrame(items)
print("原始数据:", df.shape)
# 1. 去重(按标题)
df = df.drop_duplicates(subset=["标题"])
# 2. 填充:标题为空补"未知",链接为空补"无"
df["标题"] = df["标题"].fillna("未知").replace("", "未知")
df["链接"] = df["链接"].replace("", "无")
# 3. 类型转换:发布时间转成日期类型
df["发布时间"] = pd.to_datetime(df["发布时间"])
print("清洗后数据:\n", df)
# 4. 按"来源"分组统计
result = df.groupby("来源").agg(
新闻条数=("标题", "count"),
最早时间=("发布时间", "min"),
最晚时间=("发布时间", "max")
).reset_index().sort_values("新闻条数", ascending=False)
print("\n按来源统计:\n", result)
# 5. 保存最终报表
result.to_csv("news_report.csv", index=False, encoding="utf-8-sig")
df.to_csv("news_clean.csv", index=False, encoding="utf-8-sig")
print("\n已保存 news_clean.csv 与 news_report.csv")| 代码 | 白话解释 |
|---|---|
| df = pd.DataFrame(items) | 把 Step 3 的字典列表 items 变成 DataFrame 表格:每个字典一行、键当列名。Python 数据结构 → 表格,只此一行。 |
| df = df.drop_duplicates(subset=["标题"]) | 按"标题"列去重,防止同一条新闻被重复采集。结果要写回 df 才生效。 |
| df["标题"] = df["标题"].replace("", "未知") | replace 把空字符串 "" 替换成"未知"。注意:空字符串不是 NaN,fillna 填不掉,必须用 replace。 |
| df["发布时间"] = pd.to_datetime(df["发布时间"]) | 把发布时间列转成"日期时间"类型。转完后才能比较大小、求最早/最晚,这是统计的前提。 |
| df.groupby("来源").agg(新闻条数=("标题","count"), 最早时间=("发布时间","min"), ...) | 按来源分组:统计每组新闻条数、最早与最晚发布时间。agg 里"新列名 = (原列, 函数)"。 |
| result.reset_index().sort_values("新闻条数", ascending=False) | 把来源从索引变回普通列,再按新闻条数从多到少排序。 |
| result.to_csv("news_report.csv", index=False, encoding="utf-8-sig") | 保存统计报表;utf-8-sig 让 Excel 打开不乱码。 |
05
完整代码 main.py
把四个步骤整合为一个程序:采集 → 解析 → 清洗 → 统计 → 输出,课堂可直接运行。
# main.py —— 校园新闻动态采集与分析系统(综合实训)
# 运行:python main.py
import pandas as pd
from lxml import etree
import requests
# ① 采集:真实场景请求页面(此处保留接口,课堂用内置示例)
def fetch_page(url=None):
if url is None:
# 课堂演示:内置新闻列表 HTML
return """
<div class="news-list">
<div class="item"><a href="/n/1.html">学校召开教学改革会议</a><span>2023-02-10</span><em>官方发布</em></div>
<div class="item"><a href="/n/2.html">春季运动会圆满落幕</a><span>2023-02-11</span><em>校园新闻</em></div>
<div class="item"><a href="/n/3.html">新图书馆正式开放</a><span>2023-02-12</span><em>校园新闻</em></div>
<div class="item"><a href="/n/4.html">人工智能专业获批</a><span>2023-02-13</span><em>官方发布</em></div>
<div class="item"><a href="">标题缺失的新闻</a><span>2023-02-14</span><em>行业资讯</em></div>
</div>
"""
resp = requests.get(url, headers={"User-Agent": "Mozilla/5.0"}, timeout=10)
resp.encoding = "utf-8"
return resp.text
# ② 解析:XPath 提取标题 / 链接 / 时间 / 来源
def parse_news(html):
root = etree.HTML(html)
items = []
for node in root.xpath('//div[@class="item"]'):
title = node.xpath('a/text()')
link = node.xpath('a/@href')
time = node.xpath('span/text()')
src = node.xpath('em/text()')
items.append({
"标题": title[0] if title else "",
"链接": link[0] if link else "",
"发布时间": time[0] if time else "",
"来源": src[0] if src else "",
})
return items
# ③ 预处理:去重、填充、类型转换
def clean(df):
df = df.drop_duplicates(subset=["标题"])
df["标题"] = df["标题"].replace("", "未知")
df["链接"] = df["链接"].replace("", "无")
df["发布时间"] = pd.to_datetime(df["发布时间"])
return df
# ④ 统计:按来源分组
def report(df):
return df.groupby("来源").agg(
新闻条数=("标题", "count"),
最早时间=("发布时间", "min"),
最晚时间=("发布时间", "max")
).reset_index().sort_values("新闻条数", ascending=False)
# 主流程
if __name__ == "__main__":
html = fetch_page() # ① 采集
items = parse_news(html) # ② 解析
print("采集到新闻:", len(items), "条")
df = pd.DataFrame(items) # ③ 预处理
df = clean(df)
print("清洗后:", len(df), "条")
result = report(df) # ④ 统计
print("\n📊 按来源统计结果:")
print(result.to_string(index=False))
df.to_csv("news_clean.csv", index=False, encoding="utf-8-sig")
result.to_csv("news_report.csv", index=False, encoding="utf-8-sig")
print("\n✅ 已保存 news_clean.csv 与 news_report.csv")| 代码/函数 | 白话解释 |
|---|---|
| def fetch_page(url=None): | 定义"采集"函数。默认用内置示例 HTML;传入真实网址时用 requests 请求并返回网页源码。一个函数把"演示/真实"两种模式都装下。 |
| def parse_news(html): | 定义"解析"函数:输入页面源码 html,用 XPath 提取标题/链接/时间/来源,返回字典列表。函数只做一件事,方便单独测试。 |
| def clean(df): | 定义"清洗"函数:去重 → 填充 → 时间类型转换,输入 DataFrame、输出干净的 DataFrame。 |
| def report(df): | 定义"统计"函数:按来源分组聚合(条数、最早/最晚时间),排序后返回结果表。 |
| if __name__ == "__main__": | 主入口:只有直接运行本文件(python main.py)时才执行下面代码;被别的文件 import 时不会执行——Python 的标准写法。 |
| html = fetch_page() | 主流程第 1 步:调用采集函数,拿到页面源码。 |
| items = parse_news(html) | 第 2 步:调用解析函数,拿到字典列表。 |
| df = pd.DataFrame(items) | 第 3 步:列表转表格。 |
| df = clean(df) / result = report(df) | 第 4、5 步:清洗表格、统计结果。每个步骤一行调用,逻辑一目了然。 |
| df.to_csv(...) / result.to_csv(...) | 收尾:保存清洗表和统计报表两个文件。 |
# 运行输出示例
采集到新闻: 5 条
清洗后: 5 条
📊 按来源统计结果:
来源 新闻条数 最早时间 最晚时间
官方发布 2 2023-02-10 2023-02-13
校园新闻 2 2023-02-11 2023-02-12
行业资讯 1 2023-02-14 2023-02-14
✅ 已保存 news_clean.csv 与 news_report.csv06
验收标准
✅ 完成清单(教师可据此评分)
| 验收项 | 标准 | 涉及项目 |
|---|---|---|
| 环境搭建 | 能正确安装并导入 requests / lxml / pandas | 项目一 |
| 数据采集 | 能获取页面 HTML,正确处理编码与请求头 | 项目二 |
| 数据解析 | 能提取标题、链接、时间、来源四类字段,无漏取 | 项目三 |
| 数据存储 | 能生成 news_clean.csv,字段完整、编码正确 | 项目四 |
| 数据预处理 | 能完成去重、填充、类型转换,脏数据被正确处理 | 项目六 |
| 统计输出 | 能按来源分组统计并输出排序报表 | 项目六 |
| 代码规范 | 函数拆分清晰、有注释、可读性好 | 全部 |
07