[特殊字符]️ 实战爬虫:Python 抓取【采购公告】接口数据(含踩坑解析)

本文通过一次完整实战,演示如何使用 Python + requests 抓取
采购网公告列表数据 ,并重点分析 JSON 结构解析中常见的 TypeError 问题。


一、需求背景

在日常学习爬虫时,我们经常会遇到这种场景:

  • 页面是 列表页
  • 数据不是写在 HTML 中
  • 而是通过 XHR 接口动态加载

例如湖南省政府采购网的公告列表页:

复制代码
http://www.ccgp-hunan.gov.cn/page/notice/more.jsp?noticeTypeID=prcmNotices

👉 页面滚动 / 翻页时,实际请求的是一个接口。


二、定位真实接口(关键一步)

1️⃣ 打开开发者工具

  • F12 → Network
  • 选择 Fetch / XHR
  • 翻页或刷新页面

你会发现不断请求:

复制代码
getNoticeList4Web.do

2️⃣ 接口核心信息

请求地址

复制代码
http://www.ccgp-hunan.gov.cn/mvc/getNoticeList4Web.do

请求方式

复制代码
POST

Content-Type

复制代码
application/x-www-form-urlencoded

3️⃣ 请求参数分析(表单数据)

从 Network → Payload 可以看到完整参数:

text 复制代码
nType: prcmNotices
pType:
prcmPrjName:
prcmItemCode:
prcmOrgName:
startDate: 2025-01-01
endDate: 2025-12-28
prcmPlanNo:
page: 1
pageSize: 18

⚠️ 重点

这是 form 表单请求,不是 JSON!


三、返回数据结构解析(重点)

接口返回的是一个 JSON,大致结构如下:

json 复制代码
{
  "total": 4750,
  "hzMap": null,
  "rows": [
    {
      "NOTICE_TITLE": "湖南省XX项目公开招标公告",
      "NEWWORK_DATE": "2025-12-25",
      "PRCM_MODE_NAME": "公开招标"
    },
    ...
  ]
}

📌 重点字段说明:

字段 含义
total 总数据量
rows 当前页的数据列表
NOTICE_TITLE 公告标题
NEWWORK_DATE 发布时间

四、原始代码 & 问题分析

你一开始的核心代码是:

python 复制代码
for k, v in res_data.items():
    for y in v:
        print(y)

❌ 报错原因

接口返回的数据中:

python 复制代码
res_data["total"] = 4750

于是代码等价于:

python 复制代码
for y in 4750:
    ...

直接触发:

text 复制代码
TypeError: 'int' object is not iterable

👉 int 不能被 for 遍历


五、正确的解析思路(核心)

我们真正需要的只有:

python 复制代码
res_data["rows"]

因为它才是 列表(list)


六、完整可运行示例代码(推荐版本)

python 复制代码
import requests

url = "http://www.ccgp-hunan.gov.cn/mvc/getNoticeList4Web.do"

page = 1

while True:
    para = {
        "nType": "prcmNotices",
        "pType": "",
        "prcmPrjName": "",
        "prcmItemCode": "",
        "prcmOrgName": "",
        "startDate": "2025-01-01",
        "endDate": "2025-12-28",
        "prcmPlanNo": "",
        "page": page,
        "pageSize": 18
    }

    response = requests.post(url, data=para)
    res_data = response.json()

    rows = res_data.get("rows")

    # 如果没有数据,说明翻页结束
    if not rows:
        print("数据抓取完成")
        break

    print(f"===== 第 {page} 页 =====")

    for item in rows:
        title = item.get("NOTICE_TITLE")
        date = item.get("NEWWORK_DATE")
        mode = item.get("PRCM_MODE_NAME")

        print(date, mode, title)

    page += 1

七、代码关键点解析

1️⃣ 为什么用 data=para

因为接口是:

复制代码
Content-Type: application/x-www-form-urlencoded

如果写成:

python 复制代码
json=para

❌ 会直接拿不到数据。


2️⃣ 为什么只遍历 rows

python 复制代码
rows = res_data.get("rows")
  • rows 是 list
  • 每一项是 dict
  • 正好对应公告数据

3️⃣ 为什么要判断 rows 是否为空?

python 复制代码
if not rows:
    break

这是 分页爬虫的标准写法,用于自动停止。


八、常见坑位总结(必看)

❌ 坑 1:对 int / None 直接 for

python 复制代码
for y in res_data["total"]:

✅ 正确:

先判断类型 or 直接定位 list 字段


❌ 坑 2:把 form 请求当 JSON 请求

python 复制代码
requests.post(url, json=para)  # ❌

✅ 正确:

python 复制代码
requests.post(url, data=para)

❌ 坑 3:无脑遍历 res_data.items()

接口返回结构一定要 先 print 看清楚


九、可以继续扩展的方向

  • 保存为 CSV / Excel
  • 写入 MySQL / SQLite
  • 增加异常重试
  • 多进程抓取
  • 解析公告详情页

十、总结

爬虫的本质不是写代码

而是 看接口 + 读数据结构 + 精准解析

这次案例里最核心的一点就是:

不要假设接口返回的数据结构,一定要先看清楚 JSON


相关推荐
wuhen_n6 分钟前
JavaScript内置数据结构
开发语言·前端·javascript·数据结构
AIFarmer6 分钟前
在EV3上运行Python语言——无线编程
python·ev3
不会代码的小测试8 分钟前
UI自动化-POM封装
开发语言·python·selenium·自动化
2401_8414956411 分钟前
【LeetCode刷题】二叉树的层序遍历
数据结构·python·算法·leetcode·二叉树··队列
roman_日积跬步-终至千里15 分钟前
【Java并发】Java 线程池实战:警惕使用CompletableFuture.supplyAsync
java·开发语言·网络
lsx20240620 分钟前
C++ 基本的输入输出
开发语言
ZH154558913121 分钟前
Flutter for OpenHarmony Python学习助手实战:GUI桌面应用开发的实现
python·学习·flutter
B站计算机毕业设计超人26 分钟前
计算机毕业设计Hadoop+Spark+Hive招聘推荐系统 招聘大数据分析 大数据毕业设计(源码+文档+PPT+ 讲解)
大数据·hive·hadoop·python·spark·毕业设计·课程设计
B站计算机毕业设计超人27 分钟前
计算机毕业设计hadoop+spark+hive交通拥堵预测 交通流量预测 智慧城市交通大数据 交通客流量分析(源码+LW文档+PPT+讲解视频)
大数据·hive·hadoop·python·spark·毕业设计·课程设计
CodeSheep程序羊32 分钟前
拼多多春节加班工资曝光,没几个敢给这个数的。
java·c语言·开发语言·c++·python·程序人生·职场和发展