Shein 是网络上最大的快时尚零售商之一,拥有一个不断翻新的庞大目录,涵盖男装、女装、配饰和家居用品。每个分类页和搜索页都是一个公开的产品网格:标题、价格、星级评分,以及指向产品自有页面的链接,任何无需账户浏览的人都能看到。正是那些公开的房源数据,被代发货商、价格追踪者和市场研究者盯着,用来发现优惠、跟进定价并评估什么正在流行。

本指南将向你展示如何用 Python 抓取 Shein 房源。你会构建一个小巧、可直接运行的抓取脚本,它通过 Crawling API 获取一个 Shein 分类页或搜索页,为每个产品解析一条干净的记录,处理分页,并把结果导出为 JSON 和 CSV。整个演示始终限定在公开房源数据范围内:任何人无需登录就能在 Shein 房源页上看到的产品名称、价格、评分和链接。

你将构建什么

一个 Python 脚本:它接收一个 Shein 分类页或搜索 URL,通过 Crawling API 取回渲染后的页面,并为网格上的每个产品提取一条结构化记录。我们以一个女装分类页作为贯穿全文的示例。抓取器从每张产品卡片抓取以下字段:

  • 标题 房源卡片上显示的产品名称。
  • 价格 当卡片显示时的当前销售价。
  • 评分 卡片上展示的平均星级评分。
  • 链接 指向产品自有详情页的绝对 URL。
  • 图片 来自卡片缩略图的产品图片 URL。

这些直接映射到旧版演示所采集的字段(产品名称、定价、评分和图片 URL),并精简到一个房源网格实际暴露的内容。像 SKU、完整描述、颜色和尺码这样的单品详情住在每个产品的自有页面上,你通过跟进 link 字段并以同样的方式解析那个页面来抵达它。

为什么普通请求在 Shein 上会失败

如果你把一个简单的 HTTP 客户端指向一个 Shein 分类 URL,你很少能拿到你想要的产品网格。有两点对你不利。首先,Shein 在客户端渲染它的房源:服务器交付一个轻量的空壳,并在页面的 JavaScript 运行以及你滚动时逐渐填入产品卡片,所以最初的 HTML 通常缺失大部分产品。一个只读取第一个 HTTP 响应、没有浏览器执行脚本的库,看到的是一个几乎空白的页面。

其次,Shein 会标记自动化流量。数据中心 IP 段以及看起来不像真实购物者的请求模式,在你触及网格之前就会遭遇一个质询页面、一次区域重定向,或一次彻底的封禁。所以一个能正常工作的 Shein 抓取器在一次请求中需要两样东西:一个渲染页面的浏览器,以及一个被 Shein 读作真实访客的 IP。你可以自己用无头浏览器和一池轮换住宅代理把它拼起来,但保持那套技术栈的健康才是大部分工作量所在。Crawling API 把两者折叠进单次调用:你把房源 URL 发给它,它在一个可信住宅 IP 背后渲染页面,处理轮换和任何质询,并把处理完的 HTML 返回给你解析。

前置条件

在写任何代码之前,你需要准备好几样东西。它们都花不了多少时间。

Python 基础。你应当能够自如地编写并运行 Python 脚本,以及用 pip 安装软件包。如果你刚接触这门语言,官方 Python 文档或任何入门课程都涵盖了本教程默认的水平,而我们关于在 Python 中使用 BeautifulSoup 的指南深入讲解了这个解析库。

Python 3.8 或更高版本。python --version(或 python3 --version)确认你的版本。如果你还没有,请从 python.org 安装,并确保 Python 在你系统的 PATH 上。

一个 Crawlbase 账户和 token。注册一个免费账户,打开你的仪表盘,并从账户文档页复制你的 token。免费档位包含 1,000 次请求且无需信用卡,足够用来构建并测试这个抓取器。Shein 需要 JavaScript token,因为它的房源是客户端渲染的。把这个 token 当成密码对待,并不要把它纳入版本控制。

搭建项目

创建一个虚拟环境,让项目依赖保持隔离,然后安装抓取器所需的两个库。crawlbase 是 Crawling API 的官方客户端,而 beautifulsoup4 解析返回的 HTML,让你能用 CSS 选择器从产品卡片中逐个抽取字段。

