Files

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
hermes
tags related_skills
ozon
e-commerce
scraping
seller-api
cdp
russia
cross-border
proxy-management
camoufox-google-2fa-login
node-playwright-web-automation
cross-border-social-marketing

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/CODE renders 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_id for categories)
  • Attribute API: use id not attribute_id, dictionary attributes need dict_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, NOT category_id
  • Attributes: use attribute.id, NOT attribute.attribute_id
  • Dictionary attributes: must include dictionary_attribute_id (alias dict_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:

  1. Navigate to seller.ozon.ru/app/registration/signin
  2. SSO auto-login → company selection → "下一步"
  3. Dashboard loads with top-nav messenger links showing unread counts
  4. 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.