222 lines
9.8 KiB
Markdown
222 lines
9.8 KiB
Markdown
---
|
|
name: ozon-operations
|
|
description: "Ozon e-commerce operations: scraping product pages, seller API (import/pricing/attributes), and CDP auto-surf data collection."
|
|
version: 1.2.0
|
|
author: Hermes Agent
|
|
license: MIT
|
|
metadata:
|
|
hermes:
|
|
tags: [ozon, e-commerce, scraping, seller-api, cdp, russia, cross-border]
|
|
related_skills: [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:**
|
|
```python
|
|
# 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:**
|
|
```bash
|
|
# 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:
|
|
```python
|
|
# 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`:
|
|
|
|
```python
|
|
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:**
|
|
```bash
|
|
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.
|
|
|
|
```javascript
|
|
// 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:**
|
|
```bash
|
|
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.
|