bash
python --version

python -m venv shein_env
source shein_env/bin/activate

pip install crawlbase beautifulsoup4

在 Windows 上,用 shein_env\Scripts\activate 激活环境,而不是那一行 source。两个库都装好后,创建本指南后续逐步搭建的脚本文件:

bash
touch shein_listing.py

理解房源页面

一个 Shein 房源位于一个分类 URL,比如 https://www.shein.com/Women-Clothing-c-2030.html,或位于一个搜索 URL,比如 https://www.shein.com/pdsearch/dress/。两者都布局出一个产品卡片网格,每个产品一张,每张都携带同样的少数几个字段:一张缩略图、一个标题、一个价格、一个可选的星级评分,以及一个指向产品详情页的链接。

在写选择器之前,在你的浏览器里打开一个房源页面,右键点击一张产品卡片,选择 Inspect。Shein 把每个产品包裹在一个 class 含有 product-card 的 section 中,把标题放在一个链接或名称元素里,在一个专门的价格元素里显示价格,并在那个容器内暴露缩略图和产品链接。这些就是你要瞄准的元素。Shein 的确切类名会随时间变化,所以在你能做到的地方,依靠像 product-card 子串这样的部分匹配,而不是一条脆弱的完整类名链。

第 1 步:获取渲染后的房源页

先从拿到处理完的页面开始。导入 CrawlingAPI 类,用你的 token 初始化它,设置房源 URL,并在开启渲染的情况下请求它。在解析之前检查状态码,能让失败显式暴露而不是悄无声息。

python
from crawlbase import CrawlingAPI

api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"})

def crawl(page_url):
    options = {"ajax_wait": "true", "page_wait": 5000}
    response = api.get(page_url, options)
    if response["status_code"] == 200:
        return response["body"].decode("utf-8", "ignore")
    print(f"Request failed: {response['status_code']}")
    return None

if __name__ == "__main__":
    listing_url = "https://www.shein.com/Women-Clothing-c-2030.html"
    html = crawl(listing_url)
    print(html[:500] if html else "No HTML returned")

对于一个随页面加载逐渐填入的网格,两个等待选项很关键。ajax_wait 告诉 API 等待异步内容完成,而 page_wait 在加载后固定等待若干毫秒,让那些延迟渲染的卡片在页面被捕获前出现。运行脚本,你应当看到真实的产品标记,而不是一个近乎空白的空壳。这能在你写下任何一个选择器之前确认渲染是有效的。

Crawlbase Crawling API

那个产品网格只有在页面的 JavaScript 运行后才会出现,而且只有当 Shein 把请求读作一个真实购物者时才会。Crawling API 接收你的 token,在真实浏览器中运行房源页,在服务端轮换住宅 IP,并处理任何质询,然后交给你处理完的 HTML。你省去了自己运行无头浏览器集群和代理池的麻烦。先在免费的 1,000 次请求档位上把它指向一个分类页或搜索 URL 吧。

第 2 步:用 BeautifulSoup 解析产品卡片

拿到渲染后的 HTML 后,把它加载进 BeautifulSoup,找到每张产品卡片,并按选择器抽取每个字段。Shein 把每个产品包裹在一个你据以选择的卡片容器里,把标题放在一个名称链接中,在一个价格元素里显示价格,并在卡片内暴露缩略图和产品链接。把每张卡片包在一个 try/except 中,这样一个格式异常的房源不会让整次运行崩溃。

python
from bs4 import BeautifulSoup

BASE = "https://www.shein.com"

def text_of(card, selector):
    el = card.select_one(selector)
    return el.get_text(strip=True) if el else None

def absolute(href):
    if not href:
        return None
    if href.startswith("//"):
        return "https:" + href
    return href if href.startswith("http") else BASE + href

def parse_link(card):
    a = card.select_one("a[href]")
    return absolute(a["href"]) if a else None

def parse_image(card):
    img = card.select_one("img")
    if not img:
        return None
    src = img.get("src") or img.get("data-src")
    return absolute(src)

