DEV Community

Silkline
Silkline

Posted on

How to get Taobao product data in Python (prices, sales, shops)

Disclosure: I built the Taobao Scraper used in this tutorial. It's a paid Actor on Apify (pay per result), and the pricing is listed at the end.

Taobao (淘宝) and its premium side, Tmall (天猫), are where most of China shops online. If you source products, track competitor prices or study what Chinese consumers buy, the data you want is there: prices, discounts, how many units sold, which shops sell them and where they ship from.

Getting it yourself is painful. The site is in Chinese, search needs a logged-in account, and requests are signed. This tutorial skips that and gets Taobao data into a pandas DataFrame in a few lines.

What you'll build

A short script that:

  1. searches Taobao for a keyword, best sellers first,
  2. analyses prices, Tmall share and shipping locations with pandas,
  3. narrows the search to Tmall stores in a price range,
  4. reads the buyer Q&A (问大家) of the top product,
  5. lists everything that product's shop sells.

Setup

You need Python 3.10+ and an Apify account. Copy your API token from Console → Settings → API & Integrations.

pip install apify-client pandas
export APIFY_TOKEN="your-token-here"
Enter fullscreen mode Exit fullscreen mode

Step 1: search by keyword

The Actor has three modes: search, questions and shop. Start with search.

Search in Chinese. "phone case" works, but "手机壳" is what Chinese shoppers type and gives the results they see.

import os
from decimal import Decimal
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
actor = client.actor("clover_folktale/taobao-scraper")

run = actor.call(
    run_input={
        "mode": "search",
        "keywords": ["手机壳"],   # "phone case"
        "sort": "sales",          # best sellers first; also "general", "priceHighToLow"
        "maxResults": 40,
    },
    max_total_charge_usd=Decimal("0.50"),  # hard spending cap for this run
)

items = list(client.dataset(run.default_dataset_id).iterate_items())
print(len(items), "products")
Enter fullscreen mode Exit fullscreen mode

maxResults is the total for the whole run, and paging is handled for you. max_total_charge_usd is a safety net: the run stops once that amount is spent.

This uses apify-client 3.x, where call() returns a Run object. On 1.x/2.x, use run["defaultDatasetId"] instead of run.default_dataset_id.

Each item is a flat dict:

{
  "type": "product",
  "itemId": "1234567890123",
  "url": "https://item.taobao.com/item.htm?id=1234567890123",
  "title": "Example phone case title",
  "price": 136.0,
  "discountPrice": 99.0,
  "discountType": "Example promotion",
  "salesText": "1万+",
  "salesCount": 10000,
  "commentCount": null,
  "isTmall": true,
  "shopId": "1659916565",
  "shopName": "Example flagship store",
  "sellerLevel": 18,
  "sellerPositiveRate": 99.6,
  "descriptionRating": 4.8,
  "serviceRating": 4.8,
  "shippingRating": 4.8,
  "itemLocation": "广东 东莞",
  "imageUrl": "https://example.com/main.jpg"
}
Enter fullscreen mode Exit fullscreen mode

Values above are illustrative; missing fields are null (search results often have no commentCount). Prices are in yuan. salesText is what Taobao shows ("1万+" means 10,000+), and salesCount is that number as an integer, so "1万+" becomes 10,000, a floor, not an exact count.

Step 2: look at the market with pandas

import pandas as pd

df = pd.DataFrame(items)

top = df.sort_values("salesCount", ascending=False)[
    ["title", "price", "salesText", "shopName", "isTmall", "itemLocation"]
].head(5)
print(top.to_string(index=False))

print(df["price"].describe()[["min", "50%", "max"]])
print("Tmall share:", round(df["isTmall"].mean(), 2))
print(df["itemLocation"].value_counts().head(5))
Enter fullscreen mode Exit fullscreen mode

When I ran it on 22 October 2026 for 手机壳, sorted by sales:

  • the top 5 products had all sold 10,000+ units;
  • prices ran from ¥9.80 to ¥605.88, with a median of about ¥70;
  • 38 of the 40 best sellers (95%) were Tmall stores;
  • 23 shipped from Shenzhen and 11 from Dongguan.

That last point is the kind of thing that is hard to see on the website and takes one line in pandas. Save the whole table with df.to_csv("taobao_phone_cases.csv", index=False).

Step 3: Tmall stores in a price range

run = actor.call(
    run_input={
        "mode": "search",
        "keywords": ["手机壳"],
        "tmallOnly": True,
        "minPrice": 30,
        "maxPrice": 100,
        "maxResults": 20,
    },
    max_total_charge_usd=Decimal("0.50"),
)
tmall = list(client.dataset(run.default_dataset_id).iterate_items())
Enter fullscreen mode Exit fullscreen mode

One detail that matters: the price range applies to the price after discount (discountPrice), which is what shoppers actually pay. In my run all 20 products had a discounted price between ¥30 and ¥100, but five of them listed a higher price, one at ¥200 discounted to ¥100. Filter on discountPrice in your own analysis too.

Step 4: read the buyer Q&A

Taobao's 问大家 ("ask everyone") section is where buyers ask people who already bought the product. It's short, honest feedback. Pass item IDs or product links to mode questions:

top_item = df.sort_values("salesCount", ascending=False).iloc[0]["itemId"]

run = actor.call(
    run_input={"mode": "questions", "items": [top_item], "maxResults": 10},
    max_total_charge_usd=Decimal("0.50"),
)
questions = list(client.dataset(run.default_dataset_id).iterate_items())

for q in questions[:3]:
    print(q["question"], "|", q["answerCount"], "answers |", q["answers"][0]["text"])
Enter fullscreen mode Exit fullscreen mode

For the best-selling case, the first three questions were (translated):

  • "Is the quality good? Will it scratch the phone?" 4 answers. First answer: "No."
  • "Does it protect the camera too?" 4 answers. "Yes, but the case is a bit stiff and hard to take off."
  • "Does it affect wireless charging in the car?" 5 answers. "No."

Each question record has question, askedAt, answerCount and answers (each with text and answeredAt).

Step 5: everything a shop sells

shop_id = df.sort_values("salesCount", ascending=False).iloc[0]["shopId"]

run = actor.call(
    run_input={"mode": "shop", "shops": [shop_id], "maxResults": 50},
    max_total_charge_usd=Decimal("0.50"),
)
catalog = list(client.dataset(run.default_dataset_id).iterate_items())
print(len(catalog), "products in this shop")
Enter fullscreen mode Exit fullscreen mode

Shop records have the same fields as search results. The source doesn't return the shop's name in this mode, so shopName is empty there; keep it from the search step.

What it costs

The Actor is pay-per-event:

Event Price
Run start $0.03 per run, plus one more for each further keyword, item or shop the run reaches
Product or question returned $8 per 1,000

The 40-product search in step 1 costs $0.03 + 40 × $0.008 = $0.35. Check the store page for the current price on your plan. On the free Apify plan, every run returns a sample dataset so you can see the output format; real searches need a paid plan.

Going further

  • Track prices over time. Save the step 1 input as a task and schedule it daily in the Apify Console. Comparing discountPrice day by day shows when sellers start promotions.
  • No-code exports. Every dataset downloads as JSON, CSV or Excel and connects to Google Sheets, Zapier and Make.
  • AI agents. The Actor is available through the Apify MCP server, so an agent can call it directly.
  • Other Chinese marketplaces. The same approach works for 1688 (wholesale prices and suppliers) and Douyin Shop (sales and creator commission rates).

The data is public listing information only. You are responsible for using it in line with applicable laws and Taobao's terms.

If a field you need is missing, open an issue on the Actor page. I read them.

Top comments (0)