Walmart 是全球最大的网络零售商之一,它所展示的商品数据,包括标题、价格、评分和库存状态,对价格研究、市场监控或零售产品构建来说都相当有用。问题在于 Walmart 大量在客户端渲染内容,并对爬虫严防死守,因此普通的 HTTP 请求只会返回一个空壳或拦截页面。本指南将向你展示如何用 Selenium 抓取 Walmart 商品页面:一个小型、可运行的 Python 方案,它驱动无头 Firefox,将流量路由通过 Crawlbase Smart AI Proxy,使请求看起来像真实访客,提取公开的商品字段,并将其写入磁盘。

为了保持本指南的诚实与可辩护性,整个演练都仅限于公开数据:任何人无需登录即可看到的商品标题、价格、评分和评论数。它不涉及用户账户、登录墙后的内容、结账操作或个人数据。文末附近的合法性部分不是套话,请在将其用于生产规模之前阅读它。

为什么要将 Selenium 与代理一起使用

Selenium 是一个浏览器自动化工具。它以编程方式驱动一个真实的浏览器,因此会运行页面的 JavaScript,并看到与真人相同的已渲染 DOM。这解决了问题的渲染那一半:Walmart 在客户端填充其商品详情,而 Selenium 会等待这些元素出现后再去读取它们。Selenium 解决不了的是网络那一半。默认情况下,它从你自己的 IP 发送请求,而 Walmart 会迅速标记数据中心和重复访客的流量,在页面加载完成之前就对其发起挑战或屏蔽。

这正是代理填补的空缺。Crawlbase Smart AI Proxy是一个单一的代理端点,它在服务端将请求轮换到一个住宅 IP 池中。你只需将 Firefox 指向它一次,Selenium 发出的每个请求都会通过一个全新的真实用户地址发出。你从 Selenium 获得渲染能力,从代理获得反屏蔽能力,每个工具都做它真正擅长的那部分。你也可以用一个轮换住宅代理列表自己组装 IP 轮换,但保持这个池健康并正确轮换它,正是 Smart AI Proxy 已经为你完成的大部分工作。

谁负责什么

在脑海里把边界划清楚。Selenium 渲染并读取页面:它运行 JavaScript、等待元素并拉取字段。Smart AI Proxy 负责网络:它轮换住宅 IP,使请求看起来像真实访客而非机器人。把这些职责弄混,或者干脆跳过代理,是 Walmart 爬虫返回空字段或拦截页面最常见的原因。

你将构建什么

一个小型、可运行的 Python 脚本,它接受一个 Walmart 商品 URL,启动配置为通过 Smart AI Proxy 路由的无头 Firefox,等待标题和价格渲染,提取公开字段,在超时时重试,并打印结构化结果。你可以原样运行每个代码片段;只需换上你自己的访问 token 和商品 URL。

设置 Firefox、Python 和 geckodriver

Selenium 在你的机器上需要三样东西:一个要驱动的浏览器、Python 绑定,以及连接它们的驱动程序。对 Firefox 来说,那个驱动程序就是 geckodriver。

如果你还没有 Mozilla Firefox,请先从官方网站安装它。然后确认你拥有 Python 3.8 或更高版本

bash
python --version

接下来下载 geckodriver。它是 Selenium 与 Firefox 之间的桥梁:前往 GitHub 上的 geckodriver 发布页面,下载适合你操作系统的版本,并将其解压到一个你能引用的位置。记下这个路径,因为脚本会用到它。如果 geckodriver 在你的 PATH 上,现代 Selenium 通常能自动找到它,但传入一个显式路径才是可靠的默认做法,也是本指南采用的方式。

现在创建一个虚拟环境,让项目依赖保持隔离,然后安装这些库。

bash
python -m venv walmart_env
source walmart_env/bin/activate

pip install selenium random-user-agent

在 Windows 上,请用 walmart_env\Scripts\activate 而非那行 source 命令来激活环境。两个依赖完成实际工作:selenium 驱动 Firefox,而 random-user-agent 生成逼真的 user-agent 字符串,让每个会话看起来都略有不同。user agent 是一个小细节;在保持不被屏蔽这件事上,代理才是重头戏。

获取你的 Smart AI Proxy 端点

创建一个 Crawlbase 账户并打开仪表盘,找到你的 Smart AI Proxy 访问 token。代理是一个你将 Firefox 指向的单一端点,其形式如下。

bash
http://[email protected]:8012

