Python爬虫实战:GovDataMiner —— 开放数据门户数据集元数据采集器(附 CSV 导出)!

㊙️本期内容已收录至专栏《Python爬虫实战》,持续完善知识体系与项目实战,建议先订阅收藏,后续查阅更方便~持续更新中!

㊗️爬虫难度指数:⭐

🚫声明:本数据&代码仅供学习交流,严禁用于商业用途、倒卖数据或违反目标站点的服务条款等,一切后果皆由使用者本人承担。公开榜单数据一般允许访问,但请务必遵守"君子协议",技术无罪,责任在人。

全文目录:

      • [🌟 开篇语](#🌟 开篇语)
      • [1️⃣ 标题 && 摘要(Abstract)](#1️⃣ 标题 && 摘要(Abstract))
      • [2️⃣ 背景与需求(Why)](#2️⃣ 背景与需求(Why))
      • [3️⃣ 合规与注意事项(必写)](#3️⃣ 合规与注意事项(必写))
      • [4️⃣ 技术选型与整体流程(What/How)](#4️⃣ 技术选型与整体流程(What/How))
      • [5️⃣ 环境准备与依赖安装(可复现)](#5️⃣ 环境准备与依赖安装(可复现))
      • [6️⃣ 核心实现:请求层(Fetcher)](#6️⃣ 核心实现:请求层(Fetcher))
      • [7️⃣ 核心实现:解析层(Parser)](#7️⃣ 核心实现:解析层(Parser))
      • [8️⃣ 数据存储与导出(Storage)](#8️⃣ 数据存储与导出(Storage))
      • [9️⃣ 运行方式与结果展示(必写)](#9️⃣ 运行方式与结果展示(必写))
      • [🔟 常见问题与排错(老司机的血泪史)](#🔟 常见问题与排错(老司机的血泪史))
      • [1️⃣1️⃣ 进阶优化(可选但加分)](#1️⃣1️⃣ 进阶优化(可选但加分))
      • [1️⃣2️⃣ 总结与延伸阅读](#1️⃣2️⃣ 总结与延伸阅读)
      • [🌟 文末](#🌟 文末)
        • [📌 专栏持续更新中|建议收藏 + 订阅](#📌 专栏持续更新中|建议收藏 + 订阅)
        • [✅ 互动征集](#✅ 互动征集)

🌟 开篇语

哈喽,各位小伙伴们你们好呀~我是【喵手】。

运营社区: C站 / 掘金 / 腾讯云 / 阿里云 / 华为云 / 51CTO

欢迎大家常来逛逛,一起学习,一起进步~🌟

我长期专注 Python 爬虫工程化实战 ,主理专栏 👉 《Python爬虫实战》:从采集策略反爬对抗 ,从数据清洗分布式调度 ,持续输出可复用的方法论与可落地案例。内容主打一个"能跑、能用、能扩展 ",让数据价值真正做到------抓得到、洗得净、用得上

📌 专栏食用指南(建议收藏)

  • ✅ 入门基础:环境搭建 / 请求与解析 / 数据落库
  • ✅ 进阶提升:登录鉴权 / 动态渲染 / 反爬对抗
  • ✅ 工程实战:异步并发 / 分布式调度 / 监控与容错
  • ✅ 项目落地:数据治理 / 可视化分析 / 场景化应用

📣 专栏推广时间 :如果你想系统学爬虫,而不是碎片化东拼西凑,欢迎订阅/关注专栏《Python爬虫实战》

订阅后更新会优先推送,按目录学习更高效~

1️⃣ 标题 && 摘要(Abstract)

摘要 :本文将跳过传统的 HTML 解析(XPath/CSS),带你解锁更高效的 API 抓包策略 。我们将利用浏览器开发者工具(F12)定位目标站点的隐蔽 API,使用 requests 模拟数据请求,直接获取干净的 JSON 格式数据。我们将抓取数据集的 名称所属主题 以及 最后更新时间,并自动生成一份 Excel 报表。

读完能获得什么?

  1. ⚡️ 学会使用浏览器"网络(Network)"面板抓包,找到隐藏的数据接口。
  2. 🔓 掌握处理 application/json 类型 POST 请求的技巧。
  3. 📊 获得一套通用的zf/企业数据门户采集脚本模板。

2️⃣ 背景与需求(Why)

为什么要爬?

zf开放数据(Open Government Data)涵盖了交通、气象、经济、民生等高价值领域。对于数据分析师来说,我们需要一个"目录"来监控哪些数据更新了,或者通过聚合不同城市的"主题分布",来分析数字化建设的侧重点。

目标站点 :以某典型"市级开放数据平台"为例(通用架构)。
目标数据

字段名 字段说明 目标价值
dataset_name 数据集名称 核心标识,如"全市停车场信息"
subject 所属主题 分类标签,如"交通出行"、"城市建设"
update_time 更新时间 判断数据鲜活度,过滤僵尸数据

3️⃣ 合规与注意事项(必写)

⚠️ 高压线预警

爬取zf网站,合规性是第一生命线。

  1. Robots.txt:务必检查。如果明确禁止爬取 API 路径,请立刻停止。
  2. 频率控制(QPS) :zf服务器通常还需要服务公众查询,带宽有限。绝对禁止多线程高并发!我们将严格限制在单线程,且每次请求间隔 2 秒以上。
  3. 敏感信息:虽然是"开放数据",但我们只采集"元数据(目录信息)",不涉及任何个人隐私或未公开的涉密数据。
  4. 态度:我们是数据的"利用者"而非"破坏者"。如果在 Header 里留下了你的联系方式,会显得更专业且友善。

4️⃣ 技术选型与整体流程(What/How)

技术选型:Requests (JSON模式) + Pandas

  • 为什么不用 BeautifulSoup? 因为现代数据门户大多是动态渲染(Vue/React/Angular)。你右键"查看源代码"可能只能看到一个空的 <div id="app"></div>。pp">`。数据都在 JSON 接口里!

    • 流程图

5️⃣ 环境准备与依赖安装(可复现)

Python 版本:3.8+

依赖安装

bash 复制代码
pip install requests pandas openpyxl
  • requests: 处理 HTTP 交互。
  • pandas + openpyl: 处理数据表格,支持导出为 Excel (.xlsx),比 CSV 对中文支持更友好。

项目结构

text 复制代码
gov_data_miner/
├── output/            # 结果保存
├── spider.py          # 核心代码
└── config.py          # 存放 URL 和 Headers 配置

6️⃣ 核心实现:请求层(Fetcher)

这一步是成败关键。我们需要模拟浏览器的 **POST 请求。

🔍 侦探时间(How to find the API):

  1. 打开目标数据门户的"数据集列表"页。
  2. F12 打开开发者工具,切到 Network (网络) -> Fetch/XHR
  3. 点击网页上的"下一页"或刷新。
  4. 观察列表里出现的请求,通常叫 list, search, `query 之类的。
  5. 点击该请求,查看 Payload (载荷) 和 Preview (响应预览)。
python 复制代码
# spider.py
import requests
import time
import random
import json

class DataPortalFetcher:
    def __init__(self):
        # 伪装成浏览器,Headers 越全越好
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
            'Content-Type': 'application/json;charset=UTF-8',
            'Referer': 'https://data.example-city.gov.cn/data/', # 必填!很多接口校验 Referer
            'Origin': 'https://data.example-city.gov.cn'
        }
        # 假设我们抓到了这个 API 地址 (示例地址)
        self.api_url = "https://data.example-city.gov.cn/api/v1/dataset/list"

    def fetch_page(self, page_num, page_size=10):
        """抓取单页数据"""
        # 根据 F12 抓包看到的 Payload 结构来构造
        payload = {
            "page": page_num,
            "size": page_size,
            "order": "updateTime", # 按更新时间排序
            "keywords": ""         # 搜索关键词为空,即全量
        }
        
        try:
            # ✅ 随机休眠,模拟真人阅读,防封核心!
            time.sleep(random.uniform(1.5, 3.5))
            
            # 注意:大多数现代 API 需要用 json=payload,而不是 data=payload
            response = requests.post(self.api_url, headers=self.headers, json=payload, timeout=10)
            
            if response.status_code == 200:
                return response.json() # 直接转为字典
            else:
                print(f"❌ 状态码异常: {response.status_code}")
                return None
        except Exception as e:
            print(f"❌ 请求报错: {e}")
            return None

7️⃣ 核心实现:解析层(Parser)

既然拿到的是 JSON,那就不用正则也不用 XPath 了,直接按 字典键值对(Key-Value) 取值,爽滑无比。

python 复制代码
class DataParser:
    def parse_json(self, json_data):
        """解析 JSON 响应,提取核心字段"""
        cleaned_list = []
        
        # 1. 防御性编程:检查 JSON 结构是否符合预期
        # 假设结构是: {'code': 200, 'data': {'list': [...], 'total': 100}}
        if not json_data or json_data.get('code') != 200:
            print("⚠️ 接口返回 Code 非 200 或数据为空")
            return []

        raw_list = json_data.get('data', {}).get('list', [])
        
        for item in raw_list:
            # 2. 字段提取与容错
            # 不同的网站字段名不同,需要根据 Preview 里的内容调整
            dataset_name = item.get('name') or item.get('title', '未知名称')
            
            # 主题有时候是个对象 {'id':1, 'name':'交通'},有时候直接是字符串
            subject = item.get('subjectName') or item.get('topic', '未分类')
            
            # 时间戳处理:有些返回 1678888888 (毫秒),有些返回 "2023-01-01"
            update_time = item.get('updateTime') or item.get('gmtModified')
            
            cleaned_list.append({
                "名称": dataset_name,
                "主题": subject,
                "更新时间": update_time,
                "来源API": "example-city-api"
            })
            
        return cleaned_list

8️⃣ 数据存储与导出(Storage)

这次我们用 Excel 格式(.xlsx),因为它对业务人员查看更友好,且不用担心 CSV 在 Excel 打开时的乱码问题。

python 复制代码
import pandas as pd
import os
from datetime import datetime

class ExcelStorage:
    def save(self, data_list, filename="dataset_report.xlsx"):
        if not data_list:
            print("⚠️ 没有数据需要保存")
            return

        df = pd.DataFrame(data_list)
        
        # 简单清洗:转换一下时间格式(如果拿到的是毫秒时间戳)
        # df['更新时间'] = pd.to_datetime(df['更新时间'], unit='ms') 
        
        output_dir = "output"
        os.makedirs(output_dir, exist_ok=True)
        file_path = os.path.join(output_dir, filename)
        
        # 导出 Excel
        df.to_excel(file_path, index=False)
        print(f"✅ 报表已生成: {file_path} (共 {len(df)} 条)")

9️⃣ 运行方式与结果展示(必写)

python 复制代码
# main.py (入口)
# 假设我们要抓前 3 页
def main():
    print("🚀 开放数据门户爬虫启动...")
    
    fetcher = DataPortalFetcher()
    parser = DataParser()
    storage = ExcelStorage()
    
    all_data = []
    
    # 循环抓取前 3 页
    for page in range(1, 4):
        print(f"📡 正在抓取第 {page} 页...")
        json_data = fetcher.fetch_page(page)
        
        if json_data:
            batch_data = parser.parse_json(json_data)
            print(f"   ↳ 本页提取到 {len(batch_data)} 条数据")
            all_data.extend(batch_data)
        else:
            print("   ↳ 本页抓取失败,停止后续任务。")
            break
            
    # 统一保存
    print(f"💾 正在保存全量数据 ({len(all_data)} 条)...")
    storage.save(all_data)
    print("🎉 任务完成!")

if __name__ == "__main__":
    main()

示例运行结果

text 复制代码
🚀 开放数据门户爬虫启动...
📡 正在抓取第 1 页...
   ↳ 本页提取到 10 条数据
📡 正在抓取第 2 页...
   ↳ 本页提取到 10 条数据
📡 正在抓取第 3 页...
   ↳ 本页提取到 10 条数据
💾 正在保存全量数据 (30 条)...
✅ 报表已生成: output/dataset_report.xlsx (共 30 条)
🎉 任务完成!

Image of Excel spreadsheet showing columns: Name, Subject, Update Time

🔟 常见问题与排错(老司机的血泪史)

  1. JSON 解析报错 (JSONDecodeError)

    • 现象requests.exceptionsJSONDecodeError
    • 原因:你以为返回的是 JSON,其实服务器报错返回了 HTML(比如 500 错误页面),或者触发了 WAF 防火墙验证页。
    • * :先打印 response.text 看看内容到底是啥,不要盲目 .json()。。
  2. 缺少字段

    • 现象:有的行有"主题",有的行是 `None。
    • 原因:zf数据录入质量参差不齐。
    • 解法 :使用 .get('key', '默认值') 是必须的!千万别用 data'key'] 直接取,会报错崩掉整个程序。
  3. 时间戳坑

      • 现象1672531200000 这种数字。
    • 解法 :这是毫秒级 Unix 时间戳。用 datetime.fromtimestamp(ts/1000) 转成人话。
  4. Header 里的加密参数

    • 现象 :请求头里有个 X-Sign: a8f9c2...,没有这个参数请求就 403。
    • 解法 :这就触及到 JS 逆向 的深水区了。如果遇到这种情况,新手建议换个没加密的门户练手,或者改用 Selenium/Playwright 这种"所见即所得"的工具来绕过签名生成。

1️⃣1️⃣ 进阶优化(可选但加分)

  • 全量翻页自动化 :目前的 demo 只写了前 3 页。进阶版应该先读取第一页返回的 total (总数) 字段,计算出总页数 total_pages = total // page_size + 1,然后用 for 循环跑完所有页。
  • 增量爬取 :记录上一次爬取到的最新 update_time。下次运行通过 API 的排序功能,只抓取时间晚于该节点的数据,实现"每日更新"。
  • 数据清洗管道 :把"主题"字段里的乱七八糟的分隔符(如 交通; 出行交通_出行)统一清洗为标准列表。

1️⃣2️⃣ 总结与延伸阅读

复盘

这篇教程带你走出了"HTML 解析"的舒适区,迈向了更底层的 API 交互。对于开放数据平台这类现代化网站,这才是最正统、最高效的打法。你不仅学会了爬虫,还顺便复习了 HTTP 协议和 JSON 结构,一举两得!

下一步

    • 可视化:拿到数据后,试试用 ECharts 或 Tableau 做一个"各领域数据开放热度图"。
  • 深挖 :既然拿到了列表,能不能根据列表里的 dataset_id,再发起请求去下载具体的 CSV/XLS 文件?(提示:是个下载链接的 URL 拼接问题)。

感觉如何?这种"直接调接口"的感觉是不是比用正则去匹配 HTML 代码爽多了?

如果你想针对具体的某个城市(比如北京或上海)进行实战调整,或者对如何查看 Network 面板有疑问,告诉我,我来给你做 1 对 1 辅导!

🌟 文末

好啦~以上就是本期 《Python爬虫实战》的全部内容啦!如果你在实践过程中遇到任何疑问,欢迎在评论区留言交流,我看到都会尽量回复~咱们下期见!

小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦~
三连就是对我写作道路上最好的鼓励与支持! ❤️🔥

📌 专栏持续更新中|建议收藏 + 订阅

专栏 👉 《Python爬虫实战》,我会按照"入门 → 进阶 → 工程化 → 项目落地"的路线持续更新,争取让每一篇都做到:

✅ 讲得清楚(原理)|✅ 跑得起来(代码)|✅ 用得上(场景)|✅ 扛得住(工程化)

📣 想系统提升的小伙伴:强烈建议先订阅专栏,再按目录顺序学习,效率会高很多~

✅ 互动征集

想让我把【某站点/某反爬/某验证码/某分布式方案】写成专栏实战?

评论区留言告诉我你的需求,我会优先安排更新 ✅


⭐️ 若喜欢我,就请关注我叭~(更新不迷路)

⭐️ 若对你有用,就请点赞支持一下叭~(给我一点点动力)

⭐️ 若有疑问,就请评论留言告诉我叭~(我会补坑 & 更新迭代)


免责声明:本文仅用于学习与技术研究,请在合法合规、遵守站点规则与 Robots 协议的前提下使用相关技术。严禁将技术用于任何非法用途或侵害他人权益的行为。

相关推荐
历程里程碑2 小时前
滑动窗口---- 无重复字符的最长子串
java·数据结构·c++·python·算法·leetcode·django
人工智能AI技术3 小时前
【Agent从入门到实践】43 接口封装:将Agent封装为API服务,供其他系统调用
人工智能·python
Darkershadow3 小时前
蓝牙学习之Time Set
python·学习·蓝牙·ble·mesh
m0_736919104 小时前
超越Python:下一步该学什么编程语言?
jvm·数据库·python
学习中的DGR4 小时前
[极客大挑战 2019]Http 1 新手解题过程
网络·python·网络协议·安全·http
布茹 ei ai4 小时前
Python屏幕监视器 - 自动检测屏幕变化并点击
开发语言·python
天天睡大觉5 小时前
Python学习12
网络·python·学习
程序员杰哥5 小时前
性能测试详解
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·性能测试
人工智能AI技术6 小时前
【Agent从入门到实践】42实战:用Docker打包Agent,实现一键部署
人工智能·python