项目七 综合实训:校园新闻动态采集与分析系统

把前六个项目串成一条完整链路:采集 → 解析 → 清洗 → 统计 → 输出,并达到验收标准。

项目第14-16周requests + BS4 + pandas6 段代码

本项目学习目标

  • 能独立完成“取数 → 清洗 → 分析 → 出结果”全流程
  • 代码按模块拆分、可重复运行
  • 能解释每一步在做什么、为什么这么做
  • 能按验收标准自检并交付

本项目任务导览

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 第三方库并验证版本。

PYTHON
# 打开终端,安装所需库
pip install requests beautifulsoup4 lxml pandas

# 验证安装
python -c "import requests, bs4, lxml, pandas; print('环境就绪')"

Step 2:采集——获取页面内容

目标:模拟"Requests 采集"环节,得到新闻列表页的 HTML 文本。

PYTHON
# 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 中批量提取"标题、时间、来源",整理为字典列表。

PYTHON
# 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,去重、填充、类型转换,再按"来源"分组统计。

PYTHON
# 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

把四个步骤整合为一个程序:采集 → 解析 → 清洗 → 统计 → 输出,课堂可直接运行。

PYTHON
# 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(...)收尾:保存清洗表和统计报表两个文件。
PYTHON
# 运行输出示例
采集到新闻: 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.csv
06

验收标准

✅ 完成清单(教师可据此评分)

验收项标准涉及项目
环境搭建能正确安装并导入 requests / lxml / pandas项目一
数据采集能获取页面 HTML,正确处理编码与请求头项目二
数据解析能提取标题、链接、时间、来源四类字段,无漏取项目三
数据存储能生成 news_clean.csv,字段完整、编码正确项目四
数据预处理能完成去重、填充、类型转换,脏数据被正确处理项目六
统计输出能按来源分组统计并输出排序报表项目六
代码规范函数拆分清晰、有注释、可读性好全部
07

拓展任务(进阶选做)