㊗️本期内容已收录至专栏《Python爬虫实战》持续完善知识体系与项目实战建议先订阅收藏后续查阅更方便㊙️本期爬虫难度指数⭐⭐ (中级)福利一次订阅后专栏内的所有文章可永久免费看持续更新中保底1000(篇)硬核实战内容。全文目录 开篇语0️⃣ 前言Preface1️⃣ 摘要Abstract2️⃣ 背景与需求Why3️⃣ 合规与注意事项必写⚠️4️⃣ 技术选型与整体流程What/How5️⃣ 环境准备与依赖安装可复现6️⃣ 核心实现请求层Fetcher7️⃣ 核心实现解析层Parser8️⃣ 数据存储与导出Storage9️⃣ 运行方式与结果展示必写 常见问题与排错强烈建议写1️⃣1️⃣ 进阶优化可选但加分1️⃣2️⃣ 总结与延伸阅读 文末✅ 专栏持续更新中建议收藏 订阅✅ 互动征集✅ 免责声明 开篇语哈喽各位小伙伴们你们好呀我是【喵手】。运营社区 C站 / 掘金 / 腾讯云 / 阿里云 / 华为云 / 51CTO欢迎大家常来逛逛一起学习一起进步我长期专注Python 爬虫工程化实战主理专栏 《Python爬虫实战》从采集策略到反爬对抗从数据清洗到分布式调度持续输出可复用的方法论与可落地案例。内容主打一个“能跑、能用、能扩展”让数据价值真正做到——抓得到、洗得净、用得上。专栏食用指南建议收藏✅ 入门基础环境搭建 / 请求与解析 / 数据落库✅ 进阶提升登录鉴权 / 动态渲染 / 反爬对抗✅ 工程实战异步并发 / 分布式调度 / 监控与容错✅ 项目落地数据治理 / 可视化分析 / 场景化应用专栏推广时间如果你想系统学爬虫而不是碎片化东拼西凑欢迎订阅专栏《Python爬虫实战》一次订阅后专栏内的所有文章可永久免费阅读持续更新中。订阅后更新会优先推送按目录学习更高效0️⃣ 前言Preface项目核心本篇我们将使用 Python 的requests结合BeautifulSoup针对政务统计机构的公报发布栏目编写一个稳健的爬虫。程序将穿越不同的年份分页深入详情页提取公报的 PDF 附件链接并抓取正文首段作为摘要最终输出一份结构化的元数据清单statistical_communiques_archive.csv。读完你能获得什么学会应对政务老网站常见的SSL证书报错与极其缓慢的响应延迟。掌握在杂乱无章的 HTML 结构中利用正则Regex精准提取年份与日期的硬核技巧。收获一份具有极高学术/行研价值的宏观数据导航雏形。1️⃣ 摘要Abstract核心内容本项目针对国家或地方统计局的“统计公报”栏目设计。通过模拟浏览器请求遍历年份列表页获取公报标题与详情入口。随后进入详情页通过 DOM 节点扫描提取隐藏的 PDF 附件 URL并抽取文章第一段作为摘要说明实现多字段的整合与 CSV 落地。读完你能获得什么理解爬取政府公开数据时的防御性编程Defensive Programming思维。掌握使用 Python 灵活处理文件扩展名.pdf,.doc与相对路径拼接的能力。建立一套适用于长周期历史数据归档的爬虫架构。2️⃣ 背景与需求Why为什么要爬做宏观经济分析或者城市发展研究时经常需要对比某市过去 20 年的 GDP 和人口变化。然而政府网站的“历史归档”往往分页很深且不同年份的页面排版风格迥异。手动一页页去寻找并下载 PDF不仅耗时而且容易遗漏。用爬虫把所有公报的**元信息Meta-info**先抽取成一张数据表后续无论是检索还是批量下载附件都会变得犹如探囊取物目标字段清单精准制导Title公报标题如2023年XX市国民经济和社会发展统计公报Year所属年份从标题或发布逻辑中清洗出如2023Publish_Date发布日期如2024-03-15PDF_LinkPDF 原件下载链接若无则保留详情页链接Abstract摘要通常取正文第一段介绍性文字3️⃣ 合规与注意事项必写⚠️政务数据采集是一把双刃剑我们在获取公共知识的同时必须守住极客的底线极度温柔的并发绝对核心政务外网服务器不仅要展示文章还要承载大量的办事大厅业务。严禁使用高并发多线程冲击请求之间必须加入 3-5 秒的随机休眠。只采公开数据公报属于“主动公开的政务信息”完全合法合规。但切勿尝试扫描未公开的敏感目录。不代表官方立场我们抓取的只是元数据聚合使用这些数据时需注明来源于政府公开渠道并保持客观中立。4️⃣ 技术选型与整体流程What/How技术定调稳健优先为了应对可能存在的网络波动我们必须引入urllib3的重试机制。解析层面bs4处理这类以文本为主的政务网页游刃有余。整体流程图English visualization as requested5️⃣ 环境准备与依赖安装可复现来吧打开终端装好我们的“数字挖掘机”Python 版本推荐 3.8安装命令pipinstallrequests beautifulsoup4 pandas urllib3推荐项目结构gov_stats_archive/ ├── archive_spider.py # 核心爬虫脚本 └── datasets/ # 用于存放最终的归档文件6️⃣ 核心实现请求层Fetcher政府老网站有时会遇到“证书过期SSL Error”或者响应极慢的情况。我们需要一个带“厚重护甲”的请求器。importrequestsimporttimeimportrandomimportloggingimporturllib3fromrequests.adaptersimportHTTPAdapterfromurllib3.util.retryimportRetry# 忽略不安全的 SSL 证书警告针对老旧政务网站urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)logging.basicConfig(levellogging.INFO,format%(asctime)s - %(levelname)s - %(message)s)defget_robust_session():创建一个具备自动重试和超时防挂死的 Sessionsessionrequests.Session()# 设置 3 次重试遇到 500, 502, 503, 504 自动退避retryRetry(total3,backoff_factor1.5,status_forcelist[500,502,503,504])adapterHTTPAdapter(max_retriesretry)session.mount(http://,adapter)session.mount(https://,adapter)session.headers.update({User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36,Accept:text/html,application/xhtmlxml,})returnsessiondeffetch_html(session,url):try:# verifyFalse 是为了对付一些政务网站过期或自签名的 SSL 证书respsession.get(url,timeout15,verifyFalse)resp.raise_for_status()resp.encodingresp.apparent_encoding# 自动识别 GBK / UTF-8returnresp.textexceptExceptionase:logging.error(fFailed to fetch{url}-{e})returnNone7️⃣ 核心实现解析层Parser公报详情页的排版往往五花八门。有的把日期写在正文开头有的在标题下面。提取 PDF 链接也是个技术活。frombs4importBeautifulSoupfromurllib.parseimporturljoinimportredefparse_list_page(html,base_url):提取列表页的所有公报链接soupBeautifulSoup(html,html.parser)articles[]# 假设列表在一个 class 为 list-box 的 ul 下itemssoup.select(ul.list-box li a)foriteminitems:titleitem.get_text(stripTrue)linkitem.get(href)# 过滤掉非公报内容比如领导讲话if公报intitle:full_linkurljoin(base_url,link)articles.append({Title:title,Detail_URL:full_link})# 获取下一页逻辑 (省略具体 DOM依网站而定)next_tagsoup.find(a,textre.compile(r下一页|下页))next_urlurljoin(base_url,next_tag[href])ifnext_tagandnext_tag.has_attr(href)elseNonereturnarticles,next_urldefextract_detail_info(html,url):【硬核钻取】在详情页中提取年份、日期、摘要和 PDFifnothtml:returnNonesoupBeautifulSoup(html,html.parser)info{}# 1. 清洗年份利用正则从 HTML 标题或网页 title 提取title_textsoup.title.get_text()ifsoup.titleelseyear_matchre.search(r(19|20)\d{2},title_text)info[Year]year_match.group(0)ifyear_matchelseUnknown# 2. 提取发布日期通常在 classpub-time 或文本中包含 时间date_strUnknown Datedate_nodesoup.find(lambdatag:tag.namein[span,div]andre.search(r发布时间|日期|时间,tag.get_text()))ifdate_node:# 用正则提取 YYYY-MM-DD 或 YYYY年MM月DD日d_matchre.search(r\d{4}[-年/]\d{1,2}[-月/]\d{1,2},date_node.get_text())ifd_match:date_strd_match.group(0).replace(年,-).replace(月,-).replace(日,)info[Publish_Date]date_str# 3. 提取 PDF 链接找带有 .pdf 后缀的 a 标签pdf_linkurl# 默认降级为 HTML 原文链接fora_taginsoup.find_all(a,hrefTrue):if.pdfina_tag[href].lower():pdf_linkurljoin(url,a_tag[href])break# 找到第一个就收手info[PDF_Link]pdf_link# 4. 提取摘要取正文中第一个有实质内容的段落abstractNo abstract found.paragraphssoup.select(div.content-box p)# 假设正文在这个 div 里forpinparagraphs:textp.get_text(stripTrue)iflen(text)30and公报notintext:# 过滤掉只有标题的段落找真正的文字abstracttext[:150]...# 截取前 150 字breakinfo[Abstract]abstractreturninfo8️⃣ 数据存储与导出Storage政务数据非常严谨我们将其保存为CSV格式。并且为了防止中途断网导致数据丢失采用**追加写入Append Mode**的策略。importpandasaspdimportosdefsave_archive(data_list,filenamedatasets/statistical_communiques_archive.csv):ifnotdata_list:returnos.makedirs(os.path.dirname(filename),exist_okTrue)dfpd.DataFrame(data_list)# 按照公报标题去重防止重复采集df.drop_duplicates(subset[Title],inplaceTrue)file_existsos.path.isfile(filename)df.to_csv(filename,modea,headernotfile_exists,indexFalse,encodingutf-8-sig)logging.info(f✅ Archive updated!{len(data_list)}new records appended to{filename})9️⃣ 运行方式与结果展示必写主函数的骨架展现了一个优雅爬虫应有的节奏感。defmain():sessionget_robust_session()# 示例 URL请替换为你目标统计局的真实公报列表页base_urlhttp://tjj.example-gov.cn/tjgb/current_urlbase_url logging.info( Statistical Archive Spider Started...)whilecurrent_url:logging.info(f Scanning Pagination:{current_url})htmlfetch_html(session,current_url)ifnothtml:breakarticles,next_urlparse_list_page(html,base_url)logging.info(f - Found{len(articles)}communiqués. Drilling down...)page_results[]forarticleinarticles:# 敬畏之心请求之间随机延时保护政务服务器time.sleep(random.uniform(2.5,4.5))detail_htmlfetch_html(session,article[Detail_URL])meta_infoextract_detail_info(detail_html,article[Detail_URL])ifmeta_info:# 合并标题和新提取的字段article.update(meta_info)page_results.append(article)logging.info(f - Archived: [{meta_info[Year]}]{article[Title][:15]}...)# 每页抓完立即落盘save_archive(page_results)current_urlnext_urlifcurrent_url:time.sleep(5)# 翻页前深呼吸logging.info( Mission Accomplished! The historical archive is secured.)if__name____main__:main()示例输出结果 (statistical_communiques_archive.csv)TitleYearPublish_DatePDF_LinkAbstractDetail_URL2023年XX市国民经济和社会发展统计公报20232024-03-21http://…/attach/2023gb.pdf2023年面对复杂严峻的外部环境全市坚持稳中求进工作总基调经济运行回升向好…http://…/detail/12022年XX市国民经济和社会发展统计公报20222023-03-18http://…/detail/2 (降级)2022年全市统筹疫情防控和经济社会发展综合实力迈上新台阶全年实现地区生产总值…http://…/detail/2(可以看到2022年由于没有 PDF 附件我们的系统自动把 HTML 详情页链接作为备用保留了下来极其稳健) 常见问题与排错强烈建议写爬政务网站绝对是个体力活加智力活这里是防坑指南返回 403 Forbidden 或是出现带有“验证码”的页面诊断你可能触发了政务外网的WAF 防火墙如瑞数、加速乐。解法立即停止程序将time.sleep的时间调大到 10 秒以上或者尝试更换家用宽带的 IP。千万不要硬刚防火墙。年份提取出来乱七八糟诊断有的标题不规范例如叫《回顾这十年我市统计公报》。解法如果标题里没年份你可以在详情页中抓取“发布日期”然后用date_str[:4]将其前四位发布年份反向推导为所属年份通常是发布年份减一。乱码问题再次发作诊断老网站用了特殊的字体库或者中文字符集不支持。解法尝试强制转码response.content.decode(GB18030, errorsreplace)GB18030的字库比GBK更全能容错绝大多数生僻字。1️⃣1️⃣ 进阶优化可选但加分PDF 自动化下载器既然你已经拿到了PDF_Link你可以再写一个小脚本。读取这个 CSV针对所有后缀是.pdf的链接发起请求并按Year_Title.pdf的格式保存到本地磁盘。这就构成了一个完整的离线知识库NLP 数据提取高阶如果你不需要全本 PDF其实可以利用正则表达式直接在爬取Abstract的同时提取文本里的核心数据“实现地区生产总值(.*?)亿元”、“常住人口(.*?)万人”。直接把自然语言转化为结构化的经济数据库1️⃣2️⃣ 总结与延伸阅读 太牛了你今天完成的工作其价值不可估量。通过这套爬虫逻辑你跨越了时间和不规则网页的阻碍将原本散落在互联网各个角落的历史宏观数据聚合成了一张极其清晰的元数据图谱。这套从“列表页获取索引”到“详情页通过正则和 DOM 遍历抽取复杂字段PDF、年份”的架构正是所有高端数据分析师构建其专属 Database 的基石。下一步建议如果你对宏观经济感兴趣国家统计局官方还有一个“国家数据data.stats.gov.cn”平台。那个平台大量使用了动态接口API你可以去尝试用抓包的方式分析它的网络请求那将是一片更广阔的新天地 文末好啦以上就是本期的全部内容啦如果你在实践过程中遇到任何疑问欢迎在评论区留言交流我看到都会尽量回复咱们下期见小伙伴们在批阅的过程中如果觉得文章不错欢迎点赞、收藏、关注哦三连就是对我写作道路上最好的鼓励与支持❤️✅ 专栏持续更新中建议收藏 订阅墙裂推荐订阅专栏 《Python爬虫实战》本专栏秉承着以“入门 → 进阶 → 工程化 → 项目落地”的路线持续更新争取让每一期内容都做到✅ 讲得清楚原理✅ 跑得起来代码✅ 用得上场景✅ 扛得住工程化想系统提升的小伙伴强烈建议先订阅专栏 《Python爬虫实战》再按目录大纲顺序学习效率十倍上升✅ 互动征集想让我把【某站点/某反爬/某验证码/某分布式方案】等写成某期实战评论区留言告诉我你的需求我会优先安排实现(更新)哒~⭐️ 若喜欢我就请关注我叭更新不迷路⭐️ 若对你有用就请点赞支持一下叭给我一点点动力⭐️ 若有疑问就请评论留言告诉我叭我会补坑 更新迭代✅ 免责声明本文爬虫思路、相关技术和代码仅用于学习参考对阅读本文后的进行爬虫行为的用户本作者不承担任何法律责任。使用或者参考本项目即表示您已阅读并同意以下条款合法使用 不得将本项目用于任何违法、违规或侵犯他人权益的行为包括但不限于网络攻击、诈骗、绕过身份验证、未经授权的数据抓取等。风险自负 任何因使用本项目而产生的法律责任、技术风险或经济损失由使用者自行承担项目作者不承担任何形式的责任。禁止滥用 不得将本项目用于违法牟利、黑产活动或其他不当商业用途。使用或者参考本项目即视为同意上述条款,即 “谁使用谁负责” 。如不同意请立即停止使用并删除本项目。