Python爬虫实战:Requests+BeautifulSoup抓取政府招标信息并存储为CSV

📅 2026/8/5 15:30:45
Python爬虫实战:Requests+BeautifulSoup抓取政府招标信息并存储为CSV
1. 项目概述从数据金矿到结构化表格最近在帮一个做企业服务的朋友分析市场他提了个很实际的需求想系统地看看最近半年各个地方政府都发布了哪些招标采购信息特别是跟他业务相关的IT服务和设备采购。手动去一个个网站翻那效率太低了而且信息分散格式不一根本没法做分析。这个场景是不是很熟悉无论是做市场调研、竞品分析还是寻找商机政府公开的招标采购公告都是一个巨大的、高质量的数据金矿。但这些信息通常以网页形式发布直接阅读和汇总费时费力。这时候python爬虫的价值就凸显出来了。我们需要的就是一个能自动访问指定网站、精准抓取关键字段比如项目名称、采购单位、预算金额、公告时间、链接并把这些零散的信息规整地存入一个CSV表格的工具。CSV格式的好处是几乎所有的数据分析工具Excel、Python的pandas、数据库都能无缝对接后续做筛选、统计、可视化都非常方便。这个项目就是一个典型的“python爬虫实例”目标明确爬取政府招标采购信息并写入CSV。它不涉及复杂的模拟登录或高频请求核心在于对网页结构的解析和数据的规整清洗非常适合用来巩固爬虫基础并解决一个真实的商业信息收集问题。2. 核心思路与工具选型为什么是Requests BeautifulSoup面对一个爬虫项目第一步永远是分析目标和选择趁手的工具。我们的目标是政府招标网站这类网站通常有几个特点页面结构相对规范基于HTML模板、反爬措施可能包括简单的请求头校验但一般不会像大型电商平台那样部署复杂的验证码或动态加载。因此我们的技术栈可以以轻量、易用、高效为原则。2.1 网络请求库Requests 是唯一选择对于发送HTTP请求获取网页内容Requests库是Python社区毫无争议的标准。它语法简洁直观requests.get(url)一行代码就能搞定大部分事情。相比Python内置的urllibRequests的API设计更人性化自动处理连接池、会话、重定向等细节让开发者能更专注于业务逻辑。在这个项目里我们需要用它来模拟浏览器访问招标列表页和详情页。2.2 网页解析库BeautifulSoup 精准定位拿到网页HTML源码后我们需要从中“挖出”我们需要的数据。这就是解析库的工作。为什么选BeautifulSoup因为它对新手极其友好支持多种解析器我们常用lxml速度快查找元素的语法接近自然语言。通过find()、find_all()、select()等方法我们可以像使用CSS选择器一样轻松定位到包含“项目名称”、“采购单位”的HTML标签。虽然像PyQuery或lxml直接XPath也有其优势但BeautifulSoup在易读性和上手速度上更胜一筹适合这种结构清晰的静态页面抓取。2.3 数据存储CSV模块与Pandas的权衡最终数据需要写入CSV。这里有两个主流选择Python内置的csv模块和第三方库pandas。csv模块轻量无需额外安装对于简单的写入操作足够用。你可以精细控制写入的每一步。pandas库功能强大DataFrame数据结构处理表格数据得心应手一行df.to_csv(‘data.csv’, indexFalse, encoding‘utf-8-sig’)就能搞定并且能自动处理编码问题特别是解决Excel打开中文乱码的‘utf-8-sig’编码。对于本项目我推荐使用pandas。原因在于爬取过程中我们很可能需要对数据进行初步清洗或转换比如处理空值、格式化日期pandas在这些方面的能力远超内置模块为后续分析铺平道路。虽然多引入了一个依赖但带来的便利性是值得的。2.4 辅助工具伪装与等待请求头Headers这是绕过基础反爬的关键。我们需要在requests.get()中传入一个模拟真实浏览器的headers字典至少包含User-Agent。直接从浏览器开发者工具F12 - Network - 点击请求 - Headers里复制即可。时间等待time.sleep在循环抓取多个页面时务必在请求间插入随机延时如time.sleep(random.uniform(1, 3))这是网络爬虫的道德和法律底线既能避免对目标服务器造成压力也能显著降低IP被封锁的风险。注意在选取目标网站时务必首先仔细阅读网站的robots.txt文件通常在网站根目录如https://某网站.com/robots.txt和“免责声明”、“使用条款”。只爬取允许爬取的部分并严格控制访问频率。3. 实战拆解四步走搞定数据抓取与存储思路清晰了我们开始动手。整个过程可以分解为四个清晰的步骤环境搭建、页面分析、数据抓取、存储输出。3.1 第一步环境准备与依赖安装工欲善其事必先利其器。首先确保你的Python环境建议3.6以上已经就绪。然后通过pip安装必要的库。打开你的命令行终端或CMD执行以下命令pip install requests beautifulsoup4 pandas lxmllxml是一个高效的HTML/XML解析器作为BeautifulSoup的解析后端能大幅提升解析速度。安装完成后创建一个新的Python脚本文件比如gov_bid_crawler.py并在开头导入这些库import requests from bs4 import BeautifulSoup import pandas as pd import time import random import re3.2 第二步目标页面结构与URL规律分析这是爬虫成功与否最关键的一步决定了我们如何定位数据和翻页。我们以一个典型的省级政府采购网为例请注意以下为示例实际爬取请替换为具体、合规的目标网址。打开目标网站用浏览器打开招标公告列表页。分析列表页按下F12打开开发者工具使用“元素检查”Inspector功能。查看一条公告的HTML结构。通常公告会包裹在一个div或li标签里每个字段标题、链接、发布时间都有特定的CSS类名或标签层次。找到包含公告链接的a标签。定位选择器记下包裹单条公告的容器的CSS选择器路径如div.list-item以及内部各个字段的选择器如标题a.title发布时间span.date。分析翻页逻辑点击下一页观察浏览器地址栏URL的变化。常见模式有查询参数型?page2路径型/index_2.html需要从页面中提取“下一页”的链接。假设我们分析的网站列表页URL为https://example.gov.cn/cgxx/gggs/index.html翻页通过?page参数实现单条公告的标题和链接在div classnews-listlia href...这个结构里。3.3 第三步编写核心爬取函数我们将爬取过程封装成函数提高代码可读性和复用性。def fetch_page(url): 发送请求获取页面HTML headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 } try: response requests.get(url, headersheaders, timeout10) response.raise_for_status() # 检查请求是否成功 # 很多政府网站使用GBK编码这里需要根据实际情况调整 response.encoding response.apparent_encoding # 或直接指定 gbk / utf-8 return response.text except requests.RequestException as e: print(f请求失败: {url}, 错误: {e}) return None def parse_list_page(html): 解析列表页提取当前页所有公告的链接和基本信息 soup BeautifulSoup(html, lxml) bid_list [] # 根据实际分析的结构定位公告项 # 示例假设每个公告在一个 classlist-item 的 div 里 items soup.find_all(div, class_list-item) for item in items: # 提取标题和详情页链接 title_tag item.find(a, hrefTrue) if not title_tag: continue title title_tag.get_text(stripTrue) detail_url title_tag[href] # 处理相对链接 if detail_url.startswith(/): detail_url https://example.gov.cn detail_url # 提取发布时间 (假设在 span.date 里) date_tag item.find(span, class_date) publish_date date_tag.get_text(stripTrue) if date_tag else N/A # 可以先从列表页提取部分信息也可以只存链接稍后抓详情页 bid_list.append({ 标题: title, 链接: detail_url, 公告日期: publish_date, # 采购单位、预算金额等通常需要在详情页获取 }) return bid_list def parse_detail_page(html): 解析公告详情页提取更丰富的信息 soup BeautifulSoup(html, lxml) info_dict {} # 这里需要根据详情页的具体结构进行定位通常信息在固定的标签或表格里 # 示例1通过CSS选择器定位 # info_dict[采购单位] soup.select_one(td:contains(采购人) td).get_text(stripTrue) if soup.select_one(td:contains(采购人)) else N/A # 示例2更通用的方法遍历所有p或td标签通过文本关键词匹配 for elem in soup.find_all([p, td, div]): text elem.get_text(stripTrue) if 采购人 in text or 采购单位 in text: # 简单提取相邻单元格或下一个兄弟节点的内容实际需精细调整 info_dict[采购单位] elem.find_next_sibling().get_text(stripTrue) if elem.find_next_sibling() else text.split()[-1] if 预算金额 in text: info_dict[预算金额] elem.find_next_sibling().get_text(stripTrue) if elem.find_next_sibling() else text.split()[-1] if 项目概况 in text or 采购需求 in text: info_dict[项目概况] elem.find_next().get_text(stripTrue, separator ) if elem.find_next() else N/A # 如果页面结构规整可以直接用更精准的选择器 # 假设采购单位信息在一个id为‘purchaser’的span里 purchaser_tag soup.find(span, idpurchaser) if purchaser_tag: info_dict[采购单位] purchaser_tag.get_text(stripTrue) return info_dict3.4 第四步整合流程与数据存储现在我们把所有函数串联起来并加入翻页逻辑和CSV存储。def main(): base_url https://example.gov.cn/cgxx/gggs/index.html?page{} all_bids_data [] # 假设爬取前5页 for page_num in range(1, 6): print(f正在抓取第 {page_num} 页...) list_url base_url.format(page_num) html fetch_page(list_url) if not html: print(f第 {page_num} 页抓取失败跳过。) continue bids_on_page parse_list_page(html) for bid in bids_on_page: detail_html fetch_page(bid[链接]) if detail_html: detail_info parse_detail_page(detail_html) # 合并列表页和详情页信息 bid.update(detail_info) all_bids_data.append(bid) print(f已抓取: {bid[标题][:30]}...) # 礼貌访问随机延时 time.sleep(random.uniform(1.5, 3)) # 翻页延时 time.sleep(random.uniform(2, 4)) # 使用pandas保存到CSV if all_bids_data: df pd.DataFrame(all_bids_data) # 指定列顺序方便查看 columns_order [标题, 采购单位, 预算金额, 公告日期, 项目概况, 链接] # 只保留df中存在的列 existing_columns [col for col in columns_order if col in df.columns] df df[existing_columns] # 关键使用 utf-8-sig 编码避免Excel打开中文乱码 df.to_csv(government_bids.csv, indexFalse, encodingutf-8-sig) print(f数据抓取完成共 {len(all_bids_data)} 条记录已保存至 government_bids.csv) else: print(未抓取到任何数据。) if __name__ __main__: main()4. 关键细节、常见问题与避坑指南代码跑起来只是第一步在实际操作中你会遇到各种各样的问题。下面这些是我踩过坑后总结的经验。4.1 编码问题乱码的终结者中文网站编码五花八门GBK、GB2312、UTF-8都有。乱码是新手最常见的“拦路虎”。识别编码response.apparent_encoding是Requests库猜测的编码通常比较准。但最可靠的方法是查看网页HTML源码中meta charset...标签的内容。统一内部编码在代码内部统一使用UTF-8处理字符串。解决Excel乱码这是另一个经典问题。用pandas的to_csv()方法时务必指定encodingutf-8-sig。这个sigBOM头能让Excel正确识别UTF-8编码。如果用Python内置csv模块需要import codecs并用codecs.open(file.csv, w, utf-8-sig)来打开文件。4.2 反爬应对低调行事长久生存政府网站虽然反爬不一定极端但基本的礼貌和规避措施必须要有。User-Agent轮换准备一个列表存放几个主流浏览器的User-Agent字符串每次请求随机选取一个。代理IP池如果抓取量非常大或触发了IP限制需要考虑使用代理IP。但对于小规模、低频的抓取控制好请求间隔通常就够了。会话保持使用requests.Session()对象它可以自动处理cookies在某些需要维持登录状态的场景本项目通常不需要下很有用。错误重试网络请求可能失败实现一个简单的重试机制如最多重试3次能提高鲁棒性。4.3 数据清洗从原始文本到规整数据抓下来的文本往往包含多余的空格、换行符、不可见字符或者格式不统一。使用.get_text(stripTrue)BeautifulSoup的这个方法可以一键去除首尾空白。使用.get_text(separator )当标签内有多段文本被br隔开时这个参数可以用空格或其他字符连接它们避免文本挤在一起。正则表达式re库是利器比如从“预算金额1,234.56万元”中提取数字1234.56可以用re.search(r[\d,]\.?\d*, text)。处理缺失值不是每个公告都有“预算金额”字段。在保存到DataFrame前用bid.get(预算金额, N/A)来设置默认值避免后续处理出错。4.4 网页结构变动让爬虫更健壮网站改版是爬虫的“天敌”。如何让代码更不容易“死”避免过于脆弱的选择器尽量不要使用过长、依赖绝对位置如div[1]/div[2]/span[3]的XPath或CSS选择器。优先使用具有明确语义的class或id即使它们看起来有点长。多用find()和find_all()的文本匹配例如soup.find(td, textre.compile(采购人))这样即使外围的div结构变了只要td标签里的文字没变就能找到。添加异常捕获和日志在解析每个字段时用try...except包裹即使某个字段解析失败也不影响整条记录的保存同时将错误信息记录下来便于后续调整代码。5. 项目扩展与进阶思考完成基础爬取后这个项目还有很大的深化空间可以把它变成一个更强大的数据工具。5.1 定时任务与自动化市场信息瞬息万变每天手动运行脚本不现实。你可以使用操作系统的定时任务Linux的cron Windows的“任务计划程序”来每天定时执行你的Python脚本。将脚本部署到云服务器搭配crontab或Celery等任务队列实现完全自动化抓取。在脚本开头加入简单的逻辑比如只抓取今天或昨天发布的公告实现增量爬取避免重复数据。5.2 数据入库与分析CSV文件适合初步存储但数据量大时查询不便。存入数据库使用sqlite3Python内置或pymysql、psycopg2连接MySQL/PostgreSQL将数据存入关系型数据库。可以设计bids表包含标题、单位、金额、日期、链接等字段并建立索引方便按时间、地区、金额范围进行复杂查询。初步分析用pandas直接读取CSV或数据库你可以轻松计算各个采购单位的招标频次、月度预算总额趋势、热门采购品类的词云分析等。这些分析结果能生成直观的图表为决策提供数据支撑。5.3 应对更复杂的网站如果目标网站使用了JavaScript动态加载数据即数据不在初始HTML中而是通过Ajax请求获取RequestsBeautifulSoup的组合就力不从心了。方案一寻找隐藏的API。打开浏览器开发者工具的“Network” - “XHR”选项卡刷新页面查看是否有返回JSON数据的请求直接模拟这个请求往往更高效。方案二使用Selenium或Playwright。这些工具可以控制一个真实的浏览器等待JavaScript执行完毕后再获取完整的页面源码。虽然速度慢、资源消耗大但能解决绝大多数动态加载问题。这是爬虫技能树上的一个重要进阶分支。这个项目麻雀虽小五脏俱全。它串联起了HTTP请求、HTML解析、数据清洗、文件存储等爬虫核心环节。更重要的是它解决了一个真实的需求。当你看到自己抓取的几百条招标信息整齐地排列在Excel里并能快速筛选出高预算、对口的项目时那种效率提升带来的成就感正是学习技术最大的乐趣所在。在实际操作中最花时间的部分永远是“网页结构分析”和“数据字段定位”耐心和细心是关键。多尝试多调试每一个成功抓取的网站都会让你的技能包更厚实一分。