㊗️本期内容已收录至专栏《Python爬虫实战》,持续完善知识体系与项目实战,建议先订阅收藏,后续查阅更方便~
㊙️本期爬虫难度指数:⭐⭐⭐
🉐福利: 一次订阅后,专栏内的所有文章可永久免费看,持续更新中,保底1000+(篇)硬核实战内容。

全文目录:
-
-
- [🌟 开篇语](#🌟 开篇语)
- [1️⃣ 摘要(Abstract)](#1️⃣ 摘要(Abstract))
- [2️⃣ 背景与需求(Why)](#2️⃣ 背景与需求(Why))
- [3️⃣ 合规与注意事项(必写)](#3️⃣ 合规与注意事项(必写))
- [4️⃣ 技术选型与整体流程(What/How)](#4️⃣ 技术选型与整体流程(What/How))
- [5️⃣ 环境准备与L] ➔ [Save Index]](#5️⃣ 环境准备与L] ➔ [Save Index])
- [5️⃣ 环境准备与依赖安装(可复现)](#5️⃣ 环境准备与依赖安装(可复现))
- [6️⃣ 核心实现:URL 拼接与请求封装(Helper)](#6️⃣ 核心实现:URL 拼接与请求封装(Helper))
- [7️⃣ 核心实现:列表与详情页解析(The Spider)](#7️⃣ 核心实现:列表与详情页解析(The Spider))
- [8️⃣ 数据整合与导出(Controller)](#8️⃣ 数据整合与导出(Controller))
- [9️⃣ 运行方式与结果展示(必写)](#9️⃣ 运行方式与结果展示(必写))
- [🔟 常见问题与排错(Troubleshooting)](#🔟 常见问题与排错(Troubleshooting))
- [1️⃣1️⃣ 进阶优化(Optional)](#1️⃣1️⃣ 进阶优化(Optional))
- [1️⃣2️⃣ 总结与延伸阅读](#1️⃣2️⃣ 总结与延伸阅读)
- [🌟 文末](#🌟 文末)
-
- [✅ 专栏持续更新中|建议收藏 + 订阅](#✅ 专栏持续更新中|建议收藏 + 订阅)
- [✅ 互动征集](#✅ 互动征集)
- [✅ 免责声明](#✅ 免责声明)
-
🌟 开篇语
哈喽,各位小伙伴们你们好呀~我是【喵手】。
运营社区: C站 / 掘金 / 腾讯云 / 阿里云 / 华为云 / 51CTO
欢迎大家常来逛逛,一起学习,一起进步~🌟
我长期专注 Python 爬虫工程化实战 ,主理专栏 《Python爬虫实战》:从采集策略 到反爬对抗 ,从数据清洗 到分布式调度 ,持续输出可复用的方法论与可落地案例。内容主打一个"能跑、能用、能扩展 ",让数据价值真正做到------抓得到、洗得净、用得上。
📌 专栏食用指南(建议收藏)
- ✅ 入门基础:环境搭建 / 请求与解析 / 数据落库
- ✅ 进阶提升:登录鉴权 / 动态渲染 / 反爬对抗
- ✅ 工程实战:异步并发 / 分布式调度 / 监控与容错
- ✅ 项目落地:数据治理 / 可视化分析 / 场景化应用
📣 专栏推广时间 :如果你想系统学爬虫,而不是碎片化东拼西凑,欢迎订阅专栏👉《Python爬虫实战》👈,一次订阅后,专栏内的所有文章可永久免费阅读,持续更新中。
💕订阅后更新会优先推送,按目录学习更高效💯~
1️⃣ 摘要(Abstract)
项目名称 :GovStats_Indexer ------ 区域性统计数据附件链接自动化清洗与索引系统
一句话说明 :
本项目针对典型的政府统计局"数据发布/统计公报"栏目(通常为静态多页列表),利用 Python 自动化遍历分页,深度解析列表页或详情页,提取指标名称、发布时间及核心附件下载链接,最终生成一份可供检索的资源索引 CSV。
读完能获得什么?
- URL 拼接艺术 :彻底搞懂相对路径(
./report.doc)与绝对路径的转换逻辑。 - 详情页穿透:应对"列表页只有标题,下载链接在详情页正文里"的经典场景。
- SSL 证书绕过 :解决政府老旧网站常见的
SSLError问题。
2️⃣ 背景与需求(Why)
为什么要爬这个?
- 宏观研究:你需要收集某省过去 10 年的《国民经济和社会发展统计公报》来做 GDP 趋势分析。
- 招投标/市调:寻找某市发布的"第七次人口普查"详细数据表(通常是 Excel 附件)。
- 痛点 :手动一页页翻、一个个点开下载太慢了,而且不知道哪个文件坏了。我们需要先拿到链接清单。
目标站点与字段:
-
目标:某市统计局 -> "数据发布" 或 "统计年鉴" 栏目。
-
字段清单:
title: 文件/指标标题(如:2023年1-9月主要经济指标)publish_date: 发布时间(YYYY-MM-DD)file_url: 核心资产,文件的直接下载地址file_type: 文件扩展名(xls/pdf/doc)source_page: 来源页面(方便回溯)
3️⃣ 合规与注意事项(必写)
政府网站比较特殊,请务必保持敬畏之心:
- SSL 证书报错 :很多地方统计局网站的 HTTPS 证书可能过期了或者签发机构不被信任。在代码中我们需要显式地关闭验证(
verify=False),但这不代表网站不安全,只是配置老旧。 - 低频采集 :这些服务器通常没有 CDN 加速,配置也就是几核 CPU。一定要慢! 建议每次请求间隔 2-3 秒。如果你把统计局网站搞崩了,后果很严重。
- 只读不写:只抓取公开数据,不要尝试扫描后台目录或进行 SQL 注入测试。
4️⃣ 技术选型与整体流程(What/How)
技术栈:
requests:关闭 SSL 验证的神器。BeautifulSoup:处理不规范 HTML(比如标签没闭合)比 XPath 容错率更高。urllib.parse.urljoin:本篇的 MVP 。用于解决../../images/data.xls这种相对路径的拼接问题。
整体流程图:
**Pagination Loop** \] ➔ \[**Get List HTML** \] ➔ \[**Extract Item Link** \] ➔ \[**Deep Dive (Request Detail Page)** \] ➔ \[**Find Attachment ``** \] ➔ \[**Construct Absolute URL** \] ➔ \[**Save Index**
5️⃣ 环境准备与L] ➔ [Save Index]
5️⃣ 环境准备与依赖安装(可复现)
项目结构:
text
GovStats/
├── output/
│ └── stats_index.csv # 结果索引
├── spider.py # 爬虫逻辑
└── requirements.txt
依赖安装:
bash
pip install requests beautifulsoup4 pandas loguru
6️⃣ 核心实现:URL 拼接与请求封装(Helper)
政府网站最让人头疼的就是链接拼接 。
比如当前页面是 http://xx.gov.cn/data/list.html,源码里的链接是 ./2023/report.xls。
如果你直接拼,很容易拼错。必须用 urljoin。
python
import requests
from urllib.parse import urljoin
from loguru import logger
import urllib3
# 禁用安全请求警告(针对 verify=False)
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
class GovFetcher:
def __init__(self):
self.headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36"
}
def fetch(self, url):
"""
封装的请求方法,自动处理 SSL 错误和编码
"""
try:
# verify=False 是关键,绕过过期证书
resp = requests.get(url, headers=self.headers, verify=False, timeout=15)
# 自动检测编码(解决 GBK/UTF-8 混用问题)
# 如果自动检测不准,可以尝试 resp.encoding = 'gbk' 或 'utf-8'
resp.encoding = resp.apparent_encoding
return resp.text, resp.url
except Exception as e:
logger.error(f"Failed to fetch {url}: {e}")
return None, None
def make_absolute_url(self, base_url, relative_url):
"""
将相对路径转为绝对路径
"""
if not relative_url: return ""
return urljoin(base_url, relative_url)
7️⃣ 核心实现:列表与详情页解析(The Spider)
这里我们假设一种最复杂的场景:
- **列表*只展示标题。
- 点击标题进入详情页。
- **详情页正文底部有一个"附件下载:xxx.xls"。
python
from bs4 import BeautifulSoup
import re
import time
import random
class StatsSpider:
def __init__(self, base_list_url):
self.fetcher = GovFetcher()
self.base_list_url = base_list_url # e.g., "http://tjj.city.gov.cn/col/col123/index.html"
def parse_list_page(self, html, current_url):
"""解析列表页,提取详情页链接"""
soup = BeautifulSoup(html, "html.parser")
items = []
# 寻找列表项,这里需要根据实际网站结构调整选择器
# 假设结构是 <ul classs="news_list"><li><a href="...">Title</a><span>Date</span></li></ul>
# 这是一个非常通用的政府网站构
li_tags = soup.select("ul li")
for li in li_tags:
a_tag = li.find("a")
date_span = li.find("span")
if a_tag:
title = a_tag.get_text(strip=True)
relative_link = a_tag.get('href')
# 拼接详情页 URL
detail_url = self.fetcher.make_absolute_url(current_url, relative_link)
# 提取日期
pub_date = date_span.get_text(strip=True) if date_span else "Unknown"
items.append({
"title": title,
"date": pub_date,
"detail_url": detail_url
})
return items
def parse_detail_page_for_attachments(self, html, detail_url):
"""
深入详情页,寻找 .xls, .pdf, .doc, .zip 等附件链接
"""
soup = BeautifulSoup(html, "html.parser")
attachments = []
# 查找所有 a 标签
all_links = soup.find_all("a")
for link in all_links:
href = link.get('href')
text = link.get_text(strip=True)
if not href: continue
# 判断是否是文件链接(通过后缀名)
# 这是一个简单的正则,匹配常见文档格式
if re.search(r'\.(xls|xlsx|doc|docx|pdf|zip|rar)$', href, re.IGNORECASE):
abs_url = self.fetcher.make_absolute_url(detail_url, href)
file_type = abs_url.split('.')[-1]
attachments.append({
"file_name": text or "Download", # 有时候 a 标签里没字
"file_url": abs_url,
"file_type": file_type
})
return attachments
8️⃣ 数据整合与导出(Controller)
将列表页抓取和详情页挖掘结合起来。
python
import pandas as pd
class StatsController:
def __init__(self):
self.spider = StatsSpider("http://example.gov.cn/data/list.html")
self.results = []
def run(self, start_page=1, end_page=3):
# 假设分页规律是 list_1.html, list_2.html... 或者 ?page=1
# 这里演示 ?page=X 的情况
for page in range(start_page, end_page + 1):
logger.info(f"📄 Processing List Page {page}...")
# 构造分页 URL
if page == 1:
target_url = self.spider.base_list_url
else:
# 这是一个伪代码逻辑,具体看网站
target_url = f"{self.spider.base_list_url}?page={page}"
html, real_url = self.spider.fetcher.fetch(target_url)
if not html: continue
# 1. 拿到列表项
items = self.spider.parse_list_page(html, real_url)
for item in items:
logger.info(f" 🔍 Checking detail: {item['title'][:20]}...")
# 2. 进入详情页
detail_html, _ = self.spider.fetcher.fetch(item['detail_url'])
if not detail_html: continue
# 3. 提取附件
attachments = self.spider.parse_detail_page_for_attachments(detail_html, item['detail_url'])
if attachments:
for att in attachments:
# 只有找到了附件,才记录下来
self.results.append({
"indicator_name": item['title'],
"publish_date": item['date'],
"attachment_name": att['file_name'],
"download_link": att['file_url'],
"file_type": att['file_type'],
"source_url": item['detail_url']
})
logger.success(f" 📎 Found File: {att['file_name']} ({att['file_type']})")
else:
logger.debug(" ❌ No attachment found.")
# 详情页之间休眠,不要太快
time.sleep(random.uniform(0.5, 1.5))
# 列表页翻页休眠
time.sleep(2)
def save(self):
df = pd.DataFrame(self.results)
df.to_csv("output/stats_index.csv", index=False, encoding='utf-8-sig')
logger.success(f"🎉 Job Done! Saved {len(df)} file records.")
9️⃣ 运行方式与结果展示(必写)
运行入口:
python
if __name__ == "__main__":
bot = StatsController()
bot.run(start_page=1, end_page=2) # 先试跑 2 页
bot.save()
生成的 CSV 结果:
| indicator_name | publish_date | attachment_name | download_link | file_type | source_url |
|---|---|---|---|---|---|
| 2023年某市统计公报 | 2023-03-15 | 公报全文.pdf | http://.../202303/P023.pdf | http://.../art/123.html | |
| 7月工业经济运行情况 | 2023-08-20 | 附表1:主要工业产品产量.xls | http://.../data/tab1.xls | xls | http://.../art/456.html |
| 7月工业经济运行情况 | 2023-08-20 | 附表2:分行业增加值.xls | http://.../data/tab2.xls | xls | http://.../art/456.html |
注意:你会发现同一篇详情页可能对应多行数据(因为包含多个附件),这正是我们想要的"原子化"索引。
🔟 常见问题与排错(Troubleshooting)
-
**链接提取出来是 `javascript:openFile(123):
- 现象:很多老政府网站用 JS 脚本来触发下载,href 并不是真实链接。
- 对策 :这是最麻烦的情况。你需要分析那个
openFile 函数(通常在页面的
1️⃣1️⃣ 进阶优化(Optional)
-
文件下载器(Downloader):
- 虽然我们只做索引,但可以写一个辅助脚本:读取生成的 CSV,过滤出
file_type= 'xls'的行,然后批量下载。 df[df['file_type'].isin'xls', 'xlsx'])].apply(download_func, axis=1)
- 虽然我们只做索引,但可以写一个辅助脚本:读取生成的 CSV,过滤出
-
**增量监控:
- 记录上一次爬取的最新
publish_date。下次运行时,一旦遇到日期早于记录日期的,直接break退出循环。
- 记录上一次爬取的最新
-
PDF 文本解析:
- 如果附件是 PDF,可以使用
pdfplumber库,尝试把 PDF 里的表格硬抽取出来转为 Excel。这是一个巨大的坑,但也是巨大的机会。
- 如果附件是 PDF,可以使用
1️⃣2️⃣ 总结与延伸阅读
复盘总结:
我们完成了一个政务数据挖掘机。
- 我们克服了老旧网站的 SSL 和 编码问题。
- 我们实现了跨页面的数据关联(列表页的时间 + 详情页的附件)。
- 我们构建了一个高价值的数据索引。
延伸阅读:
- Wget :如果你只是想简单粗暴地把整个网站所有 PDF 扒下来,Linux 的
wget -r -A.pdf http://target.com可能比 Python 更快。 - Gerapy / Scrapy:如果你要同时爬取全国 300 个地级市的统计局,请务必使用 Scrapy + Redis 分布式架构。
🌟 文末
好啦~以上就是本期的全部内容啦!如果你在实践过程中遇到任何疑问,欢迎在评论区留言交流,我看到都会尽量回复~咱们下期见!
小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦~
三连就是对我写作道路上最好的鼓励与支持! ❤️🔥
✅ 专栏持续更新中|建议收藏 + 订阅
墙裂推荐订阅专栏 👉 《Python爬虫实战》,本专栏秉承着以"入门 → 进阶 → 工程化 → 项目落地"的路线持续更新,争取让每一期内容都做到:
✅ 讲得清楚(原理)|✅ 跑得起来(代码)|✅ 用得上(场景)|✅ 扛得住(工程化)
📣 想系统提升的小伙伴 :强烈建议先订阅专栏 《Python爬虫实战》,再按目录大纲顺序学习,效率十倍上升~

✅ 互动征集
想让我把【某站点/某反爬/某验证码/某分布式方案】等写成某期实战?
评论区留言告诉我你的需求,我会优先安排实现(更新)哒~
⭐️ 若喜欢我,就请关注我叭~(更新不迷路)
⭐️ 若对你有用,就请点赞支持一下叭~(给我一点点动力)
⭐️ 若有疑问,就请评论留言告诉我叭~(我会补坑 & 更新迭代)
✅ 免责声明
本文爬虫思路、相关技术和代码仅用于学习参考,对阅读本文后的进行爬虫行为的用户本作者不承担任何法律责任。
使用或者参考本项目即表示您已阅读并同意以下条款:
- 合法使用: 不得将本项目用于任何违法、违规或侵犯他人权益的行为,包括但不限于网络攻击、诈骗、绕过身份验证、未经授权的数据抓取等。
- 风险自负: 任何因使用本项目而产生的法律责任、技术风险或经济损失,由使用者自行承担,项目作者不承担任何形式的责任。
- 禁止滥用: 不得将本项目用于违法牟利、黑产活动或其他不当商业用途。
- 使用或者参考本项目即视为同意上述条款,即 "谁使用,谁负责" 。如不同意,请立即停止使用并删除本项目。!!!
