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:
- searches Taobao for a keyword, best sellers first,
- analyses prices, Tmall share and shipping locations with pandas,
- narrows the search to Tmall stores in a price range,
- reads the buyer Q&A (问大家) of the top product,
- 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"
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")
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-client3.x, wherecall()returns aRunobject. On 1.x/2.x, userun["defaultDatasetId"]instead ofrun.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"
}
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))
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())
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"])
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")
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
discountPriceday 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)