Tự động hóa nghiên cứu thị trường: Dùng Python để scrape Shopify

Cập nhật lần cuối vào April 16, 2026

Endpoint /products.json của Shopify là một trong những “bí mật công khai” quen thuộc nhất trong dữ liệu ecommerce. Chỉ cần gắn nó vào bất kỳ URL cửa hàng Shopify nào, bạn sẽ nhận được JSON có cấu trúc trả về — không cần API key, không cần xác thực, cũng không phải cào từng lớp HTML rối rắm bên trong.

Tôi làm việc trong team của , nên phần lớn thời gian của tôi xoay quanh việc mọi người trích xuất dữ liệu từ web như thế nào. Và chuyện scrape Shopify xuất hiện liên tục — từ đội sales theo dõi giá đối thủ, team vận hành ecommerce benchmark danh mục sản phẩm, đến bộ phận mua hàng tìm nhà cung cấp mới. Với trên Shopify và nền tảng này nắm khoảng , lượng dữ liệu sản phẩm có thể khai thác là cực kỳ lớn.

Hướng dẫn này sẽ đi hết toàn bộ quy trình: endpoint trả về gì, cách phân trang qua hàng nghìn sản phẩm, cách xử lý giới hạn tần suất mà không bị chặn, và cách “làm phẳng” JSON lồng nhau của Shopify thành file CSV hoặc Excel gọn gàng bằng pandas. Tôi cũng sẽ đề cập đến những endpoint ít ai nhắc tới (/collections.json, /meta.json) và giới thiệu một lựa chọn no-code cho những ai muốn bỏ qua Python hoàn toàn.

Endpoint /products.json của Shopify là gì (và vì sao nó giúp scrape dễ hơn rất nhiều)

Mỗi cửa hàng Shopify đều có một endpoint công khai tại {store-url}/products.json trả về dữ liệu sản phẩm dạng có cấu trúc. Không cần API key. Không cần OAuth. Không cần xác thực dưới bất kỳ hình thức nào. Bạn chỉ việc thêm /products.json vào URL cửa hàng là có ngay một mảng JSON chứa toàn bộ sản phẩm trong catalog.

Bạn có thể tự kiểm tra ngay: mở hoặc trên trình duyệt. Bạn sẽ thấy JSON sạch, có cấu trúc rõ ràng với tiêu đề sản phẩm, giá, biến thể, hình ảnh, thẻ tag — đủ cả.

So với cách còn lại: phân tích HTML theme của Shopify. HTML này thường lồng nhau rất sâu, mỗi store lại một kiểu, và còn thay đổi mỗi lần merchant cập nhật theme. Ví dụ bạn sẽ phải xử lý kiểu này:

Cách dùng HTML (khó nhằn):

1<div class="product-card__info">
2  <h3 class="product-card__title">
3    <a href="/products/classic-blue-jeans">Classic Blue Jeans</a>
4  </h3>
5  <span class="price price--on-sale" data-product-price>$149.00</span>
6</div>

Cách dùng JSON (gọn gàng):

1{
2  "title": "Classic Blue Jeans",
3  "handle": "classic-blue-jeans",
4  "vendor": "Hiut Denim",
5  "variants": [{"price": "149.00", "sku": "HD-BLU-32", "available": true}]
6}

JSON thắng ở tính nhất quán, độ tin cậy và độ dễ xử lý. Endpoint này cũng hỗ trợ hai tham số rất quan trọng — ?limit= (tối đa 250 sản phẩm mỗi trang, mặc định là 30) và ?page= để phân trang — chúng ta sẽ dùng rất nhiều trong phần code bên dưới.

Điểm cần phân biệt: đây là endpoint storefront công khai, không phải . Admin API yêu cầu token truy cập của chủ cửa hàng và cung cấp dữ liệu đơn hàng, tồn kho, và thông tin khách hàng. Còn /products.json công khai chỉ là dữ liệu sản phẩm dạng read-only mà ai cũng có thể truy cập. Tôi sẽ nói kỹ hơn về sự khác biệt này ở phần sau, vì trên các diễn đàn thường có rất nhiều nhầm lẫn.

Một lưu ý: không phải cửa hàng Shopify nào cũng mở endpoint này. Trong quá trình thử nghiệm của tôi, khoảng 71% store trả về JSON hợp lệ (allbirds.com, gymshark.com, colourpop.com, kyliecosmetics.com đều hoạt động), trong khi một số cấu hình tùy chỉnh trả về 404 (hiutdenim.co.uk, bombas.com). Cách kiểm tra nhanh rất đơn giản: vào {store-url}/products.json trên trình duyệt và xem kết quả.

Vì sao nên scrape Shopify bằng Python? Các trường hợp ứng dụng phổ biến nhất cho doanh nghiệp

Vì sao phải làm việc này? Vì ROI. hiện dùng công cụ scrape giá tự động cho phân tích cạnh tranh, tăng mạnh so với mức 34% vào năm 2020. Nghiên cứu cũng cho thấy . Dữ liệu này thực sự có giá trị tiền bạc.

Đây là những tình huống phổ biến nhất mà tôi thường thấy:

