2026 Python采集实战:BeautifulSoup批量抓取多页电商商品数据

简介: 多页采集难点不在代码,而在稳定性:超时、结构突变、代理失效等常致中断。本文以实战为例,详解会话复用、智能重试、随机延时、异常隔离与CSV结构化存储,强调“稳准全”优于盲目提速,适合电商等真实场景。

单页商品抓取不难,真正容易出问题的是从第1页跑到第50页。

我做采集这些年遇到过不少类似情况:前几页运行正常,到了中间突然超时;某一页结构变化,整个程序直接退出;代理已经失效,脚本却还在对同一个地址反复重试。

所以,多页采集不能只在单页代码外面套一个for循环。至少要补上会话复用、有限重试、随机间隔、异常记录和CSV存储。

本文仍使用公开爬虫练习网站演示。实际采集电商平台前,应确认访问授权、服务条款和数据使用边界。

先建立可复用的请求会话

如果每一页都重新创建连接,会增加额外开销。requests.Session()可以复用部分连接及公共请求头:

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry


session = requests.Session()

session.headers.update({
   
    "User-Agent": (
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
        "AppleWebKit/537.36 (KHTML, like Gecko) "
        "Chrome/126.0.0.0 Safari/537.36"
    ),
    "Accept-Language": "zh-CN,zh;q=0.9"
})

retry = Retry(
    total=2,
    connect=2,
    read=2,
    status=2,
    backoff_factor=0.8,
    status_forcelist=[429, 500, 502, 503, 504],
    allowed_methods=["GET"]
)

adapter = HTTPAdapter(max_retries=retry)
session.mount("http://", adapter)
session.mount("https://", adapter)

重试次数不是越多越好。我一般先设为2次,用于处理短暂网络波动。遇到403、登录提示或验证码时,应停止并检查权限与访问策略,而不是通过无限重试继续施压。

编写多页商品解析逻辑

完整示例代码如下:

import csv
import random
import time
from urllib.parse import urljoin

import requests
from bs4 import BeautifulSoup
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry


BASE_URL = "https://books.toscrape.com/catalogue/page-{}.html"
OUTPUT_FILE = "ecommerce_products.csv"

session = requests.Session()

session.headers.update({
   
    "User-Agent": (
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
        "AppleWebKit/537.36 (KHTML, like Gecko) "
        "Chrome/126.0.0.0 Safari/537.36"
    )
})

retry = Retry(
    total=2,
    backoff_factor=0.8,
    status_forcelist=[429, 500, 502, 503, 504],
    allowed_methods=["GET"]
)

adapter = HTTPAdapter(max_retries=retry)
session.mount("http://", adapter)
session.mount("https://", adapter)


def fetch_page(page_number):
    page_url = BASE_URL.format(page_number)

    response = session.get(
        page_url,
        timeout=(5, 15)
    )
    response.raise_for_status()

    soup = BeautifulSoup(response.text, "html.parser")
    cards = soup.select("article.product_pod")

    products = []

    for card in cards:
        title_tag = card.select_one("h3 a")
        price_tag = card.select_one(".price_color")
        stock_tag = card.select_one(".instock.availability")

        if not title_tag or not price_tag:
            continue

        products.append({
   
            "页码": page_number,
            "商品名称": title_tag.get("title", "").strip(),
            "价格": price_tag.get_text(strip=True),
            "库存状态": (
                stock_tag.get_text(" ", strip=True)
                if stock_tag else ""
            ),
            "详情链接": urljoin(
                page_url,
                title_tag.get("href", "")
            )
        })

    return products


def save_products(products):
    fieldnames = [
        "页码",
        "商品名称",
        "价格",
        "库存状态",
        "详情链接"
    ]

    with open(
        OUTPUT_FILE,
        "w",
        newline="",
        encoding="utf-8-sig"
    ) as file:
        writer = csv.DictWriter(
            file,
            fieldnames=fieldnames
        )
        writer.writeheader()
        writer.writerows(products)


