9.8 KiB
name, description, version, author, license, metadata
| name | description | version | author | license | metadata | |||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ozon-operations | Ozon e-commerce operations: scraping product pages, seller API (import/pricing/attributes), and CDP auto-surf data collection. | 1.2.0 | Hermes Agent | MIT |
|
Ozon Operations
Three workflows for working with Ozon (Russian e-commerce platform): scraping, seller API, and automated data collection.
Workflow Overview
| Workflow | Purpose | Method |
|---|---|---|
| Product Page Scraping | Extract product data from ozon.ru | CamouFox + Russia proxy + slider CAPTCHA |
| Seller API | Import, pricing, attributes, health analysis | REST API (v3) |
| CDP Auto-Surf | Continuous product data collection | CDP tunnel, random page hopping |
| CDP Seller Login | Automate Ozon Seller web UI via Bridge CDP | CDP Input.dispatchMouseEvent |
| Product Health Audit | Analyze product availability & fix issues | v3 API + availability triage |
| Messenger Management | Read & manage platform notifications | CDP evaluate (read-only); manual mark-read only |
Section 0: CDP Interaction with Ozon Seller UI
Ozon Seller (seller.ozon.ru) is a React SPA that rejects standard JS click() and requires CDP Input.dispatchMouseEvent for button interactions. Never use element.click() or dispatchEvent(new MouseEvent('click')) — these are silently ignored by React/Vue synthetic event handlers.
Reliable click pattern:
# Get button position, then:
cdp('Input.dispatchMouseEvent', {'type': 'mouseMoved', 'x': x, 'y': y})
cdp('Input.dispatchMouseEvent', {'type': 'mousePressed', 'x': x, 'y': y, 'button': 'left', 'clickCount': 1})
time.sleep(0.15)
cdp('Input.dispatchMouseEvent', {'type': 'mouseReleased', 'x': x, 'y': y, 'button': 'left', 'clickCount': 1})
Also needed: Input.insertText for form fields (textareas/inputs ignore value= assignment + event dispatch in React).
UI limitations:
- Product filter tabs (错误, 待修改, etc.) cannot be activated via URL params or DOM clicks — they use internal React state
- Product detail pages don't support deep-linking (
/app/products/CODErenders empty) - Product list uses lazy loading — scrolling down doesn't load more; must navigate page-by-page
- "生成密钥" button opens an inline form (not a modal); the form uses permission checkboxes + text input
See: references/ozon-cdp-interaction.md
Sidebar navigation URLs and common 404 traps are documented in references/ozon-seller-navigation.md — use for direct CDP navigate between sections without clicking.
Section 1: Product Page Scraping
Scrape Ozon.ru product pages using CamouFox anti-detection browser with Russian proxy and slider CAPTCHA auto-solve.
When to use: Extracting product data (title, price, rating, images) from specific Ozon product pages.
Key challenges:
- Anti-bot detection (Cloudflare, slider CAPTCHA)
- Geo-restricted content (Russia-only products)
- Dynamic pricing and availability
Quick start:
# Use Russian SOCKS5 proxy with CamouFox
python3 scripts/ozon_scraper.py --proxy socks5://ru-proxy:1080 --url "https://ozon.ru/product/..."
Pitfalls:
- Must use Russian proxy for full product data
- Slider CAPTCHA requires CamouFox (not standard Playwright)
- Request frequency triggers rate limiting
See: references/ozon-ru-scraper.md for full scraping guide, proxy selection, and CAPTCHA handling.
Section 2: Seller API
Ozon Seller API for product import, review, pricing, categories, attributes, and product health analysis.
When to use: Programmatic product management on Ozon — importing listings, updating prices, checking review status, auditing product health.
API Key Acquisition
API keys for Ozon stores are stored in AtomK backend:
# Login to AtomK
r = requests.post('https://atomlisting.com/api/v1/auth/login',
json={'username': 'admincao', 'password': 'Tt123456!'}, verify=False)
token = r.json()['token']
# Get stores with extra_data.api_key
r = requests.get('https://atomlisting.com/api/v1/stores',
headers={'Authorization': f'Bearer {token}'}, verify=False)
for store in r.json():
extra = store.get('extra_data', {})
# extra['api_key'] — Ozon API Key
# extra['client_id'] — Ozon Client-Id
Pitfall: API keys can be deactivated by Ozon. If the API returns {"code":7,"message":"Api-key is deactivated"}, you must generate a new key at seller.ozon.ru/app/settings/api-keys using the CDP mouse-event pattern (Section 0).
Product Health Audit (v3 API)
Use POST /v3/product/list + POST /v3/product/info/list to audit all products. The critical field is availabilities, NOT status.state:
av = product.get("availabilities", [{}])[0]
status = av.get("availability") # "AVAILABLE", "UNAVAILABLE", "HIDDEN"
reasons = [r["human_text"]["text"] for r in av.get("reasons", [])]
Common states:
| State | Meaning | Typical fix |
|---|---|---|
AVAILABLE |
Active for sale | None |
HIDDEN |
"Нарушен запрет на копирование" — copy prohibition | Replace images/description with original content |
UNAVAILABLE |
"Неактуальный товар" — stale product | Update stock, pricing, or re-activate |
NO_DATA |
Empty availabilities array | May need manual review |
Full audit script: See references/ozon-product-audit.md.
Key features:
- v3/import for product creation (use
desc_cat_id+type_idfor categories) - Attribute API: use
idnotattribute_id, dictionary attributes needdict_id - Pricing: must be in CNY
- Review: check image-to-category matching
Quick start:
curl -s "https://api-seller.ozon.ru/v3/product/import" \
-H "Client-Id: $CLIENT_ID" \
-H "Api-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d @product_import.json
Pitfalls:
- Category: use
description_category_id+type_id, NOTcategory_id - Attributes: use
attribute.id, NOTattribute.attribute_id - Dictionary attributes: must include
dictionary_attribute_id(aliasdict_id) - Price must be in CNY
- v3/import is the current endpoint — earlier versions are deprecated
See: references/ozon-seller-api.md for full API reference with import, pricing, and attribute guides.
Section 4: Messenger & Notifications Management
The Ozon Seller messenger (/app/messenger?group=*) shows system notifications,
support chats, and buyer messages. Unread counts appear on the dashboard and top nav.
Group Structure
| Group | URL param | Typical content |
|---|---|---|
| 主要 (Main) | ?group=main |
Platform announcements, policy updates |
| 客服 (Support) | ?group=support_v2 |
Automated system messages (product issues, archivals) |
| 通知 (Notifications) | ?group=system |
FBS orders, returns, quality alerts |
| 推广 (Promotions) | ?group=promotion |
Marketing tools, ad features |
Critical Limitations
No programmatic mark-read available. The Ozon messenger React SPA:
- Has no "mark all as read" button in any group
- Ignores all JS
element.click(),dispatchEvent(MouseEvent), and CDP mouse events — clicks are consumed but read state never changes - No discoverable REST API (all attempts return 404; uses gRPC)
- Direct CDP navigate to messenger URLs causes SPA hydration failure (shell loads but Vue doesn't bootstrap)
Only manual mark-read works — user must click messages in their Desktop browser.
Navigation
Access must go through the SSO flow — direct CDP navigate to messenger routes fails:
- Navigate to
seller.ozon.ru/app/registration/signin - SSO auto-login → company selection → "下一步"
- Dashboard loads with top-nav messenger links showing unread counts
- Click the count badge to enter messenger (triggers proper SPA routing)
The dashboard (#__ozon) and messenger (#app) use different SPA frameworks — see bridge-cdp-agent skill reference references/ozon-dual-spa-frameworks.md.
Reading Content
Message content IS accessible via CDP evaluate — the DOM shows message text in .m9d-c2 elements. Use this to extract and summarize visible messages even though you can't mark them as read.
// Get visible message previews in current group
Array.from(document.querySelectorAll('.m9d-c2')).map(e => ({
text: (e.innerText || '').substring(0, 100),
rect: e.getBoundingClientRect()
}))
Pitfalls
- Dashboard and messenger are different SPAs — Vue instance location differs (
#__ozon.__vue__vs#app.__vue__) - Sidebar group labels are
<SPAN class="m9d-z8">inside<DIV class="m9d-z5">, not clickable buttons - Unread count badges are
<DIV class="m9d-z7">— read these to track remaining unread - The settings button at position (474, 178) opens a chat-settings panel that blocks the message list
- Page.navigate to messenger URL returns minimal shell (~58 chars); only client-side routing via Vue $router.push() works
Auto-surf Ozon.ru via CDP tunnel — randomly hops between product pages, extracts data, saves to JSONL. Runs continuously.
When to use: Continuous background collection of Ozon product data for market research, price monitoring.
Key patterns:
- CDP tunnel for browser connection
- Random page navigation (surf pattern)
- Extract: SKU, price, rating, seller, stock, delivery info
- Append to JSONL file
- Graceful shutdown on interrupt
Quick start:
python3 scripts/cdp_ozon_collector.py --output products.jsonl
Pitfalls:
- CDP connection may drop — implement reconnection
- Respect rate limits — add delays between page loads
- JSONL append mode — don't rewrite entire file each iteration
See: references/cdp-ozon-collector.md for full collection pipeline.