Use CaseAi được lợiBạn nhận được gì
Theo dõi giá đối thủTeam vận hành ecommerceTheo dõi thay đổi giá, giảm giá và giá so sánh trên catalog đối thủ
Nghiên cứu & tìm nguồn sản phẩmBộ phận mua hàng / merchandisingSo sánh tính năng sản phẩm, biến thể, chất liệu và tình trạng còn hàng
Tạo leadĐội salesTrích xuất tên nhà cung cấp, dữ liệu thương hiệu và thông tin liên hệ từ catalog
Phân tích thị trường & danh mụcĐội marketingHiểu mix sản phẩm, tag, cấu trúc collection và cách định vị
Theo dõi tồn kho & khả năng cung ứngTeam chuỗi cung ứngGiám sát trạng thái tồn kho theo từng biến thể (available: true/false) theo thời gian
Phát hiện sản phẩm mớiTeam productTheo dõi dấu thời gian created_at để phát hiện các lần ra mắt mới của đối thủ

Python là lựa chọn tự nhiên cho công việc này. dùng Python làm ngôn ngữ chính, và hệ sinh thái của nó — requests cho HTTP, pandas cho xử lý dữ liệu, httpx cho async — giúp bạn đi từ “tôi có một URL” đến “tôi có một bảng tính” chỉ trong chưa tới 80 dòng code.

Tài liệu đầy đủ các field trong products.json: Giải thích toàn bộ

Phần lớn các bài hướng dẫn khác chỉ cho bạn title, id, và handle, rồi dừng lại ở đó. Nhưng phản hồi JSON của Shopify có hơn 40 field trải rộng trên sản phẩm, biến thể, hình ảnh và tùy chọn. Biết trước những gì có thể lấy được sẽ giúp bạn khỏi phải scrape lại sau này.

Tôi tổng hợp bảng tham chiếu này từ các phản hồi /products.json thực tế được lấy vào ngày 16/04/2026. Cấu trúc này nhất quán trên mọi store có mở endpoint.

Các field ở cấp sản phẩm

FieldKiểu dữ liệuGiá trị ví dụỨng dụng trong kinh doanh
idInteger123456789Mã định danh sản phẩm duy nhất để chống trùng lặp
titleString"Classic Blue Jeans"Tên sản phẩm để dùng trong catalog và so sánh
handleString"classic-blue-jeans"URL slug — tạo link trang sản phẩm bằng {store}/products/{handle}
body_htmlString (HTML) hoặc null

Our best-selling...

Mô tả sản phẩm để phân tích nội dung và SEO
vendorString"Hiut Denim"Tên thương hiệu/nhà cung cấp cho lead gen hoặc tìm nguồn hàng
product_typeString"Jeans"Phân loại danh mục cho phân tích thị trường
created_atISO DateTime"2024-01-15T10:30:00-05:00"Theo dõi thời điểm sản phẩm được thêm vào (phát hiện ra mắt mới)
updated_atISO DateTime"2025-03-01T08:00:00-05:00"Phát hiện thay đổi gần đây trong catalog
published_atISO DateTime"2024-01-16T00:00:00-05:00"Biết thời điểm sản phẩm được đăng lên storefront
tagsMảng chuỗi["organic", "women", "straight-leg"]Phân tích từ khóa/tag cho SEO, phân loại và bắt xu hướng
variantsMảng đối tượng(xem field của variant bên dưới)Giá, SKU, tình trạng còn hàng theo từng biến thể
imagesMảng đối tượng(xem field của image bên dưới)URL hình ảnh sản phẩm cho catalog và phân tích trực quan
optionsMảng đối tượng[{"name": "Size", "values": ["S","M","L"]}]Hiểu cấu hình sản phẩm (size, màu, chất liệu)

Các field ở cấp biến thể (nằm trong từng sản phẩm)

FieldKiểu dữ liệuVí dụỨng dụng
idInteger987654321Mã định danh biến thể duy nhất
titleString"32 / Blue"Tên hiển thị của biến thể
skuString"HD-BLU-32"Dùng SKU để đối chiếu với hệ thống tồn kho
priceString"185.00"Theo dõi giá (lưu ý: đây là chuỗi, cần ép sang float nếu tính toán)
compare_at_priceString hoặc null"200.00"Giá gốc — rất quan trọng cho theo dõi giảm giá
availableBooleantrueTình trạng còn hàng (chỉ báo tồn kho công khai duy nhất)
weightFloat1.2Phân tích vận chuyển/logistics
option1, option2, option3String"32", "Blue", nullGiá trị của từng lựa chọn
created_at, updated_atISO DateTimeTheo dõi thay đổi ở cấp biến thể

Các field ở cấp hình ảnh

FieldKiểu dữ liệuVí dụỨng dụng
idInteger111222333Mã định danh hình ảnh duy nhất
srcString (URL)"https://cdn.shopify.com/..."Link tải ảnh trực tiếp
altString hoặc null"Front view of jeans"Văn bản alt của ảnh cho phân tích khả năng truy cập
positionInteger1Thứ tự hình ảnh
width, heightInteger2048, 2048Kích thước ảnh

Những gì KHÔNG có trong endpoint công khai