def main():
    all_products = []
    failed_pages = []

    for page_number in range(1, 11):
        try:
            page_products = fetch_page(page_number)
            all_products.extend(page_products)

            print(
                f"第{page_number}页完成,"
                f"获取{len(page_products)}条"
            )

        except requests.RequestException as error:
            failed_pages.append(page_number)
            print(f"第{page_number}页失败:{error}")

        time.sleep(random.uniform(1.5, 3.0))

    save_products(all_products)

    print(f"共保存{len(all_products)}条商品数据")
    print(f"失败页码:{failed_pages}")


if __name__ == "__main__":
    main()

这段代码有一个比较实用的设计:某一页失败后,只记录失败页码,不会让整个任务立即退出。后续可以单独补采失败页面,而不必把成功页面全部重跑一遍。

为什么要保留页码字段

保存结果时,我特意增加了“页码”字段。

商品名称、价格和链接是业务数据,页码则是采集链路数据。出现数量异常时,可以直接查看是哪一页缺失。如果不保存页码,最后只知道总量少了,却很难快速定位断点。

正式任务中还可以继续增加:

  • 采集时间;
  • HTTP状态码;
  • 请求耗时;
  • 出口标识;
  • 重试次数。

这些字段未必都要交付给业务人员,但对排查稳定性很有帮助。

代理IP应该在哪一步接入

如果只是抓取练习网站的10个页面,没有必要为了使用代理而使用代理。

当任务扩大到大量授权页面、不同地区商品展示验证或持续价格监测时,才需要把请求出口纳入设计。我一般会把代理认证地址写入环境变量:

import os

proxy_url = os.getenv("PROXY_URL")

if proxy_url:
    session.proxies.update({
   
        "http": proxy_url,
        "https": proxy_url
    })

接入极安代理时,也可以沿用这种方式。代理地址变化后只调整运行环境,不改采集逻辑,更适合长期维护。

不过,代理不能替代访问授权,也不能解决所有失败。403可能是权限或页面策略问题,429通常与请求频率有关,连接超时才更需要检查代理存活状态、认证配置和链路响应。

别一开始就把并发调得很高

不少人为了“提速”,单线程代码刚跑通就直接开20个线程。结果目标站响应变慢、429增加、代理连接数也被打满,最终实际完成速度反而下降。

我的习惯是先用低并发跑一小批页面,记录成功率和平均响应时间,再逐步增加任务量。每次只改一个参数,才能判断性能变化来自哪里。

对于BeautifulSoup多页采集来说,能稳定完成、失败可定位、结果可补采,比短时间内冲出一个很高的请求数更重要。先把单页解析做准,再把多页链路做稳,最后才轮到并发和代理优化。