def scrape_listing(html):
    soup = BeautifulSoup(html, "html.parser")
    cards = soup.select("section[class*='product-card']")
    results = []
    for card in cards:
        try:
            title = text_of(card, "a.goods-title-link")
            if not title:
                continue
            results.append({
                "title": title,
                "price": text_of(card, "span.product-card__price"),
                "rating": text_of(card, "span.rate-num"),
                "link": parse_link(card),
                "image": parse_image(card),
            })
        except Exception as e:
            print(f"Skipped a card: {e}")
    return results

text_of 助手查询卡片内的一个元素,当它缺失时返回 None,而不是对着空对象抛出 .get_text() 调用。这让抽取在某个字段缺失时保持稳健,而这很常见,因为不是每个产品都显示评分。absolute 助手把链接和图片来源规范化为完整 URL,因为 Shein 常常提供一个协议相对的 // 来源或一个相对的 href。跳过任何没有标题的卡片,会丢掉 Shein 在网格里留下的那些空占位槽。

选择器会漂移

Shein 的类名(卡片容器、价格 span、评分元素)会随站点更新而变化,而像 product-card 子串和一个内部锚点这样的结构模式则更耐久。把上面基于类名的选择器当成一个起始模板,而不是一份契约。当某个字段对每张卡片都返回 None 时,在浏览器开发者工具里重新检视实时房源页并更新选择器。对任何生产级抓取器来说,定期维护选择器都是常态。

第 3 步:组装脚本并导出 JSON 和 CSV

现在把获取和解析接进一个可直接运行的脚本,然后把记录写入 JSON 和 CSV 两者,让你可以把它们加载进一个 notebook 或一个电子表格。获取渲染后的房源页,把它交给解析器,并倒出结构化的行。

python
import csv
import json
from crawlbase import CrawlingAPI
from bs4 import BeautifulSoup

api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"})
BASE = "https://www.shein.com"
FIELDS = ["title", "price", "rating", "link", "image"]

def crawl(page_url):
    options = {"ajax_wait": "true", "page_wait": 5000}
    response = api.get(page_url, options)
    if response["status_code"] == 200:
        return response["body"].decode("utf-8", "ignore")
    print(f"Request failed: {response['status_code']}")
    return None

def text_of(card, selector):
    el = card.select_one(selector)
    return el.get_text(strip=True) if el else None

def absolute(href):
    if not href:
        return None
    if href.startswith("//"):
        return "https:" + href
    return href if href.startswith("http") else BASE + href

def parse_link(card):
    a = card.select_one("a[href]")
    return absolute(a["href"]) if a else None

def parse_image(card):
    img = card.select_one("img")
    if not img:
        return None
    src = img.get("src") or img.get("data-src")
    return absolute(src)

def scrape_listing(html):
    soup = BeautifulSoup(html, "html.parser")
    cards = soup.select("section[class*='product-card']")
    results = []
    for card in cards:
        try:
            title = text_of(card, "a.goods-title-link")
            if not title:
                continue
            results.append({
                "title": title,
                "price": text_of(card, "span.product-card__price"),
                "rating": text_of(card, "span.rate-num"),
                "link": parse_link(card),
                "image": parse_image(card),
            })
        except Exception as e:
            print(f"Skipped a card: {e}")
    return results