Một điểm cực kỳ dễ nhầm: inventory_quantity KHÔNG có trong phản hồi công khai /products.json. Field này đã bị loại bỏ khỏi các endpoint JSON công khai từ tháng 12/2017 vì lý do bảo mật. Chỉ báo tồn kho duy nhất bạn nhận được là field boolean available trên từng variant (true hoặc false). Nếu muốn xem số lượng tồn kho thực tế, bạn cần Shopify Admin API có xác thực và quyền của chủ store.

Trước khi viết code scrape, hãy xem bảng này và quyết định những field nào thực sự quan trọng với mục tiêu của bạn. Nếu bạn làm price monitoring, bạn cần variants[].price, variants[].compare_at_price, và variants[].available. Nếu bạn làm lead gen, hãy tập trung vào vendor, product_type, và tags. Lọc ngay từ đầu sẽ giúp file CSV gọn hơn rất nhiều.

Ngoài products.json: Collections, Meta và các endpoint Shopify khác

Hầu như không có bài hướng dẫn nào khác nhắc đến các endpoint này. Nhưng nếu làm competitive intelligence nghiêm túc, chúng rất quan trọng.

/collections.json — Tất cả danh mục của store

Trả về toàn bộ collection (danh mục) của store với tiêu đề, handle, mô tả và số lượng sản phẩm. Tôi đã kiểm tra trên zoologistperfumes.com, allbirds.com và gymshark.com — tất cả đều trả JSON hợp lệ.

1{
2  "collections": [
3    {
4      "id": 308387348539,
5      "title": "Attars",
6      "handle": "attars",
7      "published_at": "2026-03-29T12:20:32-04:00",
8      "products_count": 1,
9      "image": { "src": "https://cdn.shopify.com/..." }
10    }
11  ]
12}

Bạn muốn hiểu đối thủ tổ chức catalog của họ ra sao? Đây chính là endpoint cần dùng.

/collections/{handle}/products.json — Sản phẩm theo danh mục

Trả về sản phẩm được lọc theo một collection cụ thể. Cấu trúc JSON giống /products.json, nhưng chỉ áp dụng cho một danh mục. Điều này rất quan trọng nếu bạn scrape theo cấp danh mục — ví dụ chỉ muốn theo dõi collection “Sale” hoặc “New Arrivals” của đối thủ.

/meta.json — Metadata cấp store

Trả về tên store, mô tả, tiền tệ, quốc gia, và — phần hữu ích nhất — published_products_count. Con số này cho phép bạn tính chính xác số trang cần phân trang trước: ceil(published_products_count / 250). Không còn phải tăng page mù quáng cho đến khi gặp phản hồi rỗng nữa.

Nên dùng endpoint nào?

Bạn muốn gìEndpointCó cần xác thực không?
Tất cả sản phẩm (công khai)/products.jsonKhông
Sản phẩm trong một danh mục cụ thể/collections/{handle}/products.jsonKhông
Metadata store + số lượng sản phẩm/meta.jsonKhông
Tất cả collections (danh mục)/collections.jsonKhông
Dữ liệu đơn hàng/doanh số (chỉ store của bạn)Admin API /orders.jsonCó (API key)
Số lượng tồn kho (chỉ store của bạn)Admin API /inventory_levels.json

Câu hỏi lặp đi lặp lại trên forum — “Tôi có thể scrape xem đối thủ đã bán bao nhiêu sản phẩm không?” — có câu trả lời ngắn gọn: không. Không thể làm qua endpoint công khai. Dữ liệu bán hàng và số lượng tồn kho yêu cầu Admin API có xác thực, tức là bạn phải có quyền của chủ store. Endpoint công khai chỉ cung cấp dữ liệu catalog sản phẩm.

shopify-data-access-methods.webp

Cách scrape Shopify bằng Python: Thiết lập từng bước

  • Mức độ: Người mới bắt đầu
  • Thời gian cần: ~15 phút (cài đặt + scrape lần đầu)
  • Bạn cần: Python 3.11+, pip, terminal, và URL cửa hàng Shopify để scrape

Bước 1: Cài Python và các thư viện cần thiết

Hãy đảm bảo bạn đã cài Python 3.11 trở lên (pandas 3.0.x yêu cầu phiên bản này). Sau đó cài hai thư viện cần dùng:

1pip install requests pandas

Để xuất ra Excel, bạn cũng cần:

1pip install openpyxl

Ở đầu script, thêm các import sau:

1import requests
2import pandas as pd
3import time
4import random
5import json

Khi chạy script, bạn không nên thấy lỗi import nào. Nếu pandas báo lỗi phiên bản, hãy nâng Python lên 3.12.

Bước 2: Lấy dữ liệu sản phẩm từ /products.json

Đây là một hàm cơ bản nhận URL store, gọi endpoint và trả về JSON đã parse:

1def fetch_products_page(store_url, page=1, limit=250):
2    """Fetch a single page of products from a Shopify store."""
3    url = f"{store_url.rstrip('/')}/products.json"
4    params = {"limit": limit, "page": page}
5    headers = {
6        "User-Agent": "Mozilla/5.0 (compatible; ProductResearch/1.0)"
7    }
8    response = requests.get(url, params=params, headers=headers, timeout=30)
9    response.raise_for_status()
10    return response.json().get("products", [])