相关文章
|
Java API 网络架构
深入理解 Spring Boot 中的 @RestController 注解:概念与实践
【4月更文挑战第20天】在现代Web开发中,创建RESTful服务已成为常态。Spring Boot通过提供@RestController注解,极大简化了REST API的开发过程。本篇博客旨在详细介绍@RestController的概念、优势以及在Spring Boot项目中的具体应用方法。
1391 8
|
30天前
|
Web App开发 人工智能 Java
【Fix】电脑底部的任务栏不见了怎么办,重启任务管理器搞定!
本文介绍了Windows任务栏消失的两种修复方法:通过任务管理器重启资源管理器进程或使用命令行强制重启explorer.exe
170 2
【Fix】电脑底部的任务栏不见了怎么办,重启任务管理器搞定!
|
29天前
|
JavaScript API 开发者
DeepSeek Harness 刚发布,先让它做了个网页
DeepSeek Harness(DSH)是DeepSeek开源的Agent执行框架,践行“Model + Harness = Agent”理念。v0.1开发者预览版发布次日即实测成功:一行命令`npx @deepseek-ai/dsh web`启动,自动完成搜索、规划、写HTML、本地验证全流程,支持插件扩展与完整执行轨迹追踪。(239字)
548 1
DeepSeek Harness 刚发布,先让它做了个网页
|
2月前
|
人工智能 安全 数据挖掘
从 Demo 到生产环境:AI Agent 项目的架构设计总结
本文探讨企业级AI Agent落地难点与实践路径,指出项目常卡在PoC阶段的根源在于架构设计、工具治理、数据质量与运维体系,而非模型能力。结合Multi-Agent架构、分层记忆、工具治理、安全防护及成本控制等实战经验,为企业提供从验证到规模化落地的系统方法论。(239字)
215 0
从 Demo 到生产环境:AI Agent 项目的架构设计总结
|
5月前
|
人工智能 安全 Linux
OpenClaw(小龙虾)从0到1部署手册:阿里云+本地全流程+千问/Coding Plan适配+避坑指南
2026年,开源AI Agent框架OpenClaw(曾用名Moltbot、Clawdbot,因Logo酷似小龙虾被网友亲切称为“小龙虾”)凭借“本地优先+主动执行”的核心特性,成为现象级工具。它打破了传统AI仅能“对话答疑”的局限,通过自主规划任务、调用工具、执行操作,实现“自然语言指令→AI规划→工具调用→任务落地”的全闭环,适配办公自动化、文件管理、多渠道协作等全场景需求。
938 1
|
7月前
|
人工智能 监控 算法
基于 YOLOv8 的水体污染目标检测系统 [目标检测完整源码]
本文围绕水体环境治理这一典型的现实需求,系统性地介绍了一个基于 YOLOv8 的水体污染智能监控解决方案。从应用背景出发,逐步阐述了系统架构设计、模型选型原因、数据集构建、训练与推理流程,以及 PyQt5 可视化界面的工程实现方式。该项目不仅验证了 YOLOv8 在复杂水面场景下对废弃物、污染区域、漂浮物等目标的良好检测能力,也通过完整的软件形态提升了算法的可用性与落地价值。整体来看,该方案兼顾技术先进性与工程实用性,为水环境监测、环保执法及无人机巡检等场景提供了一条可复用、可扩展的智能化实现路径。
305 9
|
8月前
|
SQL 存储 分布式计算
Hologres Dynamic Table在淘天价格力的业务实践
淘天价格力团队依托Hologres Dynamic Table,实现亿级商品数据的高效治理。通过增量刷新与全量刷新机制,支持秒级圈选、分钟级报表更新,满足大促场景下高时效、多维度分析需求,显著提升数据灵活性与决策效率。
|
12月前
|
XML JSON 编解码
从JSON到Protobuf,深入序列化方案的选型与原理
序列化是数据跨边界传输的“翻译官”,将结构化数据转为二进制流。JSON可读性强但冗余大,Protobuf高效紧凑、性能优越,成主流选择。不同场景需权衡标准化与定制优化,选最合适方案。
687 3
|
运维 监控 Kubernetes
Bitnami 替代品:Websoft9 如何接力单服务器多应用时代
Bitnami 曾为开源应用部署带来革命性体验,但随着 Docker 成熟与战略转向云原生,其单机多应用支持逐渐弱化。面对多应用管理分散、资源冲突、运维工具缺失等痛点,Websoft9 应运而生,提供一键部署、统一管理、智能调度等能力,全面优化单服务器多应用运维体验,成为 Bitnami 的理想继任者。
434 0
Bitnami 替代品:Websoft9 如何接力单服务器多应用时代
|
缓存 NoSQL Java
Redis的操作以及SpringCache框架
以及如何在Spring Boot应用中使用Spring Cache框架集成Redis。Redis提供了丰富的数据结构和高效的内存存储能力,结合Spring Cache框架,可以显著提高应用的性能和响应速度。
490 7

热门文章

最新文章