StockX 是一个买卖正品运动鞋、街头服饰、手表和名牌手袋的在线市场。StockX 最有趣的部分是它将服装项目视为一种商品,并随着时间的推移跟踪它们的价值。这使得 StockX 成为网络抓取的主要目标,因为跟踪市场动向和产品数据是建立数据驱动业务的好方法。 在本网络抓取教程中,我们将了解如何使用 Python 抓取 StockX。我们将抓取 StockX 的隐藏网络数据,这是一种非常简单的方式,只需几行代码即可抓取电子商务网站。 我们将从快速的 Python 环境设置和工具概述开始,然后抓取一些产品和产品搜索页面。让我们开始吧!
为什么要抓取 StockX?
就像大多数电子商务目标一样,StockX 公共数据提供了重要的市场概览。因此,抓取 StockX 是通过数据驱动的决策制定获得竞争优势的好方法。 此外,由于 StockX 通过抓取数据将其产品视为商品,因此我们可以执行各种数据分析任务,以在市场趋势中保持领先地位,并超越我们的竞争对手。
StockX 数据集预览
由于我们将使用隐藏的网络数据抓取,我们将提取包含以下字段的整个产品数据集:
- 产品数据,如描述、图片、尺寸、ID、特征以及页面上可见的所有内容。
- 产品市场表现,如销售数量、价格、询价和出价。
要将这些 json 数据集解析为更小的内容,请参阅我们的JMESPath和JSONPath工具介绍。
项目设置
在这个网络抓取教程中,我们将使用 Python 和三个流行的库:
- httpx – HTTP 客户端库,我们将使用它来检索 StockX 的网页。
- parsel – HTML 解析库,我们将使用它来查找
<script>
包含隐藏 Web 数据的元素。 - nested_lookup – 允许通过键从字典中提取嵌套值。由于 StockX 的数据集庞大且嵌套,这个库将帮助我们快速可靠地找到产品数据。
这些包可以通过pip install
命令轻松安装:
$ pip install httpx parsel nested_lookup
或者,可以随意换成httpx
任何其他 HTTP 客户端包,例如requests,因为我们只需要基本的 HTTP 功能,这些功能几乎可以在每个库中互换。至于,parsel
另一个很好的选择是beautifulsoup包。 接下来,我们先来看看如何抓取StockX的单品数据。
抓取 StockX 产品数据
为了抓取单个产品数据,我们将使用隐藏的网络数据技术。 StockX 由 React 和 Next.js 技术提供支持,因此我们将寻找元素中的隐藏数据<script>
。特别是,隐藏的 Web 数据通常在以下两个位置之一可用:
<script id="__NEXT_DATA__" type="application/json">{...}</script> <!-- or --> <script data-name="query">window.__REACT_QUERY_STATE__ = {...};</script>
要为这些隐藏的数据集解析此 HTML,我们可以使用XPath或CSS 选择器:
import json from parsel import Selector def parse_hidden_Data(html: str) -> dict: """extract nextjs cache from page""" selector = Selector(html) data = selector.css("script#__NEXT_DATA__::text").get() if not data: data = selector.css("script[data-name=query]::text").get() data = data.split("=", 1)[-1].strip().strip(';') data = json.loads(data) return data
在这里,我们正在构建parsel.Selector
并查找<script>
基于 CSS 选择器的元素。 接下来,让我们添加 HTTP 功能来完成我们的产品抓取工具,让我们试一试:
import asyncio import json import httpx from nested_lookup import nested_lookup from parsel import Selector # create HTTPX client with headers that resemble a web browser client = httpx.AsyncClient( http2=True, follow_redirects=True, headers={ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "Accept-Encoding": "gzip, deflate, br", "Accept-Language": "en-US,en;q=0.9", "Cache-Control": "no-cache", }, ) def parse_nextjs(html: str) -> dict: """extract nextjs cache from page""" selector = Selector(html) data = selector.css("script#__NEXT_DATA__::text").get() if not data: data = selector.css("script[data-name=query]::text").get() data = data.split("=", 1)[-1].strip().strip(";") data = json.loads(data) return data async def scrape_product(url: str) -> dict: """scrape a single stockx product page for product data""" response = await client.get(url) assert response.status_code == 200 data = parse_nextjs(response.text) # extract all products datasets from page cache products = nested_lookup("product", data) # find the current product dataset try: product = next(p for p in products if p.get("urlKey") in str(response.url)) except StopIteration: raise ValueError("Could not find product dataset in page cache", response) return product # example use: url = "https://stockx.com/nike-air-max-90-se-running-club" print(asyncio.run(scrape_product(url)))
在上面,仅用几行代码,我们就抓取了 StockX 网站上可用的整个产品数据集。 现在我们可以抓取单个项目,让我们看看如何在 StockX 上查找产品以抓取所有数据或仅选择类别。
刮 Stockx 搜索
要发现产品,我们有两种选择:站点地图和搜索。 站点地图是发现所有产品的理想选择,通常可以通过检查 URL 找到它们/robots.txt
。例如,StockX 的 robots.txt 表示:
Sitemap: https://stockx.com/sitemap/sitemap-index.xml Sitemap: https://stockx.com/it-it/sitemap/sitemap-index.xml Sitemap: https://stockx.com/de-de/sitemap/sitemap-index.xml Sitemap: https://stockx.com/fr-fr/sitemap/sitemap-index.xml Sitemap: https://stockx.com/ja-jp/sitemap/sitemap-index.xml Sitemap: https://stockx.com/zh-cn/sitemap/sitemap-index.xml Sitemap: https://stockx.com/en-gb/sitemap/sitemap-index.xml Sitemap: https://stockx.com/ko-kr/sitemap/sitemap-index.xml Sitemap: https://stockx.com/es-es/sitemap/sitemap-index.xml Sitemap: https://stockx.com/es-mx/sitemap/sitemap-index.xml Sitemap: https://stockx.com/es-us/sitemap/sitemap-index.xml Sitemap: https://stockx.com/zh-tw/sitemap/sitemap-index.xml Sitemap: https://stockx.com/fr-ca/sitemap/sitemap-index.xml
所以我们可以抓取/sitemap/sitemap-index.xml
每个产品 URL 所在的位置。但是,如果我们想缩小我们的范围并抓取特定项目,那么我们可以抓取 StockX 的搜索页面。让我们来看看我们如何做到这一点。 首先,我们可以看到 StockX 的搜索能够按产品类别和查询进行搜索:
这些搜索页面中的每一个都可以进一步细化和排序,从而产生一个唯一的 URL。 对于此示例,我们以与查询“indigo”匹配的最畅销服装商品为例:
这将我们带到最终网址stockx.com/search/apparel?s=indigo 为了抓取这个,我们将使用与以前相同的隐藏网络数据方法。隐藏数据位于同一位置,因此我们可以重用我们的parse_hidden_data()
函数,尽管这次它只包含产品预览数据而不是整个数据集。
import asyncio import json import math from typing import Dict, List import httpx from nested_lookup import nested_lookup from parsel import Selector # create HTTPX client with headers that resemble a web browser client = httpx.AsyncClient( http2=True, follow_redirects=True, limits=httpx.Limits(max_connections=3), # keep this low to avoid being blocked headers={ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "Accept-Encoding": "gzip, deflate, br", "Accept-Language": "en-US,en;q=0.9", "Cache-Control": "no-cache", }, ) # From previous chapter: def parse_nextjs(html: str) -> dict: """extract nextjs cache from page""" selector = Selector(html) data = selector.css("script#__NEXT_DATA__::text").get() if not data: data = selector.css("script[data-name=query]::text").get() data = data.split("=", 1)[-1].strip().strip(";") data = json.loads(data) return data async def scrape_search(url: str, max_pages: int = 25) -> List[Dict]: """Scrape StockX search""" print(f"scraping first search page: {url}") first_page = await client.get(url) assert first_page.status_code == 200, "scrape was blocked" # this should be retried, handled etc. # parse first page for product search data and total amount of pages: data = parse_nextjs(first_page.text) _first_page_results = nested_lookup("results", data)[0] _paging_info = _first_page_results["pageInfo"] total_pages = _paging_info["pageCount"] or math.ceil(_paging_info["total"] / _paging_info["limit"]) # note: pageCount can be missing but we can calculate it ourselves if max_pages < total_pages: total_pages = max_pages product_previews = [edge["node"] for edge in _first_page_results["edges"]] # then scrape other pages concurrently: print(f" scraping remaining {total_pages - 1} search pages") _other_pages = [ # create GET task for each page url asyncio.create_task(client.get(f"{first_page.url}&page={page}")) for page in range(2, total_pages + 1) ] for response in asyncio.as_completed(_other_pages): # run all tasks concurrently response = await response data = parse_nextjs(response.text) _page_results = nested_lookup("results", data)[0] product_previews.extend([edge["node"] for edge in _page_results["edges"]]) return product_previews # example run result = asyncio.run(scrape_search("https://stockx.com/search/sneakers/top-selling?s=indigo", max_pages=2)) print(json.dumps(result, indent=2))
虽然此产品预览数据提供了大量数据,但我们可能希望使用我们在上一章中编写的产品抓取器来抓取整个产品数据集。请参阅该urlKey
字段以获取完整的产品 URL。
常问问题
为了总结这个抓取指南,让我们看一下有关抓取 StockX 的常见问题:
抓取 StockX.com 是否合法?
是的,抓取 StockX.com 是合法的。StockX 电子商务数据是公开可用的,只要爬虫不对网站造成损害,爬虫是完全合法的。
StockX.com 可以被抓取吗?
是的,可以抓取 StockX.com。爬网是另一种网络抓取方法,其中抓取器能够自行发现页面。StockX 提供可用于开发爬行逻辑的站点地图和推荐产品区域。有关更多信息,请参阅我们的Crawling With Python介绍。
StockX 抓取摘要
在本指南中,我们学习了如何使用 Python 和一些社区包来抓取 StockX.com。 为此,我们使用了隐藏的网络数据抓取技术,而不是传统的 HTML 解析,我们检索产品 HTML 页面并提取 Javascript 缓存数据。仅用几行 Python 代码,我们就从 StockX.com 中提取了整个产品数据集。我们还研究了如何使用站点地图或搜索页面发现 StockX 产品页面。