Chi tiết quan trọng:

  • limit=250 là mức tối đa Shopify cho phép mỗi trang. Mặc định là 30, nên đặt rõ giá trị này sẽ giảm số request xuống tối đa 8 lần.
  • Header User-Agent: Luôn đặt một chuỗi hợp lý. Request không có User-Agent dễ bị hệ thống chống bot của Shopify chú ý hơn.
  • timeout=30: Đừng để một request treo mãi.

Hãy thử với một store quen thuộc:

1products = fetch_products_page("https://allbirds.com")
2print(f"Fetched {len(products)} products")
3print(f"First product: {products[0]['title']}")

Bạn sẽ thấy kiểu như: Fetched 250 products và tiêu đề của sản phẩm đầu tiên.

Bước 3: Xử lý phân trang để scrape toàn bộ sản phẩm

Một request chỉ trả về tối đa 250 sản phẩm. Phần lớn store có nhiều hơn thế (Allbirds có hơn 1.420 sản phẩm). Bạn cần lặp qua từng trang cho đến khi nhận được phản hồi rỗng.

1def scrape_all_products(store_url, delay=1.0):
2    """Scrape all products from a Shopify store, handling pagination."""
3    all_products = []
4    page = 1
5    while True:
6        print(f"Fetching page {page}...")
7        products = fetch_products_page(store_url, page=page, limit=250)
8        if not products:
9            print(f"No more products. Total: {len(all_products)}")
10            break
11        all_products.extend(products)
12        print(f"  Got {len(products)} products (total so far: {len(all_products)})")
13        page += 1
14        # Hãy lịch sự: chờ giữa các request
15        time.sleep(delay + random.uniform(0, 0.5))
16    return all_products

Khi products trả về rỗng, nghĩa là bạn đã đến cuối danh sách.

time.sleep() kèm độ trễ ngẫu nhiên giúp bạn nằm dưới ngưỡng giới hạn tần suất không chính thức của Shopify (~2 request/giây).

Mẹo hay: Nếu bạn lấy /meta.json trước, bạn đã biết tổng số sản phẩm và có thể tính chính xác cần bao nhiêu trang: pages = ceil(product_count / 250). Như vậy sẽ tránh được việc gửi thêm một request rỗng ở cuối.

Bước 4: Parse và chọn ra những field bạn cần

Khi đã có toàn bộ sản phẩm dưới dạng list các dictionary trong Python, hãy trích xuất những field quan trọng. Dưới đây là ví dụ lấy các field phổ biến nhất cho việc theo dõi giá:

1def extract_product_data(products):
2    """Extract key fields from products, flattening variants."""
3    rows = []
4    for product in products:
5        for variant in product.get("variants", []):
6            rows.append({
7                "product_id": product["id"],
8                "title": product["title"],
9                "handle": product["handle"],
10                "vendor": product.get("vendor", ""),
11                "product_type": product.get("product_type", ""),
12                "tags": ", ".join(product.get("tags", [])),
13                "created_at": product.get("created_at", ""),
14                "variant_id": variant["id"],
15                "variant_title": variant.get("title", ""),
16                "sku": variant.get("sku", ""),
17                "price": variant.get("price", ""),
18                "compare_at_price": variant.get("compare_at_price", ""),
19                "available": variant.get("available", ""),
20                "image_url": product["images"][0]["src"] if product.get("images") else ""
21            })
22    return rows

Cách này tạo ra một dòng cho mỗi biến thể — format rất hữu ích cho so sánh giá, vì một sản phẩm như “Classic Blue Jeans” có thể có 12 biến thể (6 size × 2 màu), mỗi biến thể có giá và trạng thái còn hàng riêng.

Xuất dữ liệu Shopify sang CSV và Excel bằng pandas

Hầu hết các bài hướng dẫn scrape Shopify khác chỉ đổ JSON thô ra file rồi coi như xong. Với developer thì tạm được, nhưng với analyst ecommerce cần spreadsheet vào thứ Sáu thì hoàn toàn không đủ.

Vấn đề là JSON của Shopify có cấu trúc lồng nhau. Một sản phẩm có thể chứa hàng chục biến thể, mỗi biến thể lại có giá, SKU và tình trạng còn hàng riêng. Việc biến nó thành bảng cần một chút xử lý bằng pandas.

Làm phẳng JSON lồng nhau thành bảng gọn gàng

Có hai cách làm, tùy mục đích sử dụng:

Tùy chọn A: Một dòng cho mỗi variant (tốt nhất cho theo dõi giá và tồn kho)

1# Using the extract_product_data function from Step 4
2products = scrape_all_products("https://allbirds.com")
3rows = extract_product_data(products)
4df = pd.DataFrame(rows)
5print(f"DataFrame shape: {df.shape}")
6print(df.head())

Cách này tạo một bảng phẳng, trong đó mỗi dòng là một tổ hợp sản phẩm-variant duy nhất. Một store có 500 sản phẩm, trung bình 4 variant mỗi sản phẩm, sẽ cho ra DataFrame khoảng 2.000 dòng.