def export(rows, name="shein_listing"):
    with open(f"{name}.json", "w", encoding="utf-8") as f:
        json.dump(rows, f, indent=2, ensure_ascii=False)
    with open(f"{name}.csv", "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=FIELDS)
        writer.writeheader()
        writer.writerows(rows)
    print(f"Saved {len(rows)} products to {name}.json and {name}.csv")

def main():
    url = "https://www.shein.com/Women-Clothing-c-2030.html"
    html = crawl(url)
    if not html:
        return
    rows = scrape_listing(html)
    export(rows)

if __name__ == "__main__":
    main()

python shein_listing.py 运行完整脚本。它获取渲染后的房源页,为每个产品解析一行,并写出 shein_listing.jsonshein_listing.csv 两者。共享的 FIELDS 列表让 CSV 列顺序与字典键保持一致,所以两种导出永远不会彼此偏离。

输出长什么样

你会得到一份干净的产品记录列表,按网格顺序排列,可直接写入 JSON、CSV 或数据库。

json
[
  {
    "title": "Women's Ribbed Knit Long Sleeve Bodycon Dress",
    "price": "$12.49",
    "rating": "4.92",
    "link": "https://www.shein.com/Ribbed-Knit-Bodycon-Dress-p-12345678.html",
    "image": "https://img.ltwebstatic.com/images/ribbed-dress.jpg"
  },
  {
    "title": "Floral Print Tie Front Blouse",
    "price": "$9.00",
    "rating": "4.78",
    "link": "https://www.shein.com/Floral-Print-Blouse-p-87654321.html",
    "image": "https://img.ltwebstatic.com/images/floral-blouse.jpg"
  }
]

跨页面和分类进行扩展

一个页面只是演示;真正的研究任务会跑遍整个分类。Shein 用一个 ?page= 查询参数为它的房源分页,所以你按顺序遍历这些页面,直到一个页面返回为空,然后再去下一个分类。给请求设置节奏,让你不至于在一个紧凑的循环里猛打 Shein。

python
import time

CATEGORIES = {
    "women-clothing": "https://www.shein.com/Women-Clothing-c-2030.html",
    "dresses": "https://www.shein.com/Dresses-c-1727.html",
}

def scrape_categories(categories, max_pages=5):
    everything = {}
    for name, base_url in categories.items():
        rows = []
        for page in range(1, max_pages + 1):
            sep = "&" if "?" in base_url else "?"
            page_url = f"{base_url}{sep}page={page}"
            html = crawl(page_url)
            if not html:
                break
            found = scrape_listing(html)
            if not found:
                break
            rows.extend(found)
            print(f"{name} page {page}: {len(found)} products")
            time.sleep(2)
        everything[name] = rows
    return everything

空结果的 break 在一个分类的产品用尽时让你提早停下,max_pages 上限让一次运行保持有界,而请求之间的 time.sleep(2) 给运行设置节奏,让你不至于因密集流量而被标记。按分类名给输出加键,让每个列表保持分开,这在跨分类对比价格时正是你想要的。要随时间追踪价格变化,按计划运行这个任务,给每次导出打上日期戳,并对相继的快照做差分。这会直接喂进一个价格情报工作流,在那里你在这里采集的房源字段成为原始信号。

保持不被封禁

即便处理好了渲染,Shein 仍会监视具有抓取器特征的流量。几个习惯能让一次运行保持健康,且它们适用于任何难啃的商业目标。

  • 给请求设置节奏。在页面和分类之间用一个延迟把请求分散开,而不是全速爬取一切。把较重的任务安排在非高峰时段,以减轻 Shein 服务器的负载。
  • 依靠轮换。一池住宅 IP 把请求分散到许多真实用户地址上,让没有任何单个 IP 触发速率限制。Crawling API 替你处理这件事;如果你自己搭建技术栈,这就是要做对的部分。
  • 只保留你需要的。存储你项目用到的房源字段,丢弃其余的。定期重新检查你的选择器,让抓取器跟上标记的变化。

关于避免被封禁的更广实战手册,请参阅如何在不被封禁的情况下抓取网站,而关于为什么渲染在这里很重要的更多内容,参见如何抓取 JavaScript 网站。关于从在线商店拉取结构化产品数据的更广背景,我们关于电商网页抓取的概述涵盖了这个抓取器遵循的模式。

抓取 Shein 合法吗?

抓取 Shein 是否被允许,取决于 Shein 的服务条款、你所在的司法管辖区,以及你拿数据做什么。Shein 的条款限制自动化访问,所以无论你的工具多么谨慎,抓取都可能违反那些条款。这里的代码不会改变这一点;它只是把技术部分跑通而已。请阅读 Shein 的服务条款及其 robots.txt,并把两者都当成你所采集内容的边界。对于商业或竞争性用途,法律图景会变得更复杂,就你的具体情形咨询一位法律专家是明智之举。

有几条值得守住的底线。只采集公开数据:任何人无需账户就能在一个 Shein 房源页上看到的产品标题、价格、评分、房源链接和缩略图。把你的请求量保持得足够低,让你不至于给 Shein 的服务器造成压力,并避开个人数据,包括任何超出公开列出范围、与可识别购物者或评论者相关的内容。不要把 Shein 的产品摄影当成你自己的来再分发;一个图片 URL 是一个引用,不是一份再发布该媒体的许可。如果你打算商业地再利用这些数据,取得许可或一份官方协议,而不是假定沉默就是同意。

本指南有意限定在公开分类页和搜索房源页,因为那是让这项工作站得住脚的界线。它不涉及登录之后的任何内容、账户或订单数据、个人信息,也不涉及任何绕过认证或一个你无权通过的质询的尝试。如果 Shein 提供一个适合你用例的联盟或合作伙伴数据计划,那才是当你需要大量、有保证的结构或商业权利时的正确工具。如果你的项目需要的不止公开房源数据,一个官方渠道或一份数据协议才是正确的路径,而不是更巧妙的抓取器。

回顾

核心要点

  • 房源是公开的产品信号。每个 Shein 分类页和搜索页都布局出标题、价格、评分和链接,这正是它对价格追踪和产品研究如此有用的原因。
  • 你需要渲染和可信 IP 二者兼具。Shein 在客户端填充产品网格并拦截机器人流量,所以 Crawling API 在一次调用里于一个住宅 IP 背后渲染页面。
  • 由 BeautifulSoup 完成抽取。循环 product-card 容器,把标题、价格、评分、链接和图片映射到当前选择器上,并预期这些选择器会漂移。
  • 导出为 JSON 和 CSV。一个共享的字段列表让两个文件保持同步,而用 ?page= 分页直到一个页面为空,可把运行扩展到整个分类。
  • 守在公开数据上。尊重 Shein 的服务条款和 robots.txt,不要再发布产品图像,并且永远不要触碰账户、订单或个人信息。

常见问题

为什么普通请求从 Shein 返回不到产品?

Shein 在客户端渲染它的房源,所以一个原始的 HTTP 请求得到一个里面没有产品卡片的近乎空白的空壳。除此之外,Shein 还会对看起来不像真实浏览器的流量发起质询或封禁。通过 Crawling API 在一个可信 IP 背后渲染页面同时解决了这两点,这就是为什么这里的抓取器把它的请求经由它来路由,而不是直接获取该 URL。

我该如何抓取一个特定的 Shein 分类或搜索?

把抓取器指向分类 URL,例如 /Women-Clothing-c-2030.html,或一个搜索 URL,比如 /pdsearch/dress/。两者都渲染出同样的产品卡片网格,所以同样的选择器可以沿用。要覆盖多个分类,保留一个名称到 URL 的映射并在其上循环,在页面之间用一个短延迟给请求设置节奏。

我能拿到每个产品的 SKU、颜色和尺码吗?

那些字段住在每个产品的自有详情页上,而不是房源网格上。先从房源采集 link 字段,然后通过同一个 crawl 函数获取每个产品页,并从那个页面解析 SKU、完整描述、颜色样本和尺码选项。旧版字段映射到一次两阶段抓取:房源用于发现,详情页用于深度。

我该如何处理 Shein 上的分页?

Shein 用一个 ?page= 查询参数为房源分页。按顺序遍历这些页面,在一个页面返回不到产品时停止,并用一个最大页数上限给循环封顶,让一次运行保持有界。上面的 scrape_categories 助手正是这么做的,而用一个短暂的 sleep 给每次请求设置节奏,能让运行不至于看起来像密集流量。

我该导出为 JSON 还是 CSV?

两者都导,而脚本会从同一份记录写出两者。JSON 保留嵌套结构,并且很容易加载回 Python,而 CSV 直接在电子表格里打开,便于快速筛选和制图。共享的 FIELDS 列表让 CSV 列与字典键保持同一顺序,所以两个文件永远不会彼此偏离。

抓取 Shein 时我该如何避免被封禁?

把你的单 IP 请求速率保持得低,在页面和分类之间加一个延迟,并经由轮换住宅 IP 路由,让没有任何单个地址触发速率限制。Crawling API 替你管理轮换、一个可信 IP 池和质询处理;如果你搭建自己的技术栈,那就是要投入精力的部分。盯住状态码,并在开始看到质询时退一步。

开始构建

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

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

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