You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Python大规模抓取多分类动态加载的超市网站商品数据

问题分析与解决方案

你的代码在单分类场景运行正常,但扩展到多分类循环时崩溃,核心原因包括浏览器实例频繁创建销毁导致资源耗尽、固定等待时间不可靠、缺少异常处理,以及可能触发网站反爬机制。以下是具体改进方案和大规模爬虫最佳实践:

一、修复代码中的明显错误

你的getHtmlDynamic函数存在返回值错误:函数内生成的是soup对象,但最后返回未定义的html变量,这会直接导致后续调用失败。修正如下:

def getHtmlDynamic(url, driver):
    driver.get(url)
    # 后续用显式等待替代固定sleep
    soup = BeautifulSoup(driver.page_source, 'html5lib')
    return soup

二、核心改进方案

1. 复用浏览器实例

每次循环创建新Chrome实例会消耗大量系统资源,且容易被网站识别为爬虫。改为在循环外初始化一次浏览器,循环内复用:

import requests
import json
import pandas as pd
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
import time
import random

def getHtmlDynamic(url, driver):
    driver.get(url)
    # 显式等待价格元素加载完成,替代固定sleep
    try:
        WebDriverWait(driver, 15).until(
            EC.presence_of_all_elements_located((By.CLASS_NAME, 'contenedor-precio'))
        )
    except Exception as e:
        print(f"等待元素超时: {e}")
    soup = BeautifulSoup(driver.page_source, 'html5lib')
    return soup

def getProductsAndPrices(html):
    try:
        prodsJson = html.find_all('script', {'type': 'application/ld+json'})
        if len(prodsJson) < 2:
            print("未找到商品JSON数据")
            return pd.DataFrame()
        dfProds = pd.json_normalize(json.loads(prodsJson[1].contents[0])['itemListElement'])
        
        pricesList = html.find_all('div', {'class': 'contenedor-precio'})
        prices = []
        for row in pricesList:
            price_row = row.find_all('span')
            # 只取最后一个span(通常是最终价格),避免重复采集
            if price_row:
                prices.append(price_row[-1].text.strip())
        
        # 确保价格列表和商品数量匹配
        if len(prices) >= len(dfProds):
            dfProds['price'] = prices[:len(dfProds)]
        else:
            dfProds['price'] = None
            print("价格数量与商品不匹配")
        return dfProds
    except Exception as e:
        print(f"解析商品数据失败: {e}")
        return pd.DataFrame()

# 初始化浏览器(循环外只执行一次)
driver = webdriver.Chrome()
dfProductsConsolidated = pd.DataFrame([])

for idx, category in enumerate(dfCategories['categoryURL'][:10]):
    print(f"正在抓取第{idx+1}个分类: {category}")
    htmlProducts = getHtmlDynamic(url=category, driver=driver)
    dfProds = getProductsAndPrices(htmlProducts)
    
    if not dfProds.empty:
        dfProductsConsolidated = pd.concat([dfProductsConsolidated, dfProds], ignore_index=True)
    
    # 随机延迟,避免频繁请求
    time.sleep(random.randint(2, 5))

# 最后关闭浏览器
driver.quit()
# 保存数据
dfProductsConsolidated.to_csv('products.csv', index=False)

2. 用显式等待替代固定sleep

time.sleep(20)是盲等,既浪费时间又不可靠。改用WebDriverWait等待目标元素(如价格容器)加载完成,确保动态内容已渲染。

3. 增加异常处理

在数据解析和元素等待环节加入try-except,单个分类抓取失败不会导致整个程序崩溃,同时打印错误信息便于排查。

三、大规模爬虫最佳实践

1. 优先抓取API接口

动态加载的内容通常来自后端API,打开浏览器开发者工具(F12)的"网络"面板,过滤XHR/Fetch请求,找到返回商品和价格的API接口,直接用requests请求比Selenium高效10倍以上。比如你页面中的application/ld+json数据,或者价格加载的AJAX请求。

2. 控制请求频率

  • 加入随机延迟(如2-5秒),避免短时间内大量请求触发反爬。
  • 不要一次性抓取所有100个分类,可分批次抓取,比如每次抓10个,间隔一段时间再抓下一批。

3. 数据分批保存

每抓取几个分类就将数据写入文件,避免程序崩溃导致全部数据丢失。

4. 避免被反爬检测

  • 为浏览器设置用户代理(UA),模拟真实浏览器:
    options = webdriver.ChromeOptions()
    options.add_argument("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")
    driver = webdriver.Chrome(options=options)
    
  • 必要时使用代理IP,避免IP被封禁。

5. 遵守网站规则

查看网站的robots.txt,确认允许抓取的内容,避免违反网站条款。

内容的提问来源于stack exchange,提问作者Gianluca Peretti

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 05:22:44