Tùy chọn B: Một dòng cho mỗi sản phẩm ở dạng tổng quan (tốt nhất cho catalog overview)

1def summarize_products(products):
2    """One row per product with min/max price across variants."""
3    rows = []
4    for product in products:
5        prices = [float(v["price"]) for v in product.get("variants", []) if v.get("price")]
6        rows.append({
7            "product_id": product["id"],
8            "title": product["title"],
9            "vendor": product.get("vendor", ""),
10            "product_type": product.get("product_type", ""),
11            "variant_count": len(product.get("variants", [])),
12            "min_price": min(prices) if prices else None,
13            "max_price": max(prices) if prices else None,
14            "any_available": any(v.get("available", False) for v in product.get("variants", [])),
15            "tags": ", ".join(product.get("tags", []))
16        })
17    return rows

Xuất ra CSV, Excel và Google Sheets

1# CSV export (use utf-8-sig so Excel handles special characters)
2df.to_csv("shopify_products.csv", index=False, encoding="utf-8-sig")
3# Excel export (requires openpyxl)
4df.to_excel("shopify_products.xlsx", index=False, engine="openpyxl")
5print("Exported to shopify_products.csv and shopify_products.xlsx")

Với Google Sheets, bạn có thể dùng thư viện gspread với service account, nhưng thật lòng mà nói — với hầu hết nhu cầu, xuất CSV rồi upload lên Google Drive còn nhanh và đơn giản hơn.

Scraping Python ở mức production: Rate limit, retry và chống bị chặn

Script cơ bản vẫn ổn cho các store nhỏ. Nhưng nếu scrape store có hơn 5.000 sản phẩm, hoặc xử lý nhiều store liên tiếp, mọi thứ bắt đầu dễ lỗi.

Hiểu về rate limit và hành vi chặn của Shopify

Các endpoint JSON công khai của Shopify không có tài liệu chính thức về rate limit như mô hình leaky bucket của Admin API, nhưng thử nghiệm thực tế cho thấy:

  • Tốc độ an toàn: khoảng 2 request mỗi giây cho mỗi store
  • Ngưỡng mềm: khoảng 40 request mỗi phút trước khi bắt đầu bị giới hạn
  • HTTP 429: “Too Many Requests” — phản hồi chuẩn khi vượt rate limit
  • HTTP 430: Mã riêng của Shopify, báo hiệu mức chặn bảo mật chứ không chỉ rate limit
  • HTTP 403 hoặc chuyển hướng CAPTCHA: Một số store có thêm bảo vệ Cloudflare

Các request từ hạ tầng cloud dùng chung (AWS Lambda, Google Cloud Run) đặc biệt dễ bị chặn vì dải IP đó thường có tỷ lệ lạm dụng cao.

Kỹ thuật để scrape Shopify ổn định hơn

Đây là tiến trình từ “chạy được trên máy tôi” sang “chạy được trong production”:

Mức độKỹ thuậtĐộ tin cậy
Cơ bảnrequests.get() + ?page=Dễ hỏng với catalog lớn, có thể bị chặn
Trung cấprequests.Session() + ?limit=250 + time.sleep(1) + retry khi gặp 429Hoạt động tốt với đa số store
Nâng caoAsync httpx + xoay vòng User-Agent + exponential backoffChuẩn production, mở rộng tới 10K+ sản phẩm

Mức trung cấp (khuyên dùng cho đa số người dùng):

1import requests
2from requests.adapters import HTTPAdapter
3from urllib3.util.retry import Retry
4def create_session():
5    """Create a requests session with automatic retry logic."""
6    session = requests.Session()
7    retries = Retry(
8        total=5,
9        backoff_factor=1,  # sleep: 0.5s, 1s, 2s, 4s, 8s
10        status_forcelist=[429, 430, 500, 502, 503, 504],
11        respect_retry_after_header=True
12    )
13    session.mount("https://", HTTPAdapter(max_retries=retries))
14    session.headers.update({
15        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
16    })
17    return session

Cấu hình Retry sẽ tự xử lý phản hồi 429 bằng exponential backoff. backoff_factor=1 nghĩa là chuỗi chờ giữa các lần retry sẽ là 0,5s → 1s → 2s → 4s → 8s. Việc tái sử dụng session (requests.Session()) cũng giúp connection pooling, giảm overhead khi gọi nhiều request tới cùng một domain.

Xoay vòng User-Agent: Nếu bạn scrape nhiều store, hãy luân phiên 3–5 chuỗi User-Agent của trình duyệt thực tế. Không phải để “đánh lừa” ai cả — mà để không trông giống một bot cứ gửi header y hệt trong mọi request.

Script Python hoàn chỉnh để scrape Shopify và xuất CSV

Dưới đây là script đầy đủ, có thể copy-paste ngay, kết hợp tất cả những gì ở trên. Phần code thực tế chỉ khoảng 75 dòng (chưa tính comment), và tôi đã test với Allbirds (1.420 sản phẩm), ColourPop (hơn 2.000 sản phẩm), và Zoologist Perfumes (catalog nhỏ).

