Python爬虫实战:双十一商品数据抓取与Excel分析全流程
2026/8/27 6:12:21 网站建设 项目流程

1. 项目缘起:为什么要在双十一用Python抓商品数据?

又到一年双十一,铺天盖地的优惠券、满减规则和直播预告,看得人眼花缭乱。作为一个既想省钱又怕麻烦的“技术型”消费者,我每年都会遇到同样的困境:想对比不同平台、不同店铺的同款商品价格,手动一个个去搜、去记,不仅效率低下,还容易遗漏。更重要的是,很多所谓的“历史最低价”和“预售优惠”,如果不把数据拉出来横向对比,根本看不出门道。

今年,我决定不再被动。与其被复杂的营销规则牵着鼻子走,不如主动出击,用Python写个脚本,把感兴趣的商品信息都抓下来,存到Excel里慢慢分析。这听起来像是个“爬虫”项目,没错,它的核心确实是网络数据抓取。但它的目的不是为了“爬”而“爬”,而是为了解决一个非常具体的消费决策问题:在信息爆炸的购物节里,如何高效、准确地获取并管理商品数据,从而做出更明智的选择?

这个项目适合所有对Python有基础了解,并且受困于信息筛选的朋友。无论你是想为自己“剁手”做参谋,还是想学习如何将Python应用于解决实际生活问题,甚至是为电商数据分析做入门练习,接下来的内容都会给你一套可以直接“抄作业”的完整方案。我们将从环境搭建开始,一步步走到数据保存,过程中我会分享我踩过的坑和总结的技巧。

2. 环境与工具准备:搭建你的数据分析工作台

工欲善其事,必先利其器。在开始写代码之前,我们需要一个稳定、高效的开发环境。这个环境的核心是Python解释器和一个好用的代码编辑器或集成开发环境(IDE)。

2.1 Python安装与包管理

首先,确保你的电脑上安装了Python。我强烈推荐使用Python 3.7及以上版本,因为很多现代库对旧版本的支持已经停止。你可以从Python官网下载安装包,安装时务必勾选“Add Python to PATH”选项,这样你就可以在命令行中直接使用pythonpip命令了。

安装完成后,打开命令行(Windows上是CMD或PowerShell,Mac/Linux上是Terminal),输入python --version来验证安装是否成功。接下来,我们需要安装这个项目所需的几个核心库。我们将使用pip,这是Python的包管理工具,它就像是一个应用商店,可以轻松安装和管理第三方代码库。

这个项目主要依赖以下三个库:

  1. requests: 用于向网站发送HTTP请求,获取网页的HTML源代码。它是我们获取数据的“手”。
  2. BeautifulSoup4 (bs4): 用于解析HTML源代码,从中提取出我们需要的结构化信息,比如商品标题、价格、链接。它是我们理解网页内容的“大脑”。
  3. pandas: 这是一个功能强大的数据分析库。我们这里主要用它来将抓取到的数据整理成表格(DataFrame),并轻松地导出为Excel文件。它是我们整理和输出数据的“流水线”。

在命令行中,一次性安装它们:

pip install requests beautifulsoup4 pandas openpyxl

这里多安装了一个openpyxl,因为pandas在读写新版Excel文件(.xlsx格式)时需要这个引擎。安装过程可能会持续一两分钟,取决于你的网络速度。

2.2 开发工具选择:VSCode的轻量级配置

对于代码编辑器,我的首选是Visual Studio Code(VSCode)。它免费、轻量、插件生态丰富,对Python的支持非常好。如果你还没有安装,可以去其官网下载。

安装好VSCode后,你需要进行简单的Python环境配置:

  1. 打开VSCode,安装官方提供的“Python”扩展。在扩展市场搜索“Python”,由Microsoft发布的那个就是。
  2. 打开或创建一个项目文件夹,然后新建一个Python文件,例如double11_spider.py
  3. 在VSCode的左下角,你可以看到当前选择的Python解释器版本。点击它,在弹出的列表中选择你刚刚安装的Python环境。这一步确保了VSCode的代码提示、调试等功能都基于正确的环境。

注意:有时你可能会遇到“请安装缺失的包以使用此工作流”这类提示,这通常是因为某些VSCode扩展或项目配置依赖特定的Python包。根据提示在终端里运行指定的pip install命令即可。对于我们的核心任务,只要requestsbs4pandas安装成功,就不会有问题。

至此,你的“数字矿工”工作台就搭建好了。我们有了获取数据的工具(requests)、解析数据的工具(BeautifulSoup)、处理数据的工具(pandas),以及一个舒适的编码环境(VSCode)。接下来,我们就要深入“矿场”——电商网站的页面结构了。