主机是 smartproxy.crawlbase.com,端口是 8012,你的 token 放在 @ 之前的用户位置。Firefox 通过这个端点发送的每个请求都会获得一个轮换的住宅 IP,因此你无需自己管理代理列表。免费套餐足以在你决定购买某个方案之前,针对一个公开页面跑完整个教程。

不要把你的 token 放进源代码管理

为了可读性,下面的示例把 token 内联在代码里,但在真实代码中,请从环境变量或 .env 文件加载它,而不要提交它。泄露的代理 token 就是泄露的凭据,任何拿到它的人都能消耗你的配额。

配置无头 Firefox 使用 Smart AI Proxy

这是整个设置的核心:构建一个 Firefox 选项对象,使其以无头方式运行,携带一个随机 user agent,并将每个请求都路由通过 Smart AI Proxy。Firefox 以浏览器首选项的形式接受代理设置,因此你要将代理类型设为手动,并将每个协议都指向代理主机和端口。

python
import selenium.webdriver as webdriver
from selenium.webdriver.firefox.service import Service
from selenium.webdriver.firefox.options import Options
from random_user_agent.user_agent import UserAgent
from random_user_agent.params import SoftwareName, OperatingSystem

user_agent_rotator = UserAgent(
    software_names=[SoftwareName.FIREFOX.value],
    operating_systems=[OperatingSystem.WINDOWS.value, OperatingSystem.LINUX.value],
    limit=100,
)
user_agent = user_agent_rotator.get_random_user_agent()

firefox_options = Options()
firefox_options.add_argument("--headless")
firefox_options.add_argument("--no-sandbox")
firefox_options.add_argument("--window-size=1420,1080")
firefox_options.add_argument("--disable-gpu")
firefox_options.add_argument(f"user-agent={user_agent}")

proxy_host = "http://[email protected]"
proxy_port = 8012

firefox_options.set_preference("network.proxy.type", 1)
firefox_options.set_preference("network.proxy.http", proxy_host)
firefox_options.set_preference("network.proxy.http_port", proxy_port)
firefox_options.set_preference("network.proxy.ssl", proxy_host)
firefox_options.set_preference("network.proxy.ssl_port", proxy_port)
firefox_options.set_preference("network.http.use-cache", False)

--headless 标志让 Firefox 在没有可见窗口的情况下运行,这正是你在服务器上想要的,也能让资源占用保持在低位。network.proxy.type 设为 1 意味着"手动代理配置",随后的几行将 HTTP 和 HTTPS(SSL)流量路由通过 Smart AI Proxy 的主机和端口。禁用缓存可确保每次运行都抓取一个全新页面,而不是返回过期内容。这套设置的旧版本还配置了 FTP 和 SOCKS 首选项,但 Walmart 商品页面就是普通的 HTTPS,所以那些是你可以丢掉的噪声。

验证代理是否正常工作

在你把任何东西指向 Walmart 之前,先确认流量确实是经由代理离开的。最简单的检查就是访问一个会回显请求方 IP 的服务。加载 httpbin.org/ip 并打印响应体:如果渲染和路由都正常工作,你看到的会是代理的某个住宅地址,而不是你自己的。

python
import os
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC

driver_path = os.path.join(os.getcwd(), "drivers", "geckodriver")
service = Service(driver_path)
driver = webdriver.Firefox(service=service, options=firefox_options)

driver.get("https://httpbin.org/ip")

try:
    WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.TAG_NAME, "body"))
    )
    print(driver.find_element(By.TAG_NAME, "body").text)
finally:
    driver.quit()

这里 WebDriverWaitpresence_of_element_located 搭配,会一直等到页面的 body 元素出现,最多等待十秒,这样你就不会在 DOM 存在之前去读取它。finally 块总是会关闭浏览器会话,即使等待超时也是如此,这能防止散落的 Firefox 进程越积越多。一次成功的运行会打印出类似下面的内容。

json
{
  "origin": "51.15.242.202"
}

如果你看到一个不是你自己的、看起来像住宅 IP 的地址,说明路由正常,你已准备好把它指向一个真实目标。如果你看到的是你自己的地址,说明代理首选项没有生效,那么在继续之前请重新检查主机、端口和 token。

Crawlbase Walmart Scraper

Selenium 渲染页面,但它不会隐藏你的 IP。Smart AI Proxy 以一个即插即用的单一端点弥合了这个空缺:把 Firefox 指向它一次,每个请求就会在服务端轮换住宅 IP,于是 Walmart 会把你的爬虫读作真实访客而非机器人。没有代理列表要管理,没有轮换逻辑要编写。先在免费套餐上把它指向一个公开商品页面。

理解 Walmart 商品页面