1import requests
2import pandas as pd
3import time
4import random
5from requests.adapters import HTTPAdapter
6from urllib3.util.retry import Retry
7def create_session():
8    """Create a session with retry logic for rate limits."""
9    session = requests.Session()
10    retries = Retry(
11        total=5,
12        backoff_factor=1,
13        status_forcelist=[429, 430, 500, 502, 503, 504],
14        respect_retry_after_header=True
15    )
16    session.mount("https://", HTTPAdapter(max_retries=retries))
17    session.headers.update({
18        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
19                      "AppleWebKit/537.36 (KHTML, like Gecko) "
20                      "Chrome/125.0.0.0 Safari/537.36"
21    })
22    return session
23def scrape_shopify(store_url, delay=1.0):
24    """Scrape all products from a Shopify store via /products.json."""
25    session = create_session()
26    all_products = []
27    page = 1
28    base_url = f"{store_url.rstrip('/')}/products.json"
29    while True:
30        print(f"  Page {page}...", end=" ")
31        resp = session.get(base_url, params={"limit": 250, "page": page}, timeout=30)
32        resp.raise_for_status()
33        products = resp.json().get("products", [])
34        if not products:
35            break
36        all_products.extend(products)
37        print(f"{len(products)} products (total: {len(all_products)})")
38        page += 1
39        time.sleep(delay + random.uniform(0, 0.5))
40    return all_products
41def flatten_to_variants(products):
42    """Flatten nested product JSON into one row per variant."""
43    rows = []
44    for p in products:
45        base = {
46            "product_id": p["id"],
47            "title": p["title"],
48            "handle": p["handle"],
49            "vendor": p.get("vendor", ""),
50            "product_type": p.get("product_type", ""),
51            "tags": ", ".join(p.get("tags", [])),
52            "created_at": p.get("created_at", ""),
53            "updated_at": p.get("updated_at", ""),
54            "image_url": p["images"][0]["src"] if p.get("images") else "",
55        }
56        for v in p.get("variants", []):
57            row = {**base}
58            row["variant_id"] = v["id"]
59            row["variant_title"] = v.get("title", "")
60            row["sku"] = v.get("sku", "")
61            row["price"] = v.get("price", "")
62            row["compare_at_price"] = v.get("compare_at_price", "")
63            row["available"] = v.get("available", "")
64            rows.append(row)
65    return rows
66if __name__ == "__main__":
67    STORE_URL = "https://allbirds.com"  # Change this to your target store
68    OUTPUT_CSV = "shopify_products.csv"
69    OUTPUT_EXCEL = "shopify_products.xlsx"
70    print(f"Scraping {STORE_URL}...")
71    products = scrape_shopify(STORE_URL)
72    print(f"\nTotal products scraped: {len(products)}")
73    print("Flattening to variant-level rows...")
74    rows = flatten_to_variants(products)
75    df = pd.DataFrame(rows)
76    print(f"DataFrame: {df.shape[0]} rows x {df.shape[1]} columns")
77    df.to_csv(OUTPUT_CSV, index=False, encoding="utf-8-sig")
78    df.to_excel(OUTPUT_EXCEL, index=False, engine="openpyxl")
79    print(f"\nExported to {OUTPUT_CSV} and {OUTPUT_EXCEL}")

Chạy bằng python scrape_shopify.py. Với Allbirds, quá trình này mất khoảng 45 giây và tạo ra file CSV với khoảng 5.000+ dòng (mỗi variant một dòng). Output terminal sẽ trông như sau:

1Scraping https://allbirds.com...
2  Page 1... 250 products (total: 250)
3  Page 2... 250 products (total: 500)
4  ...
5  Page 6... 170 products (total: 1420)
6Total products scraped: 1420
7Flattening to variant-level rows...
8DataFrame: 5680 rows x 14 columns
9Exported to shopify_products.csv and shopify_products.xlsx

Bỏ qua Python: Scrape Shopify trong 2 click với Thunderbit (lựa chọn no-code)

Không phải ai cũng muốn cài Python, xử lý lỗi import, hay duy trì một script scraping. Với một nhân viên sales chỉ cần giá đối thủ vào sáng hôm sau, Python rõ ràng là quá mức cần thiết.

Đó là lý do chúng tôi xây dựng — một AI web scraper chạy dưới dạng Chrome extension. Không cần code, không cần API key, không cần setup môi trường.

Cách Thunderbit scrape các store Shopify

Thunderbit có sẵn Shopify Scraper template được cấu hình trước cho trang sản phẩm Shopify. Bạn chỉ cần cài , mở một store Shopify, rồi bấm “Scrape.” Template sẽ tự động lấy tên sản phẩm, mô tả, giá, chi tiết biến thể, hình ảnh và thông tin nhà cung cấp.

Với những store mà template không khớp hoàn toàn (theme tùy chỉnh, bố cục lạ), tính năng AI Suggest Fields của Thunderbit sẽ đọc trang và tự tạo tên cột. Bạn có thể chỉnh lại các trường này — đổi tên cột, thêm field, hoặc viết hướng dẫn như “chỉ lấy sản phẩm có compare_at_price.”

