Python + BS4实战:手把手带你爬取商业数据
目录

🌟 Hello,我是蒋星熠Jaxonic!
🌈 在浩瀚无垠的技术宇宙中,我是一名执着的星际旅人,用代码绘制探索的轨迹。
🚀 每一个算法都是我点燃的推进器,每一行代码都是我航行的星图。
🔭 每一次性能优化都是我的天文望远镜,每一次架构设计都是我的引力弹弓。
🎻 在数字世界的协奏曲中,我既是作曲家也是首席乐手。让我们携手,在二进制星河中谱写属于极客的壮丽诗篇!
在开始之前,大家可以先看我之前的两篇文章作为铺垫~
上一篇Re详解:正则表达式:用Python Re模块驯服文本数据的艺术-ZEEKLOG博客
上一篇Re的实战案例:Python爬虫抓取豆瓣TOP250数据-ZEEKLOG博客

一、bs4篇
经过上一个文章的学习我们发现Re模块配合正则表达式固然暴力直接,但是根据源代码一个个匹配写正则太过麻烦。

因此我们来引入一个新朋友:BeautifulSoup(bs4),并且附上实战案例~
让BS4告别繁琐的正则匹配~~~
1.bs4介绍
1.1 什么是BeautifulSoup4?
BeautifulSoup4(简称BS4)是Python中最流行的HTML/XML解析库,它能够从复杂的网页代码中优雅地提取出你需要的数据。无论是爬虫新手还是专业开发者,BS4都是网页数据提取的首选工具。
1.2 为什么选择BeautifulSoup4?
核心优势
- 语法简单:比正则表达式更直观易学
- 容错性强:即使网页代码不规范也能正常解析
- 支持多种解析器:兼容lxml、html.parser、html5lib等
- Python风格:提供符合Python习惯的API设计

2.bs4详解
2.1 首先下载bs4

2.2 接下来引入一个使用bs4的例子让我们快速熟悉它
from bs4 import BeautifulSoup" <ul> <li><a href="zhangwuji.com">张无忌</a></li> <li><a href="zhouxingchi.com">周星驰</a></li> <li><a href="zhubajie.com">猪八戒</a></li> <li><a href="wuzetian.com">武则天</a></li> </ul> """ #1.初始化BeautifulSoup对象,处理的是html格式的,用html的解析器进行解析 page=BeautifulSoup(html,"html.parser") page.find("标签名",attrs={"属性":"值"}) #查找某个元素,只会找到一个结果 page.find_all("标签名",attrs={"属性":"值"}) #找到一堆结果 li=page.find("li",attrs={"id":"abc"}) a=li.find("a")#可以连续套娃find,这时对属性没有条件要求 print(a)#导出标签 print(a.text)#把标签里面的值导出来,拿文本 print(a.get("href"))#把标签里对应的href属性的值导出来,拿属性 li_list=page.find_all("li")#找到html代码中所有的带有li的标签,此时li_list接受的是列表 for li in li_list:#列表循环 a=li.find("a") text=a.text href=a.get("href") print(text,href) 2.3 运行结果

可以发现都能得到我们想要的结果,相较于以前re那样原始的提取方式,bs4更显方便。
代码解析我都一步步放在了注释中,非常详细。
3.bs4使用实战案例
网址:北京新发地农副产品批发市场信息中心食品、农产品价格行情-食品商务网
可以发现数据从这里开始:

3.1 完整代码
import time from bs4 import BeautifulSoup import requests import random f=open("caijia.csv",mode="w",encoding="utf-8") # 写入CSV表头 f.write("产品名称,规格,平均价格,日期,趋势\n") numsum=0 #数据总数 n=1 #页数 while n<=29: url = f"https://price.21food.cn/guoshu-p{n}.html" #要进行伪装,否则提取的是网页主页源代码 headers={ "user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36" } resp=requests.get(url,headers=headers) resp.encoding="utf-8" #print(resp.text) #1.初始化BeautifulSoup对象,处理的是html格式的,用html的解析器进行解析 page=BeautifulSoup(resp.text,"html.parser") uls=page.find_all("ul")#找到存有信息的范围标签(列表) #print(uls) #测试能否拿到ul数据 #进行循环遍历,找到对应的数据 num = 0 #每页的数据量 for ul in uls: tables = ul.find_all("table") for table in tables: tr = table.find("tr") tds=tr.find_all("td") if len(tds) >= 5: #防止提取的时候截断 name=tds[0].text.strip() size = tds[1].text.strip() price = tds[2].text.strip() date = tds[3].text.strip() if tds[4].text.strip() == "": # 检查是否为空字符串 trend = None else: trend = tds[4].text.strip() f.write(f"{name}{size}{price}{date}\n") num=num+1 #print(name,size,price,date,trend) print(f"第{n}页已经爬取完,共{num}条数据") numsum=num+numsum n=n+1 sleep_time = random.uniform(1, 2) time.sleep(sleep_time) f.close() resp.close() print(f"全部数据已经爬取完毕!!!共{numsum}条数据") 这里翻页爬取时有个坑!博主在翻页爬的时候总是从第一页开始截断且报错:

经过反复研究代码,发现


3.2 为什么会影响翻页
没有条件检查时:
- 程序遇到只有3个td的表格
- 尝试访问
tds[4]时出现IndexError - 程序异常终止,无法继续执行到
n = n + 1 - 所以卡在第一页
有条件检查时:
- 程序遇到只有3个td的表格
- 条件
len(tds) >= 5为 False - 跳过这个表格,继续处理下一个
- 所有表格处理完后,正常执行
n = n + 1 - 继续下一页的爬取
3.3 反爬机制
由于网站限制,最终只能爬取五页数据,150条数据,研究了下发现网页有反爬验证。。。
3.4 已知信息
目前得到以下信息:
1.网站标记的是公网IP而不是代理IP
公网IP和代理池IP的区别:
形象比喻
- 公网IP = 你的家庭住址
- 代理池IP = 快递中转站/临时邮箱
详细区别对比
| 特征 | 公网IP | 代理池IP |
|---|---|---|
| 来源 | 运营商分配给你的固定或动态IP | 代理服务商提供的第三方IP |
| 归属 | 你的真实网络身份 | 中间服务器的IP |
| 稳定性 | 相对稳定 | 频繁更换 |
| 成本 | 免费(包含在网费中) | 需要付费购买 |
| 速度 | 直接连接,速度快 | 经过中转,速度较慢 |
| 匿名性 | 低(直接暴露) | 高(隐藏真实IP) |
2.一直爬取,公网IP会被禁,导致一直滑块验证(因为博主被禁了之后换成手机热点就好了)
3.在网页翻页多少次都不会触发滑块验证,除非把JS禁用了(说明网站会对JS环境做一个验证)

4.上述代码一个公网IP爬虫一次性只能爬5页
3.5 解决思路
这个我们后续再进行解决,目前的思路是:
思路1
避开滑块验证
1:设置动态公网IP(软路由)
2.模拟JavaScript执行和动态行为和丰富请求头(其它方法,例如Selenium)
浏览器执行的JS:
javascript
// 网站可能通过JS检测用户行为
document.addEventListener('mousemove', trackMouse) // 鼠标移动
document.addEventListener('scroll', trackScroll) // 滚动行为
document.addEventListener('click', trackClicks) // 点击模式
setTimeout(trackBehavior, 5000) // 行为分析
代码爬虫:无JavaScript执行无事件监听无动态行为分析
思路2
利用JS解决滑块验证(但有个问题是如果一直爬取,公网IP会被禁导致一点数据都看不到了)
3.6 结果展示



3.7 容易混淆的一点
Re模块正则表达式提取:

这里的finditer是迭代提取(懒加载),并不是一次性都加载给result,而是一遍一遍迭代的过程中加载。

这里的find_all函数是一次性全加载给tables
特性区分:
| 特性 | 正则表达式 | BeautifulSoup |
|---|---|---|
| 数据提取方式 | .group("组名") | .text |
| 返回对象类型 | 正则匹配对象 | BeautifulSoup元素对象 |
| 语法 | item.group("name") | item[0].text |
| 适用场景 | 简单文本提取 | 复杂HTML结构解析 |
3.8 图片爬虫
关于图片的爬虫也类似:
1.利用拼接获得图片子页面的跳转路径
2.在子页面源代码中获得<img src>的标签拿到图片的下载路径
3.用requests请求图片的二进制内容
4.将二进制串转化成图片输出保存
样例代码:
# 下载图片 img_resp= requests.get(img_src) #print(img_resp.text)#注意,图片不是文本,不能获取text的内容 withopen(f"[n}.jpg",mode="wb")asf:#注意,此时写入到文件的是字节,所以必须是wbI f.write(img_resp.content)#把图片信息写入到文件中 
今天的分享就到这里啦~后面我会分享xpath的教程和本篇章的解决方案~谢谢大家!
■ 我是蒋星熠Jaxonic!如果这篇文章在你的技术成长路上留下了印记
■ 👁 【关注】与我一起探索技术的无限可能,见证每一次突破
■ 👍 【点赞】为优质技术内容点亮明灯,传递知识的力量
■ 🔖 【收藏】将精华内容珍藏,随时回顾技术要点
■ 💬 【评论】分享你的独特见解,让思维碰撞出智慧火花
■ 🗳 【投票】用你的选择为技术社区贡献一份力量
■ 技术路漫漫,让我们携手前行,在代码的世界里摘取属于程序员的那片星辰大海