要从 Walmart 商品页面拉取字段,你需要知道它们在已渲染 DOM 中的位置。找到当前选择器最干净的办法,是在浏览器中打开一个商品页面,右键点击你想要的值,选择"检查"。本指南提取的字段以及它们合理的选择器列在下方。

  • 商品标题 页面上最显眼的元素,一个带有 itemprop="name" 属性的 h1,因此 XPath //h1[@itemprop="name"] 能定位到它。
  • 商品价格 渲染在购买框内部,通常位于一个标记为 itemprop="price" 的元素中,可用 //span[@itemprop="price"] 访问。
  • 评分 平均星级评分,通常暴露在一个 aria label 或标题附近一个专门的评分元素中。
  • 评论数 客户评论的数量,通常是评分旁边的一个链接或 span。
选择器会漂移

Walmart 会不经通知就更改其标记和属性名称,因此把上面的选择器当作一个起始模板,而不是一份契约。当提取返回空字符串时,请在浏览器的开发者工具里重新检查实时页面并更新选择器。对任何生产爬虫来说,这都是正常的维护,而不是哪里坏了的信号。

提取商品字段

在路由已验证、选择器在手之后,编写一个函数,让它导航到一个商品 URL,等待标题和价格渲染,并读取字段。一个重试循环包裹了整个过程,这样单次超时或瞬时屏蔽就不会终止整个运行;它会用一个全新的浏览器会话再试一次,最多到一个可配置的上限。

python
from selenium.common.exceptions import TimeoutException
from time import sleep

TITLE_XPATH = '//h1[@itemprop="name"]'
PRICE_XPATH = '//span[@itemprop="price"]'

def scrape_walmart_product(url, max_retries=3, retry_delay=5):
    for attempt in range(1, max_retries + 1):
        driver = webdriver.Firefox(service=service, options=firefox_options)
        try:
            driver.get(url)

            WebDriverWait(driver, 15).until(
                EC.presence_of_element_located((By.XPATH, TITLE_XPATH))
            )

            title = driver.find_element(By.XPATH, TITLE_XPATH).text.strip()
            price = read_optional(driver, PRICE_XPATH)

            return {"url": url, "title": title, "price": price}
        except TimeoutException:
            print(f"Timeout on attempt {attempt} for {url}")
        except Exception as error:
            print(f"Error on attempt {attempt}: {error}")
        finally:
            driver.quit()

        if attempt < max_retries:
            print(f"Retrying in {retry_delay}s...")
            sleep(retry_delay)

    return None

def read_optional(driver, xpath):
    try:
        return driver.find_element(By.XPATH, xpath).text.strip()
    except Exception:
        return None

几个决策让它变得健壮。脚本在读取任何东西之前先等待标题,因为标题在每个商品页面上都可靠地存在,标志着页面已经渲染。价格、评分和评论数通过一个小小的 read_optional 辅助函数读取,当某个元素缺失时它返回 None 而不是抛出异常,因为并非每件商品都带有每个字段。而且每次尝试都创建并退出它自己的浏览器,所以一次重试是从一个干净的会话和一个全新的代理 IP 开始,而不是复用一个已被污染的会话。

完整脚本

这里是把所有东西接线在一起的一个可运行文件。填入你的访问 token,设置 geckodriver 路径,更改商品 URL,然后运行它。

python
import os
import json
from time import sleep
import selenium.webdriver as webdriver
from selenium.webdriver.firefox.service import Service
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
from random_user_agent.user_agent import UserAgent
from random_user_agent.params import SoftwareName, OperatingSystem

ACCESS_TOKEN = os.getenv("CRAWLBASE_PROXY_TOKEN", "YOUR_ACCESS_TOKEN")
PROXY_HOST = f"http://{ACCESS_TOKEN}@smartproxy.crawlbase.com"
PROXY_PORT = 8012

TITLE_XPATH = '//h1[@itemprop="name"]'
PRICE_XPATH = '//span[@itemprop="price"]'


def build_options():
    rotator = UserAgent(
        software_names=[SoftwareName.FIREFOX.value],
        operating_systems=[OperatingSystem.WINDOWS.value, OperatingSystem.LINUX.value],
        limit=100,
    )
    user_agent = rotator.get_random_user_agent()

    options = Options()
    options.add_argument("--headless")
    options.add_argument("--no-sandbox")
    options.add_argument("--window-size=1420,1080")
    options.add_argument("--disable-gpu")
    options.add_argument(f"user-agent={user_agent}")

    options.set_preference("network.proxy.type", 1)
    options.set_preference("network.proxy.http", PROXY_HOST)
    options.set_preference("network.proxy.http_port", PROXY_PORT)
    options.set_preference("network.proxy.ssl", PROXY_HOST)
    options.set_preference("network.proxy.ssl_port", PROXY_PORT)
    options.set_preference("network.http.use-cache", False)
    return options


