1. 从需求到代码:这个价格分析爬虫到底在解决什么问题
先说个场景。我前阵子帮朋友做一份电商平台的类目价格报告,需求听起来特别简单:把某个类目下的商品价格全部拉下来,看看主流价位段集中在多少,低端、中端、高端分别有哪些品牌在卖。一开始我以为这活儿半小时就能搞定,结果手动打开网页一页一页翻,复制粘贴了不到五十条数据就崩溃了——商品数量过万,手动采集根本不现实。
这时候就需要一个能自动翻页、批量抓取、结构化存储的爬虫方案。本文要分享的正是这样一个实战项目:基于 Python 的电商类目商品价格分析爬虫,覆盖多页数据采集、价格区间统计分析,最终把数据导出为 CSV 文件,同时写入 SQLite 数据库做持久化。
注意:本文只讲公开商品信息(名称、价格、类目)的采集与统计分析,目标是个人学习与技术研究。分享的是通用技术思路,实际落地时请务必遵守目标网站的 robots 协议、相关法律法规,并控制请求频率,做个文明的爬虫开发者。
这个项目适合谁?刚学完 Python 基础语法、想拿真实项目练手的初学者,有一定爬虫经验但对数据存储和统计分析流程还不熟悉的中级开发者,以及需要快速做电商竞品价格调研的运营分析人员。无论你是哪个角色,看完这篇文章都能收获一套从采集到分析再到存储的完整闭环。
我会按真实的开发顺序来讲:先分析需求和技术选型,再手写单页采集代码,接着拆解多页翻页策略,然后做数据清洗和区间统计,最后分别实现 CSV 导出和 SQLite 持久化,并把整个过程中踩过的坑原原本本摆出来。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 开工前的技术选型与页面分析:为什么用 requests + BeautifulSoup 而不是 Selenium
2.1 请求库选型:requests 够用,就别上 Selenium
很多新手一上来就想用 Selenium 模拟浏览器,觉得"看起来更高级"。但我要泼一盆冷水:能不用浏览器渲染就用 requests。Selenium 需要额外安装浏览器驱动、占用大量内存、运行速度慢一个数量级,而且非常容易被反爬机制识别。对于静态渲染的商品列表页,requests 加合理的请求头就够了。
为什么选 requests?因为它极其轻量,只需要处理 HTTP 请求和响应,配合 BeautifulSoup 做 HTML 解析,整个链路清晰明了。我这次的目标页面是电商列表页,商品名称、价格、评论数等信息都直接渲染在 HTML 里,不需要执行 JavaScript 才能拿到数据,所以 requests 是最高效的选型。
python复制import requests
from bs4 import BeautifulSoup
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8"
}
2.2 页面结构拆解:先看 HTML 再写代码,别闭眼硬写
写爬虫最忌讳的事情就是不看页面结构直接写选择器。我拿到目标页面后的第一步,是打开开发者工具(F12),逐个检查需要的字段落在哪个标签节点里。
以某电商平台的商品列表页为例,典型结构是这样的:每个商品卡在一个 <li class="gl-item"> 或 <div class="product-item"> 里,商品标题在 div.p-name a 的 title 属性或 em 标签里,价格在 div.p-price strong i 标签里,评论数在 div.p-commit strong a 标签里。用 BeautifulSoup 的 select_one 方法一个个抽取,比 find_all 再遍历更清晰。
python复制def parse_product(item):
"""从单个商品节点中提取信息"""
title_tag = item.select_one("div.p-name a")
title = title_tag.get("title", "").strip() if title_tag else ""
price_tag = item.select_one("div.p-price strong i")
price = price_tag.get_text(strip=True) if price_tag else "0"
comment_tag = item.select_one("div.p-commit strong a")
comments = comment_tag.get_text(strip=True) if comment_tag else "0"
shop_tag = item.select_one("div.p-shop a")
shop = shop_tag.get_text(strip=True) if shop_tag else "未知店铺"
return {
"title": title,
"price": price,
"comments": comments,
"shop": shop
}
这里有个很重要的经验:先跑通单条解析,再上全页循环。我一般会先取一个商品的 HTML 节点,写一个测试脚本跑一下,确认四个字段都能正确提取,再写批量解析。如果一上来就写完整流程,出了问题根本不知道是数据没抓到还是解析逻辑写错了。
2.3 数据模型设计:字段越简单越好,但关键字段不能漏
在设计数据存储结构之前,先想清楚要分析什么。标题里提到"价格分析",那核心字段必须有商品名称、价格、评论数、店铺名。评论数可以用于加权分析——同样是 100 元的商品,一个卖了十万单,一个卖了十单,含金量完全不同。店铺名用于后续的品牌分布分析,属于可选项,但我建议加上,多个字段后期做分析时灵活很多。
原始的评论数字段里可能有"万"字后缀(比如"3.5万+"),价格字段里可能有"¥"符号或"起"字后缀。这些脏数据如果直接入库,后面的统计分析会非常痛苦。所以我在模型设计阶段就预留了一个清洗步骤,把数值型字段统一转为 float 或 int,把非数值内容过滤掉。这一步放在采集阶段做,比入库后再处理高效得多。
3. 多页数据采集的完整实现:从翻页逻辑到随机休眠
3.1 翻页参数解析:找到 URL 里的页码规律
多页采集的核心是找到翻页的 URL 规律。打开商品列表页后,手动点击第二页、第三页,观察地址栏 URL 的变化。比较常见的规律有两种:一种是路径参数式,比如 /list/xxx-0-0-0-0-0-0-0-0-0-0-0-0-0-1.html,最后一位数字代表页码;一种是查询参数式,比如 ?page=2&sort=default。
我这次遇到的正好是查询参数式,比较直观。用 page 参数控制页码,从第一页开始,每次循环加一。为了精确控制抓取范围,我封装了一个函数:传入类目 ID 和总页数,循环请求每一页,解析商品列表并累计到内存中的列表。
python复制def crawl_category(category_id, max_pages):
"""采集指定类目下前 max_pages 页的商品数据"""
all_products = []
base_url = f"https://example.com/list/{category_id}"
for page in range(1, max_pages + 1):
params = {
"page": page,
"sort": "default",
"delivery": "1"
}
response = requests.get(base_url, headers