Một vài tính năng khớp rất sát với những gì script Python làm:

  • Scrape subpage: Tự động vào từng trang chi tiết sản phẩm và làm giàu bảng bằng mô tả đầy đủ, review hoặc chi tiết variant — giống như script Python của chúng ta đi qua từng trang, nhưng không cần code.
  • Tự động phân trang: Xử lý pagination kiểu click-through và infinite scroll mà không cần cấu hình.
  • Scraping theo lịch: Thiết lập job lặp lại (ví dụ “mỗi thứ Hai lúc 9 giờ sáng”) để theo dõi giá liên tục — không cần cron job hay server.
  • Xuất file miễn phí sang CSV, Excel, Google Sheets, Airtable hoặc Notion ở mọi gói.

Script Python vs Thunderbit: So sánh thẳng thắn

Yếu tốScript PythonThunderbit (No-Code)
Thời gian thiết lập15–60 phút (môi trường + code)~2 phút (cài Chrome extension)
Có cần code khôngCó (Python)Không
Mức độ tùy biếnKhông giới hạnTrường gợi ý bởi AI + prompt tùy chỉnh
Xử lý phân trangPhải tự codeTự động
Định dạng xuấtTự code (CSV/Excel)CSV, Excel, Google Sheets, Airtable, Notion (miễn phí)
Chạy theo lịchCron job + hostingCó sẵn scheduler
Xử lý rate limitPhải tự code retry/backoffTự xử lý tự động
Phù hợp nhấtDeveloper, pipeline dữ liệu lớnNgười dùng business, cần lấy nhanh, theo dõi định kỳ

Hãy dùng Python khi bạn cần toàn quyền kiểm soát hoặc đang tích hợp vào một data pipeline lớn hơn. Hãy dùng Thunderbit khi bạn cần dữ liệu nhanh và không muốn phải bảo trì code. Nếu muốn xem sâu hơn về , chúng tôi có một bài hướng dẫn riêng về chủ đề này.

python-vs-thunderbit-comparison.webp

Mẹo và best practices khi scrape các store Shopify

Những nguyên tắc này đúng dù bạn dùng công cụ nào:

  • Luôn dùng ?limit=250 để giảm số request xuống mức thấp nhất. Mặc định 30 sản phẩm mỗi trang sẽ khiến bạn phải gửi nhiều request hơn 8 lần cho cùng một lượng dữ liệu.
  • Tôn trọng store: Chèn độ trễ 1–2 giây giữa các request. Spam server bằng request liên tục là thói quen xấu và làm tăng khả năng bị chặn.
  • Kiểm tra robots.txt trước: robots.txt mặc định của Shopify không chặn /products.json. Nhưng một số store có thể thêm rule riêng, nên hãy xác minh trước khi scrape ở quy mô lớn.
  • Lưu JSON thô cục bộ trước, rồi mới xử lý. Nếu logic parse thay đổi về sau, bạn sẽ không cần scrape lại. Chỉ cần json.dump(all_products, open("raw_data.json", "w")) trước khi làm phẳng là đã tiết kiệm rất nhiều công sức.
  • Khử trùng lặp theo product.id: Một số trường hợp biên của phân trang có thể trả về sản phẩm trùng nhau ở ranh giới giữa các trang. Dùng nhanh df.drop_duplicates(subset=["product_id", "variant_id"]) là xử lý được.
  • Ép price sang float trước khi tính toán. Shopify trả về giá dưới dạng chuỗi ("185.00"), không phải số.
  • Theo dõi thay đổi endpoint: Mặc dù /products.json đã ổn định trong nhiều năm, về mặt lý thuyết Shopify có thể siết lại. Nếu scraper đột nhiên báo 404, hãy kiểm tra store thủ công trước.

Để hiểu thêm về cách xây dựng scraper bền vững, bạn có thể xem hướng dẫn của chúng tôi về .

Các cân nhắc pháp lý và đạo đức khi scrape Shopify

Phần này ngắn, nhưng rất quan trọng.

Endpoint /products.json cung cấp dữ liệu sản phẩm công khai — tức là thông tin mà bất kỳ ai vào cửa hàng cũng có thể nhìn thấy. Điều khoản dịch vụ của Shopify có đoạn nói về việc không dùng “phương tiện tự động” để truy cập “the Services”, nhưng cách diễn đạt này chủ yếu nhằm vào chính nền tảng (dashboard admin, checkout), không phải dữ liệu storefront công khai. Tính đến tháng 04/2026, chưa có vụ kiện nào liên quan riêng đến việc scrape Shopify.

Các tiền lệ pháp lý quan trọng ủng hộ việc scrape dữ liệu công khai: vụ hiQ v. LinkedIn xác lập rằng scraping dữ liệu công khai không vi phạm CFAA, và Meta v. Bright Data (2024) phán quyết rằng các hạn chế trong TOS chỉ áp dụng khi người dùng đã đăng nhập.

Best practices:

  • Chỉ scrape dữ liệu sản phẩm công khai
  • Không scrape dữ liệu cá nhân hoặc dữ liệu khách hàng
  • Tôn trọng robots.txt và rate limit
  • Tuân thủ GDPR/CCPA nếu xử lý bất kỳ dữ liệu cá nhân nào (dữ liệu catalog sản phẩm là dữ liệu không mang tính cá nhân)
  • Dùng User-Agent rõ ràng để nhận diện mình
  • Nếu scrape chính store Shopify của bạn qua Admin API thì luôn ổn