def read_optional(driver, xpath):
    try:
        return driver.find_element(By.XPATH, xpath).text.strip()
    except Exception:
        return None


def scrape_walmart_product(url, service, options, max_retries=3, retry_delay=5):
    for attempt in range(1, max_retries + 1):
        driver = webdriver.Firefox(service=service, options=options)
        try:
            driver.get(url)
            WebDriverWait(driver, 15).until(
                EC.presence_of_element_located((By.XPATH, TITLE_XPATH))
            )
            return {
                "url": url,
                "title": driver.find_element(By.XPATH, TITLE_XPATH).text.strip(),
                "price": read_optional(driver, PRICE_XPATH),
            }
        except TimeoutException:
            print(f"Timeout on attempt {attempt} for {url}")
        except Exception as error:
            print(f"Error on attempt {attempt}: {error}")
        finally:
            driver.quit()

        if attempt < max_retries:
            print(f"Retrying in {retry_delay}s...")
            sleep(retry_delay)

    return None


def main():
    driver_path = os.path.join(os.getcwd(), "drivers", "geckodriver")
    service = Service(driver_path)
    options = build_options()

    product_url = "https://www.walmart.com/ip/Ozark-Trail-Basic-Mesh-Chair-Blue-Adult/577309300"
    result = scrape_walmart_product(product_url, service, options)

    if result:
        with open("walmart_product.json", "w") as f:
            json.dump(result, f, indent=2)
        print(json.dumps(result, indent=2))
    else:
        print("Could not scrape the product after all retries.")


if __name__ == "__main__":
    main()

输出是什么样子

python walmart_scraper.py 运行它,你会得到写入 walmart_product.json 并回显到控制台的干净结构化数据。

json
{
  "url": "https://www.walmart.com/ip/Ozark-Trail-Basic-Mesh-Chair-Blue-Adult/577309300",
  "title": "Ozark Trail Basic Mesh Chair, Blue, Adult",
  "price": "$12.98"
}

一旦你在实时页面上检查过评分和评论数选择器,就用同样的方式把它们加到 result 字典里,每次运行都会捕获该商品的完整公开字段集。要把它变成一个价格监控任务,把一个商品 URL 列表循环传给 scrape_walmart_product,并在写入文件之前把每个结果追加到一个列表中。

在规模化时保持不被屏蔽

Smart AI Proxy 替你处理 IP 轮换,但一些习惯能让更大的运行保持健康,而且它们适用于任何高难度的商业目标。

  • 控制请求节奏。在一个紧密循环里猛击 Walmart 是被限流最快的方式。把请求拉开间隔,并改变你访问的商品,而不是反复循环同一个 URL。
  • 依靠轮换。Smart AI Proxy 把你的流量分散到许多真实用户 IP 上,因此没有任何单一地址会触发限流。这正是你原本要自己构建和维护的部分。
  • 读懂状态码。一次开始返回挑战或空白页面的运行,是在告诉你当前速率太高了。把代理状态错误码当作信号,而非噪声,看到它们时就退避。

关于更宏观的方法论,请看如何在不被屏蔽的情况下抓取网站。如果你宁愿完全跳过无头浏览器,让一个 API 返回已解析的商品数据,请把这个方案与 Crawling API 比较,它为受支持的站点返回预解析的 JSON,或者与 Crawling API 比较,它返回已渲染的 HTML 而无需你自己运行 Selenium。同样的 Selenium 模式在其他零售商上也适用;按 ASIN 抓取 Amazon 演练了一个密切相关的任务。

抓取 Walmart 合法吗?

抓取一个大型商业零售商处于法律的灰色地带,"是否被允许"这个问题的答案取决于 Walmart 的服务条款、你所在的司法管辖区,以及你如何使用这些数据。Walmart 的条款限制自动化访问,因此无论你的工具有多谨慎,抓取都可能违反这些条款。这里的代码都不会改变这一点;它只是让技术部分跑起来。

有几条值得坚守的原则。只收集公开数据:任何人无需账户即可看到的标题、价格、评分和评论数。尊重 Walmart 的 robots.txt 及其声明的速率预期,并把你的请求量保持得足够低,以免给任何人的服务器造成压力。如果你打算在商业上重用这些数据,请获得许可或一份官方数据协议,而不是假设沉默就等于同意。并且永远不要收集个人数据,包括任何与个人客户账户绑定的内容或可归因于真实人物的评论。