3. 目标分析与页面结构解析:找到数据的藏身之处

在动手写爬虫之前,最重要的一步是“侦察”。我们需要弄清楚:我们想要的数据(商品名、价格、销量等)在网页的哪个位置?它们是以什么样的HTML标签和属性存在的?这一步通常通过浏览器的“开发者工具”来完成。

以国内某主流电商平台(例如淘宝)的搜索列表页为例。假设我们想抓取“蓝牙耳机”这个关键词下的商品。我们打开平台,搜索“蓝牙耳机”,然后按下F12键(或右键点击页面选择“检查”)打开开发者工具。

3.1 使用开发者工具定位元素

在开发者工具的“元素”(Elements)面板,你可以看到构成当前页面的所有HTML代码。这个面板左上角有一个箭头图标(或鼠标图标),点击它,然后将鼠标移动到网页上的某个商品区域(比如商品标题),你会发现对应的HTML代码区域会被高亮显示。

通过反复尝试和观察,你会发现,在列表页中,每一个商品卡片通常被包裹在一个具有特定类名(class)或数据属性(data-*)的容器<div>里。例如,它可能看起来像这样:

<div class="J_MouserOnverReq">import requests from bs4 import BeautifulSoup import pandas as pd import time import random def get_page_html(url): """ 发送HTTP请求,获取网页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: # 发送GET请求 response = requests.get(url, headers=headers, timeout=10) # 检查请求是否成功(状态码200) response.raise_for_status() # 设置正确的编码,避免中文乱码 response.encoding = response.apparent_encoding or 'utf-8' return response.text except requests.exceptions.RequestException as e: print(f"请求页面失败: {url}") print(f"错误信息: {e}") return None # 示例:假设这是某个商品列表页的URL(此处为示例,需替换为实际可访问的URL) base_url = "https://example.com/search?q=蓝牙耳机&page=1" html_content = get_page_html(base_url) if html_content: print("页面获取成功!") else: print("页面获取失败,请检查网络或URL。")

这段代码定义了一个健壮的请求函数。它设置了User-Agent来伪装成浏览器,使用了try-except来捕获网络请求可能出现的异常(如超时、连接错误),并处理了编码问题以防止中文乱码。这是编写生产级爬虫的基础。

4.2 使用BeautifulSoup解析与数据提取

拿到HTML字符串后,我们用BeautifulSoup将其解析成一个可以遍历和搜索的树形结构。

def parse_product_list(html): """ 解析商品列表页HTML,提取商品信息 """ if not html: return [] soup = BeautifulSoup(html, 'html.parser') product_list = [] # 根据之前分析的页面结构,找到所有商品卡片的容器 # 这里的选择器 '.item.J_MouserOnverReq' 是一个示例,你需要根据实际页面调整 items = soup.select('.item.J_MouserOnverReq') for item in items: product_info = {} # 提取商品标题 title_elem = item.select_one('.title a') product_info['标题'] = title_elem.get_text(strip=True) if title_elem else 'N/A' # 提取商品价格 price_elem = item.select_one('.price strong') product_info['价格'] = price_elem.get_text(strip=True) if price_elem else 'N/A' # 提取商品链接(可能需要补全为完整URL) link_elem = item.select_one('.pic a') if link_elem and link_elem.get('href'): href = link_elem['href'] # 如果链接是相对路径,则补全为绝对路径 if href.startswith('//'): product_info['链接'] = 'https:' + href elif href.startswith('/'): product_info['链接'] = 'https://example.com' + href # 替换为实际域名 else: product_info['链接'] = href else: product_info['链接'] = 'N/A' # 提取商品销量 sales_elem = item.select_one('.deal-cnt') product_info['月销量'] = sales_elem.get_text(strip=True) if sales_elem else 'N/A' # 提取商品ID(可能从data-id属性获取) product_info['商品ID'] = item.get('data-id', 'N/A') product_list.append(product_info) # 添加一个短暂的随机延时,模拟人工操作,避免请求过快 time.sleep(random.uniform(0.5, 1.5)) return product_list # 使用示例 if html_content: products = parse_product_list(html_content) print(f"本页共解析到 {len(products)} 个商品。") for p in products[:2]: # 打印前两个商品看看 print(p)

parse_product_list函数是数据提取的核心。我们使用soup.select()soup.select_one()方法,通过CSS选择器精准定位到包含信息的HTML元素。get_text(strip=True)用于获取元素的文本并去除首尾空白。get()方法用于获取元素的属性值。代码中加入了详细的判断(if ... else),以防止因为某个元素缺失而导致程序崩溃。最后的time.sleep是一个重要的反反爬虫技巧。

4.3 实现翻页与多页数据抓取

一个搜索关键词的结果通常有多页。我们需要模拟翻页行为,抓取所有页面的数据。翻页的逻辑通常是URL中有一个page参数在变化。

def crawl_multiple_pages(base_keyword, max_pages=5): """ 抓取多页商品数据 base_keyword: 搜索关键词 max_pages: 计划抓取的最大页数 """ all_products = [] for page in range(1, max_pages + 1): print(f"正在抓取第 {page} 页...") # 构造每一页的URL。这里URL格式是示例,需要根据实际网站调整 url = f"https://example.com/search?q={base_keyword}&page={page}" html = get_page_html(url) if not html: print(f"第 {page} 页抓取失败,跳过。") continue products_on_page = parse_product_list(html) all_products.extend(products_on_page) print(f"第 {page} 页抓取完成,获得 {len(products_on_page)} 个商品。累计 {len(all_products)} 个。") # 每抓完一页,等待稍长时间,避免被封IP if page < max_pages: sleep_time = random.uniform(2, 5) print(f"等待 {sleep_time:.2f} 秒后继续下一页...") time.sleep(sleep_time) return all_products # 执行多页抓取 keyword = "蓝牙耳机" all_products_data = crawl_multiple_pages(keyword, max_pages=3) # 先抓3页试试 print(f"抓取结束,总共获得 {len(all_products_data)} 条商品数据。")

这个函数通过循环page参数,构建每一页的URL,然后重复调用get_page_htmlparse_product_list。它将所有页面的结果收集到all_products列表中。翻页间的延时设置得比单页内解析延时更长,进一步降低了请求频率。

5. 数据清洗与保存:用Pandas打造规整的Excel报表

抓取到的原始数据往往存在一些问题:价格可能带有“¥”符号或“起”字,销量可能是“1万+”这样的文本,我们需要将其转换为适合分析的格式。然后,用Pandas将其保存为Excel。

5.1 使用Pandas进行数据清洗与转换

Pandas的DataFrame是处理表格数据的利器。

def clean_and_save_to_excel(product_list, filename='double11_products.xlsx'): """ 清洗数据并保存到Excel """ if not product_list: print("没有数据可保存。") return # 将数据列表转换为Pandas DataFrame df = pd.DataFrame(product_list) # 数据清洗示例 # 1. 价格清洗:去除货币符号,转换为浮点数 if '价格' in df.columns: # 使用正则表达式提取数字(包括小数点) df['价格_数值'] = df['价格'].str.extract(r'(\d+\.?\d*)').astype(float) # 你也可以选择替换掉非数字字符 # df['价格_数值'] = df['价格'].replace(r'[^\d.]', '', regex=True).astype(float) # 2. 销量清洗:将“1万+”转换为10000,“2000+”转换为2000 if '月销量' in df.columns: def convert_sales(s): if isinstance(s, str): if '万' in s: num = s.replace('万', '').replace('+', '') try: return float(num) * 10000 except: return None else: # 去除“+”等字符,只留数字 num = s.replace('+', '') try: return float(num) except: return None return s df['月销量_数值'] = df['月销量'].apply(convert_sales) # 3. 去重:根据商品ID去重(如果ID可靠) if '商品ID' in df.columns and df['商品ID'].notna().any(): initial_count = len(df) df.drop_duplicates(subset=['商品ID'], keep='first', inplace=True) print(f"去重后,数据从 {initial_count} 条减少到 {len(df)} 条。") # 4. 处理缺失值:可以用特定值填充,或删除 # df.fillna('N/A', inplace=True) # 用‘N/A’填充所有NaN # df.dropna(subset=['价格_数值'], inplace=True) # 删除价格为空的行 # 保存到Excel try: # 使用openpyxl引擎写入.xlsx文件 with pd.ExcelWriter(filename, engine='openpyxl') as writer: df.to_excel(writer, index=False, sheet_name='商品数据') # index=False不保存行索引 print(f"数据已成功保存到文件: {filename}") print(f"共保存 {len(df)} 条记录,列名为: {', '.join(df.columns)}") except Exception as e: print(f"保存Excel文件时出错: {e}") # 使用示例 clean_and_save_to_excel(all_products_data, '双十一蓝牙耳机商品列表.xlsx')

这个清洗函数展示了几个常见操作:使用str.extract进行正则提取、使用apply方法进行自定义函数转换、使用drop_duplicates进行去重。清洗后的数据(如价格_数值月销量_数值)是数值类型,可以直接用于后续的排序、计算和图表分析。

5.2 高级技巧:数据透视与多Sheet保存

Pandas的能力远不止于此。我们可以对清洗后的数据做简单的分析,并保存到同一个Excel文件的不同工作表(Sheet)中。

def enhanced_analysis_and_save(df, filename='double11_analysis.xlsx'): """ 进行简单分析并保存到多Sheet的Excel """ with pd.ExcelWriter(filename, engine='openpyxl') as writer: # Sheet1: 原始/清洗后的完整数据 df.to_excel(writer, index=False, sheet_name='原始数据') # Sheet2: 按价格区间统计商品数量 if '价格_数值' in df.columns: # 创建价格区间 bins = [0, 100, 500, 1000, 2000, 5000, float('inf')] labels = ['0-100', '101-500', '501-1000', '1001-2000', '2001-5000', '5000+'] df['价格区间'] = pd.cut(df['价格_数值'], bins=bins, labels=labels, right=False) price_summary = df['价格区间'].value_counts().sort_index() price_summary_df = price_summary.reset_index() price_summary_df.columns = ['价格区间', '商品数量'] price_summary_df.to_excel(writer, index=False, sheet_name='价格分布') # Sheet3: 销量Top 10的商品 if '月销量_数值' in df.columns and '标题' in df.columns: top_sales = df.nlargest(10, '月销量_数值')[['标题', '价格_数值', '月销量_数值', '链接']] top_sales.to_excel(writer, index=False, sheet_name='销量Top10') # Sheet4: 简单的描述性统计 if '价格_数值' in df.columns: stats_df = df['价格_数值'].describe().to_frame().T # 描述性统计转成DataFrame stats_df.to_excel(writer, sheet_name='数据统计') print(f"增强分析报告已保存至: {filename}") # 假设df是清洗后的DataFrame # df = pd.DataFrame(all_products_data) # ... 执行清洗步骤 ... # enhanced_analysis_and_save(df)

这个函数创建了一个包含多个工作表的Excel文件:“原始数据”存放所有信息,“价格分布”展示了不同价位段的商品数量,“销量Top10”列出了最畅销的商品,“数据统计”则提供了价格的平均值、标准差等基本信息。这样,当你打开这个Excel文件时,就能获得一个初步的数据分析报告。

6. 实战中的坑与应对策略

理论很美好,但实战中总会遇到各种意想不到的问题。下面是我在类似项目中总结的几个常见“坑”及其应对策略。

6.1 请求被拒绝与反爬虫对抗

这是最常见的问题。你可能遇到403 Forbidden错误,或者收到的HTML里包含“访问过于频繁”的提示。

  • 策略一:完善请求头(Headers)。只设置User-Agent往往不够。复制浏览器中真实请求的Headers(在开发者工具Network面板里查看),特别是CookieReferer,有时Accept-LanguageAccept-Encoding也有关。用requestsheaders参数模拟。
    headers = { 'User-Agent': '...', 'Accept': 'text/html,application/xhtml+xml...', 'Accept-Language': 'zh-CN,zh;q=0.9', 'Referer': 'https://www.example.com/', 'Cookie': '你的Cookie字符串' # 谨慎使用,注意隐私和时效性 }
  • 策略二:使用会话(Session)requests.Session()可以保持会话,自动处理Cookie,使多次请求更像同一个浏览器会话。
    session = requests.Session() session.headers.update(headers) response = session.get(url)
  • 策略三:添加延时与随机性。这是最基本的道德和策略。在请求间使用time.sleep(),并且延时时间最好随机化,不要固定间隔。
    time.sleep(random.uniform(1, 3)) # 随机等待1到3秒
  • 策略四:使用代理IP。如果单个IP被封锁,可以考虑使用代理IP池来轮换IP地址。但这涉及额外成本和复杂度,对于个人学习和小规模抓取,优先使用前三种策略。

重要提示:始终尊重网站的robots.txt文件和服务条款。对于明确禁止爬取或没有公开接口的网站,不要强行抓取。本项目旨在技术学习,请将目标定为允许爬取的公开数据源或用于练习的测试网站。

6.2 页面结构变动导致解析失败

你今天写的CSS选择器,可能因为网站前端的改版明天就失效了。代码报错,找不到元素。

  • 策略:编写健壮的解析代码。不要依赖过于复杂或脆弱的CSS路径。尽量使用具有唯一性的idclass,或者结合标签和属性选择器。广泛使用if判断元素是否存在。
    # 更健壮的写法 price_container = item.find('div', class_='price') if price_container: price_elem = price_container.find('strong') price = price_elem.text.strip() if price_elem else 'N/A' else: price = 'N/A'
  • 策略:定期维护与日志。将关键的解析步骤和抓取到的URL记录下来。当解析失败时,日志能帮你快速定位是哪一批数据或哪一个页面出了问题。可以考虑将原始HTML保存到本地文件进行调试。
    with open(f'debug_page_{page}.html', 'w', encoding='utf-8') as f: f.write(html_content)

6.3 数据保存与格式问题

  • 问题:Excel打开乱码。这通常是因为编码问题。确保在保存为Excel时,Pandas使用的是正确的引擎(openpyxlfor .xlsx),并且在之前处理HTML时已经正确设置了编码(response.encoding)。
  • 问题:数值被保存为文本。在Excel中,清洗后的“价格_数值”列可能还是文本格式,无法求和。这通常是因为原始数据中混入了非数字字符,清洗不彻底。确保在astype(float)之前,字符串是纯粹的数字格式。可以使用pd.to_numeric(errors=‘coerce’)函数,它会将无法转换的值设为NaN。
    df['价格_数值'] = pd.to_numeric(df['价格'].str.replace(r'[^\d.]', '', regex=True), errors='coerce')
  • 问题:文件过大或写入慢。如果数据量极大(数十万行),一次性用to_excel写入可能会内存不足或很慢。可以考虑分块写入,或者使用to_csv保存为CSV格式,后者更轻量、更快。

7. 项目扩展与进阶思路

完成了基础的数据抓取和保存,这个项目还有很多可以深化和扩展的方向,让它从一个脚本变成一个真正有用的工具。

7.1 构建图形化用户界面(GUI)

对于不懂代码的朋友,一个带有按钮和表格的窗口显然比运行Python脚本更友好。你可以使用tkinter(Python标准库)、PyQtDearPyGui等库来构建一个简单的桌面应用。

应用可以包含以下功能:

  • 输入关键词和抓取页数的文本框。
  • 一个“开始抓取”按钮。
  • 一个显示抓取进度的进度条或标签。
  • 一个表格控件(如tkinterTreeview)来实时展示抓取到的数据。
  • 一个“导出到Excel”按钮。

这样,你就创建了一个专属的“双十一比价小助手”。

7.2 定时任务与自动化监控

双十一的预售期很长,价格可能每天都在变。你可以使用Python的schedule库或操作系统的定时任务(如Linux的cron或Windows的“任务计划程序”),让脚本每天定时运行。

脚本可以:

  1. 抓取你关注商品的最新价格。
  2. 与昨天保存的历史价格对比。
  3. 如果发现降价幅度达到你设定的阈值(比如10%),就自动发送一封邮件或一条钉钉/微信消息提醒你。

这就实现了价格监控自动化,不错过任何一次真降价。

7.3 深入数据分析与可视化

有了干净的Excel数据,你可以用Pandas进行更深入的分析,并用matplotlibseaborn库生成图表。

  • 价格分布直方图:看看大部分蓝牙耳机集中在哪个价位段。
  • 价格-销量散点图:分析价格和销量之间是否存在相关性?是越便宜卖得越多,还是某个中间价位最受欢迎?
  • 品牌分析:从标题中提取品牌信息(可能需要用到正则表达式或简单分词),统计哪个品牌出现的频率最高,哪个品牌的平均售价最高。

这些分析能帮你更深刻地理解市场,而不仅仅是罗列商品列表。

7.4 转向更高效的爬虫框架

当项目变得复杂,需要管理大量请求、处理异步、应对更复杂的反爬时,可以考虑使用专业的爬虫框架,如Scrapy

Scrapy提供了项目结构、异步请求引擎、中间件、管道等一套完整机制。它能让你的代码更模块化,更容易扩展和维护,并且性能通常比requests同步请求更高。将本项目改写成Scrapy版本,是一个很好的进阶练习。

从打开浏览器搜索商品,到运行脚本自动获取并分析数据,这个过程本身就是一个将技术应用于生活、提升效率的绝佳案例。我个人的体会是,最初的难点往往在于如何精准地定位网页元素和应对网站的反爬措施。多使用浏览器的开发者工具进行练习,多思考如何让你的请求看起来更“像人”,这两点能解决大部分入门期的问题。最后,请务必记住:技术是用来创造价值的,在享受自动化便利的同时,一定要合法合规地使用爬虫,尊重数据所有者的权益。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询