Nếu muốn tìm hiểu sâu hơn, hãy xem bài viết của chúng tôi về .

Kết luận và các điểm chính cần nhớ

Endpoint công khai /products.json của Shopify làm cho việc trích xuất dữ liệu ecommerce trở nên đơn giản đến mức tối đa. Quy trình là: thêm /products.json → lấy dữ liệu bằng Python → phân trang với ?limit=250&page= → làm phẳng bằng pandas → xuất ra CSV hoặc Excel.

Điểm mà hướng dẫn này làm tốt hơn các bài khác:

  • Tham chiếu field đầy đủ: Biết chính xác có thể lấy những dữ liệu nào (hơn 40 field trên sản phẩm, biến thể và hình ảnh) trước khi viết dù chỉ một dòng code
  • Endpoint bổ sung: /collections.json/meta.json cung cấp insight ở cấp danh mục và metadata store mà ít bài nào đề cập
  • Kỹ thuật sẵn sàng cho production: Tái sử dụng session, exponential backoff, header User-Agent, và ?limit=250 để xử lý rate limit thực tế
  • Xuất CSV/Excel đúng cách: Làm phẳng dữ liệu theo variant bằng pandas, không chỉ đổ JSON thô
  • Lựa chọn no-code: cho những ai ưu tiên tốc độ hơn sự linh hoạt của code

Nếu bạn cần lấy dữ liệu Shopify một lần hoặc lặp lại mà không muốn viết code, hãy thử — Shopify Scraper template xử lý mọi thứ từ phân trang đến xuất file. Còn nếu bạn muốn xây pipeline dữ liệu tùy chỉnh hoặc scrape quy mô lớn trên nhiều store, script Python trong bài này sẽ cho bạn toàn quyền kiểm soát.

Xem để có video hướng dẫn trực quan, hoặc đọc thêm các bài liên quan như .

Dùng Thunderbit để scrape Shopify

FAQs

Có thể scrape bất kỳ store Shopify nào bằng products.json không?

Phần lớn store Shopify mặc định đều mở endpoint này — trong thử nghiệm, khoảng 71% trả về JSON hợp lệ. Một số store có cấu hình tùy chỉnh hoặc lớp bảo vệ bổ sung (Cloudflare, headless setup) có thể trả về 404 hoặc chặn request. Cách kiểm tra nhanh là mở {store-url}/products.json trên trình duyệt. Nếu thấy JSON thì là ổn.

Scrape Shopify có hợp pháp không?

Dữ liệu sản phẩm công khai (giá, tiêu đề, hình ảnh, mô tả) nhìn chung là có thể truy cập, và các tiền lệ pháp lý như hiQ v. LinkedIn ủng hộ việc scrape thông tin công khai. Tuy vậy, bạn vẫn nên kiểm tra điều khoản dịch vụ của store cụ thể và luật tại địa phương của bạn. Đừng scrape dữ liệu cá nhân hoặc dữ liệu khách hàng, và hãy tôn trọng rate limit.

Có thể scrape bao nhiêu sản phẩm từ một store Shopify?

Không có giới hạn cứng về tổng số. Phân trang với ?limit=250&page= cho phép bạn lấy toàn bộ catalog. Với những store rất lớn (25.000+ sản phẩm), hãy dùng session reuse và độ trễ giữa các request để tránh rate limit. Endpoint /meta.json có thể cho bạn biết chính xác số lượng sản phẩm ngay từ đầu để ước tính số trang cần lấy.

products.json khác gì với Shopify Admin API?

/products.json là endpoint công khai — không cần xác thực, chỉ cho dữ liệu sản phẩm dạng read-only, ai cũng truy cập được. Admin API yêu cầu token truy cập của chủ store và cung cấp đơn hàng, số lượng tồn kho, dữ liệu khách hàng và quyền ghi. Nếu bạn cần dữ liệu bán hàng hoặc số lượng tồn kho thực tế, bạn phải có quyền truy cập Admin API (tức là bạn phải là chủ store hoặc được họ cho phép).

Có thể scrape Shopify mà không dùng Python không?

Hoàn toàn có. Các công cụ như cho phép scrape store Shopify ngay trong Chrome extension mà không cần code. Nó tự động xử lý phân trang và xuất trực tiếp ra CSV, Excel, Google Sheets, Airtable hoặc Notion. Với developer thích ngôn ngữ khác, endpoint /products.json cũng chạy tốt với JavaScript, Ruby, Go — bất kỳ ngôn ngữ nào có thể gửi HTTP request và parse JSON.

Tìm hiểu thêm

Mục lục

Thử Thunderbit

Trích xuất lead và dữ liệu khác chỉ trong 2 cú nhấp. Powered by AI.

Nhận Thunderbit Miễn phí
Trích xuất dữ liệu bằng AI
Dễ dàng chuyển dữ liệu sang Google Sheets, Airtable hoặc Notion
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week