本指南刻意只聚焦于公开的商品数据,因为那是让这项工作保持可辩护的那条界线。它不涉及任何登录后的内容、账户或订单数据、支付或结账操作,也不涉及任何绕过身份验证或与账户绑定的 CAPTCHA 的尝试。如果你的项目需要的不止公开商品字段,正确的做法是使用 Walmart 的官方 API 或与其签订数据协议,而不是写一个更聪明的爬虫。

回顾

核心要点

  • 拆分任务。Selenium 渲染并读取页面;Smart AI Proxy 负责网络。每个工具只做一部分,正是这种分离让爬虫变得可靠。
  • 让 Firefox 通过代理。network.proxy.type 设为手动,并用你的 token 把 HTTP 和 SSL 指向 smartproxy.crawlbase.com:8012,然后在抓取前用 httpbin.org/ip 验证。
  • 先等待,再读取。在提取之前对标题使用 WebDriverWait,并通过一个在元素缺失时返回 None 的辅助函数读取可选字段。
  • 预期选择器会漂移。Walmart 会不经通知就更改其标记,因此当提取返回空字符串时,重新检查并更新 XPath。
  • 只停留在公开数据上。尊重 Walmart 的 ToS 和 robots.txt;不碰账户、不碰个人数据、不做结账或绕过身份验证的操作。

常见问题

为什么对 Walmart 商品页面的普通请求返回不了数据?

有两件事在跟一个裸 HTTP 请求作对。第一,Walmart 在客户端渲染了它的大部分商品详情,所以初始 HTML 只是一个空壳,只有在页面的 JavaScript 在真实浏览器中运行后才会填充进来。第二,Walmart 会迅速标记自动化流量并对其发起挑战或屏蔽。Selenium 通过驱动一个真实的 Firefox 来解决渲染问题,而把那个浏览器路由通过 Smart AI Proxy 给了你一个站点会读作真实访客的 IP。

用 Selenium 抓取 Walmart 需要代理吗?

对于超出单次测试请求的任何用途,需要。Selenium 渲染页面,但它默认从你自己的 IP 发送请求,而 Walmart 会迅速限流或屏蔽重复的自动化流量。把 Firefox 路由通过 Smart AI Proxy 会在服务端把你的请求轮换到住宅 IP 上,因此没有任何单一地址会触发限流。这就是一个只能跑通一次的演示和一个能持续工作的爬虫之间的区别。

在 Selenium 中如何把 Firefox 指向 Smart AI Proxy?

把 Firefox 首选项 network.proxy.type 设为 1 以启用手动配置,然后把 network.proxy.httpnetwork.proxy.ssl 设为 http://[email protected],并把对应的 _port 首选项设为 8012。在你创建 driver 时传入这些选项,Firefox 发出的每个请求就都会经由代理出去。通过加载 httpbin.org/ip 并检查返回的地址不是你自己的,来验证它。

我的 XPath 选择器返回空字符串。变了什么?

几乎可以肯定是 Walmart 的标记。它的属性名称和类结构会不经通知就更改,所以上个月还能用的选择器现在可能失效。在浏览器的开发者工具里重新检查一个实时商品页面,找到你想要字段的当前属性或元素,然后更新 XPath。定期维护选择器对任何生产爬虫来说都很正常,并不是这套方法坏了的信号。

我应该用 Selenium 还是 API 来抓取 Walmart?

当你想要对一个真实浏览器有完全的控制、需要与页面交互,或者正在学习渲染和代理如何配合时,使用 Selenium。如果你宁愿跳过无头浏览器,Scraper API 为受支持的站点返回预解析的商品 JSON,而 Crawling API 在一次调用中返回已渲染的 HTML,无需你自己运行一支浏览器舰队。对于一次性或不寻常的布局,本指南中的 Selenium 方案是灵活的选项。

抓取 Walmart 合法吗?

这取决于 Walmart 的服务条款、你所在的司法管辖区以及你的用途,而它们的条款限制自动化访问。严格只停留在公开的商品数据上,尊重 robots.txt 和速率预期,永远不要触碰账户、个人数据,或结账与身份验证流程。对于商业重用,请获得许可或一份官方数据协议,而不是依赖一个爬虫。

开始构建

大规模爬取任何站点,无需与基础设施对抗。

Crawlbase 负责处理代理、指纹和 CAPTCHA,让你的团队专注于交付数据流水线,而非维护爬取管道。1,000 次请求免费,无需信用卡。

自助开通 · 无需销售通话 · 提供企业级爬取量