xmlriver-pro


Namexmlriver-pro JSON
Version 2.0.0 PyPI version JSON
download
home_pagehttps://github.com/xmlriver-pro/xmlriver-pro
SummaryProfessional Python client for XMLRiver API with full coverage
upload_time2025-10-19 13:02:43
maintainerNone
docs_urlNone
authorXMLRiver Pro Team
requires_python>=3.10
licenseMIT
keywords xmlriver google yandex search api seo scraping search-engine news images maps ads
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # XMLRiver Pro

<div align="center">

[![GitHub stars](https://img.shields.io/github/stars/Eapwrk/xmlriver-pro?style=social)](https://github.com/Eapwrk/xmlriver-pro)
[![Star this repo](https://img.shields.io/badge/⭐-Star%20this%20repo-yellow?style=social)](https://github.com/Eapwrk/xmlriver-pro)
[![Fork this repo](https://img.shields.io/badge/🍴-Fork%20this%20repo-blue?style=social)](https://github.com/Eapwrk/xmlriver-pro/fork)
[![Watch this repo](https://img.shields.io/badge/👁-Watch%20this%20repo-green?style=social)](https://github.com/Eapwrk/xmlriver-pro/subscription)
[![Sponsor this repo](https://img.shields.io/badge/💖-Sponsor%20this%20repo-pink?style=social)](https://github.com/sponsors/Eapwrk)
[![Report Issues](https://img.shields.io/badge/🐛-Report%20Issues-red?style=social)](https://github.com/Eapwrk/xmlriver-pro/issues)
[![Join Discussions](https://img.shields.io/badge/💬-Join%20Discussions-purple?style=social)](https://github.com/Eapwrk/xmlriver-pro/discussions)
[![Pull Requests](https://img.shields.io/badge/🔀-Pull%20Requests-orange?style=social)](https://github.com/Eapwrk/xmlriver-pro/pulls)
[![Wiki](https://img.shields.io/badge/📚-Wiki-teal?style=social)](https://github.com/Eapwrk/xmlriver-pro/wiki)
[![Actions](https://img.shields.io/badge/⚙️-Actions-gray?style=social)](https://github.com/Eapwrk/xmlriver-pro/actions)
[![Releases](https://img.shields.io/badge/🚀-Releases-cyan?style=social)](https://github.com/Eapwrk/xmlriver-pro/releases)
[![Security](https://img.shields.io/badge/🔒-Security-indigo?style=social)](https://github.com/Eapwrk/xmlriver-pro/security)
[![Insights](https://img.shields.io/badge/📊-Insights-lime?style=social)](https://github.com/Eapwrk/xmlriver-pro/pulse)
[![Settings](https://img.shields.io/badge/⚙️-Settings-slate?style=social)](https://github.com/Eapwrk/xmlriver-pro/settings)
[![Code](https://img.shields.io/badge/💻-Code-emerald?style=social)](https://github.com/Eapwrk/xmlriver-pro)
[![Issues](https://img.shields.io/badge/🐛-Issues-red?style=social)](https://github.com/Eapwrk/xmlriver-pro/issues)
[![Pull Requests](https://img.shields.io/badge/🔀-Pull%20Requests-orange?style=social)](https://github.com/Eapwrk/xmlriver-pro/pulls)
[![Discussions](https://img.shields.io/badge/💬-Discussions-purple?style=social)](https://github.com/Eapwrk/xmlriver-pro/discussions)
[![Wiki](https://img.shields.io/badge/📚-Wiki-teal?style=social)](https://github.com/Eapwrk/xmlriver-pro/wiki)
[![Actions](https://img.shields.io/badge/⚙️-Actions-gray?style=social)](https://github.com/Eapwrk/xmlriver-pro/actions)
[![PyPI version](https://img.shields.io/pypi/v/xmlriver-pro?color=blue)](https://pypi.org/project/xmlriver-pro/)
[![Python Version](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://python.org)
[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
[![Downloads](https://img.shields.io/pypi/dm/xmlriver-pro?color=orange)](https://pypi.org/project/xmlriver-pro/)
[![Coverage](https://img.shields.io/badge/coverage-57%25-brightgreen.svg)](https://github.com/Eapwrk/xmlriver-pro)
[![GitHub last commit](https://img.shields.io/github/last-commit/Eapwrk/xmlriver-pro?color=blue)](https://github.com/Eapwrk/xmlriver-pro)
[![GitHub issues](https://img.shields.io/badge/github-issues-red?style=social)](https://github.com/Eapwrk/xmlriver-pro/issues)

```
__  ____  __ _     ____  _                  ____            
\ \/ /  \/  | |   |  _ \(_)_   _____ _ __  |  _ \ _ __ ___  
 \  /| |\/| | |   | |_) | \ \ / / _ \ '__| | |_) | '__/ _ \ 
 /  \| |  | | |___|  _ <| |\ V /  __/ |    |  __/| | | (_) |
/_/\_\_|  |_|_____|_| \_\_| \_/ \___|_|    |_|   |_|  \___/ 
```

**Professional Python client for XMLRiver API with full coverage**

*Fork of [KursHub-ru/xmlriver](https://github.com/KursHub-ru/xmlriver)*

[🚀 Quick Start](#-быстрый-старт) • [📚 Documentation](#-документация) • [🔧 Configuration](#-конфигурация) • [💡 Examples](#-примеры-использования)

</div>

---

## 🎯 О проекте

XMLRiver Pro — это **профессиональная** Python библиотека для работы с API xmlriver.com. Расширенная версия с поддержкой **всех типов поиска** в Google и Yandex.

### 📊 Сравнение с оригинальной библиотекой

| Функция | Оригинал | **XMLRiver Pro** |
|---------|----------|------------------|
| 🔍 Органический поиск | ✅ | ✅ **Улучшенный** |
| 📰 Новости | ✅ | ✅ **С фильтрами времени** |
| 🖼️ Изображения | ✅ | ✅ **Расширенные параметры** |
| 🗺️ Карты | ✅ | ✅ **С координатами** |
| 📢 Реклама | ✅ | ✅ **Верхние и нижние блоки** |
| 🧩 Специальные блоки | ❌ | ✅ **OneBox, Knowledge Graph** |
| ⚡ Асинхронность | ❌ | ✅ **Полная поддержка** |
| 🔄 Retry механизм | ❌ | ✅ **Экспоненциальный backoff** |
| 🛡️ Ограничение потоков | ❌ | ✅ **Максимум 10 одновременных** |
| 📊 Типизация | ❌ | ✅ **100% типизирован** |
| 🧪 Тесты | ❌ | ✅ **66 тестов, 57% покрытие** |

**Поддерживает все типы поиска:**
- 🔍 Органический поиск
- 📰 Новости с фильтрами времени  
- 🖼️ Изображения (размер, цвет, тип)
- 🗺️ Карты с координатами
- 📢 Рекламные блоки
- 🧩 Специальные блоки (OneBox, Knowledge Graph)
- ⚡ **Асинхронная поддержка** с ограничением потоков

## ✨ Ключевые особенности

- ⚡ **Асинхронная поддержка** с ограничением потоков (максимум 10)
- 🔄 **Retry механизм** с экспоненциальным backoff
- 🛡️ **Валидация параметров** и обработка ошибок
- 📊 **Форматирование результатов** поиска
- 🎯 **100% покрытие API** - все методы XMLRiver
- 🚀 **Высокая производительность** - оптимизированные запросы
- ✅ **Полная типизация** для Python 3.10+
- 🏛️ **Модульная архитектура** с четким разделением
- 🧪 **66 тестов** с покрытием 57%

## 📦 Установка

### 📦 **Из PyPI (рекомендуется):**
```bash
# Установка последней версии
pip install xmlriver-pro

# Установка конкретной версии
pip install xmlriver-pro==1.2.7
```

### 🔧 **Из исходного кода:**
```bash
git clone https://github.com/Eapwrk/xmlriver-pro.git
cd xmlriver-pro
pip install -e .
```

### 📋 **Зависимости:**
- Python 3.10+
- requests
- aiohttp (для асинхронных клиентов)
- xmltodict
- python-dotenv

## 🚀 Быстрый старт

### 🔑 Получение API ключей

1. Зарегистрируйтесь на [xmlriver.com](https://xmlriver.com)
2. Получите `user_id` и `api_key` в личном кабинете
3. Пополните баланс для использования API

### 🔧 Переменные окружения

Создайте файл `.env`:
```bash
XMLRIVER_USER_ID=your_user_id_here
XMLRIVER_API_KEY=your_api_key_here
```

### 📝 Базовые примеры

#### Асинхронный поиск
```python
import asyncio
from xmlriver_pro import AsyncGoogleClient, AsyncYandexClient

async def main():
    # Google асинхронный поиск
    async with AsyncGoogleClient(user_id=123, api_key="your_key") as google:
        results = await google.search("python programming")
        print(f"Найдено: {results.total_results} результатов")
    
    # Yandex асинхронный поиск
    async with AsyncYandexClient(user_id=123, api_key="your_key") as yandex:
        results = await yandex.search("программирование python")
        print(f"Найдено: {results.total_results} результатов")

asyncio.run(main())
```

#### Поиск новостей
```python
import asyncio
from xmlriver_pro import AsyncGoogleClient, AsyncYandexClient
from xmlriver_pro.core.types import TimeFilter

async def main():
    # Google новости
    async with AsyncGoogleClient(user_id=123, api_key="your_key") as google:
        results = await google.search_news("python", time_filter=TimeFilter.LAST_WEEK)
        for news in results.results:
            print(f"{news.title}: {news.url}")
    
    # Yandex новости
    async with AsyncYandexClient(user_id=123, api_key="your_key") as yandex:
        results = await yandex.search_news("python")
        for news in results.results:
            print(f"{news.title}: {news.url}")

asyncio.run(main())
```

#### Поиск изображений
```python
import asyncio
from xmlriver_pro import AsyncGoogleClient

async def main():
    async with AsyncGoogleClient(user_id=123, api_key="your_key") as google:
        results = await google.search_images("python logo", num_results=20)
        for image in results.results:
            print(f"{image.title}: {image.img_url}")

asyncio.run(main())
```

#### Поиск по картам
```python
import asyncio
from xmlriver_pro import AsyncGoogleClient

async def main():
    async with AsyncGoogleClient(user_id=123, api_key="your_key") as google:
        results = await google.search_maps(
            "python office",
            coords=(37.7749, -122.4194),
            zoom=12
        )
        for place in results.results:
            print(f"{place.title}: {place.address}")

asyncio.run(main())
```

#### Рекламные блоки
```python
import asyncio
from xmlriver_pro import AsyncGoogleClient, AsyncYandexClient

async def main():
    # Google реклама
    async with AsyncGoogleClient(user_id=123, api_key="your_key") as google:
        results = await google.get_ads("python programming")
        for ad in results.results:
            print(f"{ad.title}: {ad.url}")
    
    # Yandex реклама
    async with AsyncYandexClient(user_id=123, api_key="your_key") as yandex:
        results = await yandex.get_ads("программирование python")
        for ad in results.results:
            print(f"{ad.title}: {ad.url}")

asyncio.run(main())
```

## 🔧 Конфигурация

### Основные параметры

| Параметр | Описание | По умолчанию |
|----------|----------|--------------|
| `user_id` | ID пользователя XMLRiver | Обязательный |
| `api_key` | API ключ XMLRiver | Обязательный |
| `timeout` | Таймаут запроса (сек) | 60 |
| `retry_count` | Количество повторов | 3 |
| `retry_delay` | Задержка между повторами (сек) | 1.0 |
| `max_concurrent` | Максимум одновременных запросов | 10 |

### Пример конфигурации
```python
import asyncio
from xmlriver_pro import AsyncGoogleClient

async def main():
    async with AsyncGoogleClient(
        user_id=123,
        api_key="your_key",
        timeout=60,           # 60 секунд таймаут
        max_retries=3,        # 3 попытки
        retry_delay=1.0,      # 1 секунда между попытками
        max_concurrent=5      # 5 одновременных запросов
    ) as client:
        results = await client.search("python")
        print(f"Найдено: {results.total_results}")

asyncio.run(main())
```

### Переменные окружения
```python
import os
import asyncio
from dotenv import load_dotenv
from xmlriver_pro import AsyncGoogleClient

load_dotenv()

async def main():
    async with AsyncGoogleClient(
        user_id=int(os.getenv("XMLRIVER_USER_ID")),
        api_key=os.getenv("XMLRIVER_API_KEY")
    ) as client:
        results = await client.search("python")
        print(f"Найдено: {results.total_results}")

asyncio.run(main())
```

## 💡 Примеры использования

### Мониторинг потоков (асинхронные клиенты)
```python
async with AsyncGoogleClient(user_id=123, api_key="your_key") as client:
    # Проверяем статус потоков
    status = client.get_concurrent_status()
    print(f"Активных запросов: {status['active_requests']}")
    print(f"Доступных слотов: {status['available_slots']}")
```

### Основные валидаторы
```python
from xmlriver_pro.utils import validate_coords, validate_zoom, validate_url

# Валидация координат
coords = (55.7558, 37.6176)
if validate_coords(coords):
    print("Координаты валидны")

# Валидация zoom
if validate_zoom(12):
    print("Zoom валиден")

# Валидация URL
if validate_url("https://python.org"):
    print("URL валиден")
```

### Основные форматтеры
```python
from xmlriver_pro.utils import format_search_response, format_ads_response

# Форматирование результатов поиска
formatted_results = format_search_response(search_results)

# Форматирование рекламных блоков
formatted_ads = format_ads_response(ads_response)
```

## 📊 Типы данных

### Основные типы результатов

```python
from xmlriver_pro.core.types import (
    SearchResult, NewsResult, ImageResult, MapResult, 
    AdResult, AdsResponse, SearchResponse
)

# SearchResult - результат органического поиска
result = SearchResult(
    rank=1,
    url="https://python.org",
    title="Python Programming Language",
    snippet="Python is a programming language...",
    content_type="organic",
    stars=4.8
)

# NewsResult - результат поиска новостей
news = NewsResult(
    rank=1,
    url="https://news.example.com",
    title="Python News",
    snippet="Latest Python updates...",
    pub_date="2024-01-15"
)

# ImageResult - результат поиска изображений
image = ImageResult(
    rank=1,
    url="https://example.com/image.jpg",
    title="Python Logo",
    snippet="Official Python logo",
    image_url="https://example.com/logo.png",
    image_size="large"
)

# MapResult - результат поиска по картам
map_result = MapResult(
    rank=1,
    url="https://maps.google.com/...",
    title="Python Office",
    snippet="Python Software Foundation office",
    coords=(37.7749, -122.4194),
    address="San Francisco, CA"
)

# AdResult - рекламный результат
ad = AdResult(
    rank=1,
    url="https://ad.example.com",
    title="Python Course",
    snippet="Learn Python programming",
    ad_type="top"
)
```

### Перечисления (Enums)

```python
from xmlriver_pro.core.types import (
    SearchType, TimeFilter, DeviceType, OSType
)

# Типы поиска
search_type = SearchType.ORGANIC  # ORGANIC, NEWS, IMAGES, MAPS, ADS

# Фильтры времени для новостей
time_filter = TimeFilter.LAST_WEEK  # LAST_DAY, LAST_WEEK, LAST_MONTH, LAST_YEAR

# Типы устройств
device = DeviceType.DESKTOP  # DESKTOP, MOBILE, TABLET

# Операционные системы
os_type = OSType.WINDOWS  # WINDOWS, MACOS, LINUX, ANDROID, IOS
```

## ⚠️ Обработка ошибок

```python
import asyncio
import logging
from xmlriver_pro import AsyncGoogleClient
from xmlriver_pro.core import (
    XMLRiverError, AuthenticationError, RateLimitError, 
    NoResultsError, NetworkError, ValidationError,
    InsufficientFundsError, ServiceUnavailableError
)

logger = logging.getLogger(__name__)

async def main():
    async with AsyncGoogleClient(user_id=123, api_key="key") as google:
        try:
            results = await google.search("python")
        except AuthenticationError as e:
            # Ошибка аутентификации (коды 31, 42, 45)
            logger.error(f"Authentication failed: {e}")
        except RateLimitError as e:
            # Превышен лимит запросов (коды 110, 111, 115)
            logger.warning(f"Rate limit exceeded: {e}")
        except NoResultsError as e:
            # Нет результатов поиска (код 15)
            logger.info(f"No results found: {e}")
        except InsufficientFundsError as e:
            # Недостаточно средств (код 200)
            logger.error(f"Insufficient funds: {e}")
        except ServiceUnavailableError as e:
            # Сервис недоступен (коды 101, 201)
            logger.warning(f"Service unavailable: {e}")
        except NetworkError as e:
            # Ошибка сети (коды 500, 202) - требует повтора
            logger.error(f"Network error: {e}")
        except ValidationError as e:
            # Ошибка валидации параметров (коды 2, 102-108, 120, 121)
            logger.error(f"Validation error: {e}")

asyncio.run(main())
```

## 📊 Статистика и мониторинг

```python
import asyncio
from xmlriver_pro import AsyncGoogleClient, AsyncYandexClient

async def main():
    async with AsyncGoogleClient(user_id=123, api_key="your_key") as google:
        # Получение баланса (один на весь аккаунт)
        balance = await google.get_balance()
        print(f"Баланс: {balance} руб.")
        
        # Получение стоимости Google запросов
        google_cost = await google.get_cost()
        print(f"Стоимость Google: {google_cost} руб/1000 запросов")
    
    async with AsyncYandexClient(user_id=123, api_key="your_key") as yandex:
        # Получение стоимости Yandex запросов (разная для каждой системы)
        yandex_cost = await yandex.get_cost()
        print(f"Стоимость Yandex: {yandex_cost} руб/1000 запросов")

asyncio.run(main())
```

## ⚡ Ограничения API

### 🔢 **Потоки и производительность:**
- **Максимум потоков:** 10 для каждой системы (Google, Yandex, Wordstat)
- **Дневные лимиты:**
  - Google: ~200,000 запросов/сутки
  - Yandex: ~150,000 запросов/сутки
- **Скорость ответа:** 3-6 секунд (обычно), максимум 60 секунд

### ⏱️ **Рекомендации по таймаутам:**
```python
import asyncio
from xmlriver_pro import AsyncGoogleClient

async def main():
    # Используйте таймаут 60 секунд для надежности
    async with AsyncGoogleClient(user_id=123, api_key="key", timeout=60) as google:
        results = await google.search("python")
        # При низком таймауте есть риск потерять ответы
        # Деньги за запрос снимаются, но результат может не прийти

asyncio.run(main())
```

### 🚨 **Обработка ошибок потоков:**
```python
import asyncio
from xmlriver_pro import AsyncGoogleClient
from xmlriver_pro.core import RateLimitError

async def main():
    async with AsyncGoogleClient(user_id=123, api_key="key") as google:
        try:
            results = await google.search("python")
        except RateLimitError as e:
            if e.code in [110, 111, 115]:
                # Временные ошибки потоков - повторите запрос
                await asyncio.sleep(5)  # Подождите 5 секунд
                results = await google.search("python")  # Повторите

asyncio.run(main())
```

## 🧪 Тестирование

### ✅ Безопасное тестирование (БЕЗ реальных API запросов)

```bash
# Запуск всех тестов (БЕЗ real_api, по умолчанию)
pytest

# Запуск с покрытием
pytest --cov=xmlriver_pro

# Запуск конкретных тестов
pytest tests/test_google.py
pytest tests/test_yandex.py

# Запуск с детальным выводом
pytest -v
```

**✅ Эти команды БЕЗОПАСНЫ и НЕ требуют API ключей**

### ⚠️ Real API тесты (требуют ключи и СТОЯТ ДЕНЕГ!)

```bash
# ⚠️ ВНИМАНИЕ: следующие команды делают реальные API запросы и ТРАТЯТ ДЕНЬГИ!
pytest -m real_api -v  # Запуск ТОЛЬКО real_api тестов (~$5-10)
```

📖 **Полная документация по тестированию:** [TESTING.md](TESTING.md)
- Подробное описание всех типов тестов
- Инструкции по настройке Real API тестов
- Защитные механизмы и best practices

## 📚 Документация

- **[README.md](README.md)** - основная документация (этот файл)
- **[docs/README.md](docs/README.md)** - обзор всей документации
- **[docs/examples.md](docs/examples.md)** - детальные примеры всех методов
- **[docs/API_REFERENCE.md](docs/API_REFERENCE.md)** - полный справочник API
- **[docs/ADVANCED_USAGE.md](docs/ADVANCED_USAGE.md)** - продвинутые сценарии
- **[docs/SPECIAL_BLOCKS_GUIDE.md](docs/SPECIAL_BLOCKS_GUIDE.md)** - специальные блоки
- **[docs/VALIDATORS_REFERENCE.md](docs/VALIDATORS_REFERENCE.md)** - все валидаторы
- **[docs/FORMATTERS_REFERENCE.md](docs/FORMATTERS_REFERENCE.md)** - все форматтеры
- **[docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md)** - решение проблем
- **[Исходный код](https://github.com/Eapwrk/xmlriver-pro)** - полный код с комментариями

## 🤝 Вклад в проект

Issues и Pull Requests приветствуются на [GitHub](https://github.com/Eapwrk/xmlriver-pro).

### Установка для разработки

```bash
git clone https://github.com/Eapwrk/xmlriver-pro.git
cd xmlriver-pro
pip install -e ".[dev]"
pre-commit install
```

### Запуск тестов

```bash
pytest
```

## 📄 Лицензия

MIT License. Подробности в [LICENSE](LICENSE).

## 🙏 Благодарности

- [xmlriver.com](https://xmlriver.com) за предоставление API
- Python сообществу за экосистему
- Контрибьюторам проекта

## 📞 Поддержка

- 📧 Email: seo@controlseo.ru
- 🐛 Issues: [GitHub Issues](https://github.com/Eapwrk/xmlriver-pro/issues)
- 💬 Discussions: [GitHub Discussions](https://github.com/Eapwrk/xmlriver-pro/discussions)

---

## 📈 Статистика проекта

<div align="center">

![GitHub stars](https://img.shields.io/github/stars/Eapwrk/xmlriver-pro?style=social&label=Stars)
![GitHub forks](https://img.shields.io/github/forks/Eapwrk/xmlriver-pro?style=social&label=Forks)
![GitHub watchers](https://img.shields.io/github/watchers/Eapwrk/xmlriver-pro?style=social&label=Watchers)

**XMLRiver Pro** - Professional Python client for XMLRiver API

</div>

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/xmlriver-pro/xmlriver-pro",
    "name": "xmlriver-pro",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.10",
    "maintainer_email": null,
    "keywords": "xmlriver, google, yandex, search, api, seo, scraping, search-engine, news, images, maps, ads",
    "author": "XMLRiver Pro Team",
    "author_email": "XMLRiver Pro Team <seo@controlseo.ru>",
    "download_url": "https://files.pythonhosted.org/packages/fc/54/fac92e2f5349a255d91660abc38624adc515206f59d35d4261e3e4b61194/xmlriver_pro-2.0.0.tar.gz",
    "platform": null,
    "description": "# XMLRiver Pro\r\n\r\n<div align=\"center\">\r\n\r\n[![GitHub stars](https://img.shields.io/github/stars/Eapwrk/xmlriver-pro?style=social)](https://github.com/Eapwrk/xmlriver-pro)\r\n[![Star this repo](https://img.shields.io/badge/\u2b50-Star%20this%20repo-yellow?style=social)](https://github.com/Eapwrk/xmlriver-pro)\r\n[![Fork this repo](https://img.shields.io/badge/\ud83c\udf74-Fork%20this%20repo-blue?style=social)](https://github.com/Eapwrk/xmlriver-pro/fork)\r\n[![Watch this repo](https://img.shields.io/badge/\ud83d\udc41-Watch%20this%20repo-green?style=social)](https://github.com/Eapwrk/xmlriver-pro/subscription)\r\n[![Sponsor this repo](https://img.shields.io/badge/\ud83d\udc96-Sponsor%20this%20repo-pink?style=social)](https://github.com/sponsors/Eapwrk)\r\n[![Report Issues](https://img.shields.io/badge/\ud83d\udc1b-Report%20Issues-red?style=social)](https://github.com/Eapwrk/xmlriver-pro/issues)\r\n[![Join Discussions](https://img.shields.io/badge/\ud83d\udcac-Join%20Discussions-purple?style=social)](https://github.com/Eapwrk/xmlriver-pro/discussions)\r\n[![Pull Requests](https://img.shields.io/badge/\ud83d\udd00-Pull%20Requests-orange?style=social)](https://github.com/Eapwrk/xmlriver-pro/pulls)\r\n[![Wiki](https://img.shields.io/badge/\ud83d\udcda-Wiki-teal?style=social)](https://github.com/Eapwrk/xmlriver-pro/wiki)\r\n[![Actions](https://img.shields.io/badge/\u2699\ufe0f-Actions-gray?style=social)](https://github.com/Eapwrk/xmlriver-pro/actions)\r\n[![Releases](https://img.shields.io/badge/\ud83d\ude80-Releases-cyan?style=social)](https://github.com/Eapwrk/xmlriver-pro/releases)\r\n[![Security](https://img.shields.io/badge/\ud83d\udd12-Security-indigo?style=social)](https://github.com/Eapwrk/xmlriver-pro/security)\r\n[![Insights](https://img.shields.io/badge/\ud83d\udcca-Insights-lime?style=social)](https://github.com/Eapwrk/xmlriver-pro/pulse)\r\n[![Settings](https://img.shields.io/badge/\u2699\ufe0f-Settings-slate?style=social)](https://github.com/Eapwrk/xmlriver-pro/settings)\r\n[![Code](https://img.shields.io/badge/\ud83d\udcbb-Code-emerald?style=social)](https://github.com/Eapwrk/xmlriver-pro)\r\n[![Issues](https://img.shields.io/badge/\ud83d\udc1b-Issues-red?style=social)](https://github.com/Eapwrk/xmlriver-pro/issues)\r\n[![Pull Requests](https://img.shields.io/badge/\ud83d\udd00-Pull%20Requests-orange?style=social)](https://github.com/Eapwrk/xmlriver-pro/pulls)\r\n[![Discussions](https://img.shields.io/badge/\ud83d\udcac-Discussions-purple?style=social)](https://github.com/Eapwrk/xmlriver-pro/discussions)\r\n[![Wiki](https://img.shields.io/badge/\ud83d\udcda-Wiki-teal?style=social)](https://github.com/Eapwrk/xmlriver-pro/wiki)\r\n[![Actions](https://img.shields.io/badge/\u2699\ufe0f-Actions-gray?style=social)](https://github.com/Eapwrk/xmlriver-pro/actions)\r\n[![PyPI version](https://img.shields.io/pypi/v/xmlriver-pro?color=blue)](https://pypi.org/project/xmlriver-pro/)\r\n[![Python Version](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://python.org)\r\n[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)\r\n[![Downloads](https://img.shields.io/pypi/dm/xmlriver-pro?color=orange)](https://pypi.org/project/xmlriver-pro/)\r\n[![Coverage](https://img.shields.io/badge/coverage-57%25-brightgreen.svg)](https://github.com/Eapwrk/xmlriver-pro)\r\n[![GitHub last commit](https://img.shields.io/github/last-commit/Eapwrk/xmlriver-pro?color=blue)](https://github.com/Eapwrk/xmlriver-pro)\r\n[![GitHub issues](https://img.shields.io/badge/github-issues-red?style=social)](https://github.com/Eapwrk/xmlriver-pro/issues)\r\n\r\n```\r\n__  ____  __ _     ____  _                  ____            \r\n\\ \\/ /  \\/  | |   |  _ \\(_)_   _____ _ __  |  _ \\ _ __ ___  \r\n \\  /| |\\/| | |   | |_) | \\ \\ / / _ \\ '__| | |_) | '__/ _ \\ \r\n /  \\| |  | | |___|  _ <| |\\ V /  __/ |    |  __/| | | (_) |\r\n/_/\\_\\_|  |_|_____|_| \\_\\_| \\_/ \\___|_|    |_|   |_|  \\___/ \r\n```\r\n\r\n**Professional Python client for XMLRiver API with full coverage**\r\n\r\n*Fork of [KursHub-ru/xmlriver](https://github.com/KursHub-ru/xmlriver)*\r\n\r\n[\ud83d\ude80 Quick Start](#-\u0431\u044b\u0441\u0442\u0440\u044b\u0439-\u0441\u0442\u0430\u0440\u0442) \u2022 [\ud83d\udcda Documentation](#-\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f) \u2022 [\ud83d\udd27 Configuration](#-\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f) \u2022 [\ud83d\udca1 Examples](#-\u043f\u0440\u0438\u043c\u0435\u0440\u044b-\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f)\r\n\r\n</div>\r\n\r\n---\r\n\r\n## \ud83c\udfaf \u041e \u043f\u0440\u043e\u0435\u043a\u0442\u0435\r\n\r\nXMLRiver Pro \u2014 \u044d\u0442\u043e **\u043f\u0440\u043e\u0444\u0435\u0441\u0441\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u0430\u044f** Python \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 API xmlriver.com. \u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f \u0441 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u043e\u0439 **\u0432\u0441\u0435\u0445 \u0442\u0438\u043f\u043e\u0432 \u043f\u043e\u0438\u0441\u043a\u0430** \u0432 Google \u0438 Yandex.\r\n\r\n### \ud83d\udcca \u0421\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0435 \u0441 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u043e\u0439\r\n\r\n| \u0424\u0443\u043d\u043a\u0446\u0438\u044f | \u041e\u0440\u0438\u0433\u0438\u043d\u0430\u043b | **XMLRiver Pro** |\r\n|---------|----------|------------------|\r\n| \ud83d\udd0d \u041e\u0440\u0433\u0430\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u043e\u0438\u0441\u043a | \u2705 | \u2705 **\u0423\u043b\u0443\u0447\u0448\u0435\u043d\u043d\u044b\u0439** |\r\n| \ud83d\udcf0 \u041d\u043e\u0432\u043e\u0441\u0442\u0438 | \u2705 | \u2705 **\u0421 \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u043c\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0438** |\r\n| \ud83d\uddbc\ufe0f \u0418\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f | \u2705 | \u2705 **\u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b** |\r\n| \ud83d\uddfa\ufe0f \u041a\u0430\u0440\u0442\u044b | \u2705 | \u2705 **\u0421 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0430\u043c\u0438** |\r\n| \ud83d\udce2 \u0420\u0435\u043a\u043b\u0430\u043c\u0430 | \u2705 | \u2705 **\u0412\u0435\u0440\u0445\u043d\u0438\u0435 \u0438 \u043d\u0438\u0436\u043d\u0438\u0435 \u0431\u043b\u043e\u043a\u0438** |\r\n| \ud83e\udde9 \u0421\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0435 \u0431\u043b\u043e\u043a\u0438 | \u274c | \u2705 **OneBox, Knowledge Graph** |\r\n| \u26a1 \u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0441\u0442\u044c | \u274c | \u2705 **\u041f\u043e\u043b\u043d\u0430\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430** |\r\n| \ud83d\udd04 Retry \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c | \u274c | \u2705 **\u042d\u043a\u0441\u043f\u043e\u043d\u0435\u043d\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 backoff** |\r\n| \ud83d\udee1\ufe0f \u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u043f\u043e\u0442\u043e\u043a\u043e\u0432 | \u274c | \u2705 **\u041c\u0430\u043a\u0441\u0438\u043c\u0443\u043c 10 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445** |\r\n| \ud83d\udcca \u0422\u0438\u043f\u0438\u0437\u0430\u0446\u0438\u044f | \u274c | \u2705 **100% \u0442\u0438\u043f\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d** |\r\n| \ud83e\uddea \u0422\u0435\u0441\u0442\u044b | \u274c | \u2705 **66 \u0442\u0435\u0441\u0442\u043e\u0432, 57% \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u0435** |\r\n\r\n**\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0432\u0441\u0435 \u0442\u0438\u043f\u044b \u043f\u043e\u0438\u0441\u043a\u0430:**\r\n- \ud83d\udd0d \u041e\u0440\u0433\u0430\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u043e\u0438\u0441\u043a\r\n- \ud83d\udcf0 \u041d\u043e\u0432\u043e\u0441\u0442\u0438 \u0441 \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u043c\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0438  \r\n- \ud83d\uddbc\ufe0f \u0418\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f (\u0440\u0430\u0437\u043c\u0435\u0440, \u0446\u0432\u0435\u0442, \u0442\u0438\u043f)\r\n- \ud83d\uddfa\ufe0f \u041a\u0430\u0440\u0442\u044b \u0441 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0430\u043c\u0438\r\n- \ud83d\udce2 \u0420\u0435\u043a\u043b\u0430\u043c\u043d\u044b\u0435 \u0431\u043b\u043e\u043a\u0438\r\n- \ud83e\udde9 \u0421\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0435 \u0431\u043b\u043e\u043a\u0438 (OneBox, Knowledge Graph)\r\n- \u26a1 **\u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u0430\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430** \u0441 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435\u043c \u043f\u043e\u0442\u043e\u043a\u043e\u0432\r\n\r\n## \u2728 \u041a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438\r\n\r\n- \u26a1 **\u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u0430\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430** \u0441 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435\u043c \u043f\u043e\u0442\u043e\u043a\u043e\u0432 (\u043c\u0430\u043a\u0441\u0438\u043c\u0443\u043c 10)\r\n- \ud83d\udd04 **Retry \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c** \u0441 \u044d\u043a\u0441\u043f\u043e\u043d\u0435\u043d\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u043c backoff\r\n- \ud83d\udee1\ufe0f **\u0412\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432** \u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043e\u0448\u0438\u0431\u043e\u043a\r\n- \ud83d\udcca **\u0424\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432** \u043f\u043e\u0438\u0441\u043a\u0430\r\n- \ud83c\udfaf **100% \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u0435 API** - \u0432\u0441\u0435 \u043c\u0435\u0442\u043e\u0434\u044b XMLRiver\r\n- \ud83d\ude80 **\u0412\u044b\u0441\u043e\u043a\u0430\u044f \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c** - \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b\r\n- \u2705 **\u041f\u043e\u043b\u043d\u0430\u044f \u0442\u0438\u043f\u0438\u0437\u0430\u0446\u0438\u044f** \u0434\u043b\u044f Python 3.10+\r\n- \ud83c\udfdb\ufe0f **\u041c\u043e\u0434\u0443\u043b\u044c\u043d\u0430\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430** \u0441 \u0447\u0435\u0442\u043a\u0438\u043c \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435\u043c\r\n- \ud83e\uddea **66 \u0442\u0435\u0441\u0442\u043e\u0432** \u0441 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u0435\u043c 57%\r\n\r\n## \ud83d\udce6 \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430\r\n\r\n### \ud83d\udce6 **\u0418\u0437 PyPI (\u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f):**\r\n```bash\r\n# \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0439 \u0432\u0435\u0440\u0441\u0438\u0438\r\npip install xmlriver-pro\r\n\r\n# \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438\r\npip install xmlriver-pro==1.2.7\r\n```\r\n\r\n### \ud83d\udd27 **\u0418\u0437 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430:**\r\n```bash\r\ngit clone https://github.com/Eapwrk/xmlriver-pro.git\r\ncd xmlriver-pro\r\npip install -e .\r\n```\r\n\r\n### \ud83d\udccb **\u0417\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438:**\r\n- Python 3.10+\r\n- requests\r\n- aiohttp (\u0434\u043b\u044f \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0445 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432)\r\n- xmltodict\r\n- python-dotenv\r\n\r\n## \ud83d\ude80 \u0411\u044b\u0441\u0442\u0440\u044b\u0439 \u0441\u0442\u0430\u0440\u0442\r\n\r\n### \ud83d\udd11 \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 API \u043a\u043b\u044e\u0447\u0435\u0439\r\n\r\n1. \u0417\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0439\u0442\u0435\u0441\u044c \u043d\u0430 [xmlriver.com](https://xmlriver.com)\r\n2. \u041f\u043e\u043b\u0443\u0447\u0438\u0442\u0435 `user_id` \u0438 `api_key` \u0432 \u043b\u0438\u0447\u043d\u043e\u043c \u043a\u0430\u0431\u0438\u043d\u0435\u0442\u0435\r\n3. \u041f\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435 \u0431\u0430\u043b\u0430\u043d\u0441 \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f API\r\n\r\n### \ud83d\udd27 \u041f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f\r\n\r\n\u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0444\u0430\u0439\u043b `.env`:\r\n```bash\r\nXMLRIVER_USER_ID=your_user_id_here\r\nXMLRIVER_API_KEY=your_api_key_here\r\n```\r\n\r\n### \ud83d\udcdd \u0411\u0430\u0437\u043e\u0432\u044b\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u044b\r\n\r\n#### \u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043f\u043e\u0438\u0441\u043a\r\n```python\r\nimport asyncio\r\nfrom xmlriver_pro import AsyncGoogleClient, AsyncYandexClient\r\n\r\nasync def main():\r\n    # Google \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043f\u043e\u0438\u0441\u043a\r\n    async with AsyncGoogleClient(user_id=123, api_key=\"your_key\") as google:\r\n        results = await google.search(\"python programming\")\r\n        print(f\"\u041d\u0430\u0439\u0434\u0435\u043d\u043e: {results.total_results} \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432\")\r\n    \r\n    # Yandex \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043f\u043e\u0438\u0441\u043a\r\n    async with AsyncYandexClient(user_id=123, api_key=\"your_key\") as yandex:\r\n        results = await yandex.search(\"\u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 python\")\r\n        print(f\"\u041d\u0430\u0439\u0434\u0435\u043d\u043e: {results.total_results} \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432\")\r\n\r\nasyncio.run(main())\r\n```\r\n\r\n#### \u041f\u043e\u0438\u0441\u043a \u043d\u043e\u0432\u043e\u0441\u0442\u0435\u0439\r\n```python\r\nimport asyncio\r\nfrom xmlriver_pro import AsyncGoogleClient, AsyncYandexClient\r\nfrom xmlriver_pro.core.types import TimeFilter\r\n\r\nasync def main():\r\n    # Google \u043d\u043e\u0432\u043e\u0441\u0442\u0438\r\n    async with AsyncGoogleClient(user_id=123, api_key=\"your_key\") as google:\r\n        results = await google.search_news(\"python\", time_filter=TimeFilter.LAST_WEEK)\r\n        for news in results.results:\r\n            print(f\"{news.title}: {news.url}\")\r\n    \r\n    # Yandex \u043d\u043e\u0432\u043e\u0441\u0442\u0438\r\n    async with AsyncYandexClient(user_id=123, api_key=\"your_key\") as yandex:\r\n        results = await yandex.search_news(\"python\")\r\n        for news in results.results:\r\n            print(f\"{news.title}: {news.url}\")\r\n\r\nasyncio.run(main())\r\n```\r\n\r\n#### \u041f\u043e\u0438\u0441\u043a \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439\r\n```python\r\nimport asyncio\r\nfrom xmlriver_pro import AsyncGoogleClient\r\n\r\nasync def main():\r\n    async with AsyncGoogleClient(user_id=123, api_key=\"your_key\") as google:\r\n        results = await google.search_images(\"python logo\", num_results=20)\r\n        for image in results.results:\r\n            print(f\"{image.title}: {image.img_url}\")\r\n\r\nasyncio.run(main())\r\n```\r\n\r\n#### \u041f\u043e\u0438\u0441\u043a \u043f\u043e \u043a\u0430\u0440\u0442\u0430\u043c\r\n```python\r\nimport asyncio\r\nfrom xmlriver_pro import AsyncGoogleClient\r\n\r\nasync def main():\r\n    async with AsyncGoogleClient(user_id=123, api_key=\"your_key\") as google:\r\n        results = await google.search_maps(\r\n            \"python office\",\r\n            coords=(37.7749, -122.4194),\r\n            zoom=12\r\n        )\r\n        for place in results.results:\r\n            print(f\"{place.title}: {place.address}\")\r\n\r\nasyncio.run(main())\r\n```\r\n\r\n#### \u0420\u0435\u043a\u043b\u0430\u043c\u043d\u044b\u0435 \u0431\u043b\u043e\u043a\u0438\r\n```python\r\nimport asyncio\r\nfrom xmlriver_pro import AsyncGoogleClient, AsyncYandexClient\r\n\r\nasync def main():\r\n    # Google \u0440\u0435\u043a\u043b\u0430\u043c\u0430\r\n    async with AsyncGoogleClient(user_id=123, api_key=\"your_key\") as google:\r\n        results = await google.get_ads(\"python programming\")\r\n        for ad in results.results:\r\n            print(f\"{ad.title}: {ad.url}\")\r\n    \r\n    # Yandex \u0440\u0435\u043a\u043b\u0430\u043c\u0430\r\n    async with AsyncYandexClient(user_id=123, api_key=\"your_key\") as yandex:\r\n        results = await yandex.get_ads(\"\u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 python\")\r\n        for ad in results.results:\r\n            print(f\"{ad.title}: {ad.url}\")\r\n\r\nasyncio.run(main())\r\n```\r\n\r\n## \ud83d\udd27 \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\r\n\r\n### \u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b\r\n\r\n| \u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 | \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 | \u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e |\r\n|----------|----------|--------------|\r\n| `user_id` | ID \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f XMLRiver | \u041e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 |\r\n| `api_key` | API \u043a\u043b\u044e\u0447 XMLRiver | \u041e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 |\r\n| `timeout` | \u0422\u0430\u0439\u043c\u0430\u0443\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 (\u0441\u0435\u043a) | 60 |\r\n| `retry_count` | \u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u043e\u0432\u0442\u043e\u0440\u043e\u0432 | 3 |\r\n| `retry_delay` | \u0417\u0430\u0434\u0435\u0440\u0436\u043a\u0430 \u043c\u0435\u0436\u0434\u0443 \u043f\u043e\u0432\u0442\u043e\u0440\u0430\u043c\u0438 (\u0441\u0435\u043a) | 1.0 |\r\n| `max_concurrent` | \u041c\u0430\u043a\u0441\u0438\u043c\u0443\u043c \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 | 10 |\r\n\r\n### \u041f\u0440\u0438\u043c\u0435\u0440 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438\r\n```python\r\nimport asyncio\r\nfrom xmlriver_pro import AsyncGoogleClient\r\n\r\nasync def main():\r\n    async with AsyncGoogleClient(\r\n        user_id=123,\r\n        api_key=\"your_key\",\r\n        timeout=60,           # 60 \u0441\u0435\u043a\u0443\u043d\u0434 \u0442\u0430\u0439\u043c\u0430\u0443\u0442\r\n        max_retries=3,        # 3 \u043f\u043e\u043f\u044b\u0442\u043a\u0438\r\n        retry_delay=1.0,      # 1 \u0441\u0435\u043a\u0443\u043d\u0434\u0430 \u043c\u0435\u0436\u0434\u0443 \u043f\u043e\u043f\u044b\u0442\u043a\u0430\u043c\u0438\r\n        max_concurrent=5      # 5 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432\r\n    ) as client:\r\n        results = await client.search(\"python\")\r\n        print(f\"\u041d\u0430\u0439\u0434\u0435\u043d\u043e: {results.total_results}\")\r\n\r\nasyncio.run(main())\r\n```\r\n\r\n### \u041f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f\r\n```python\r\nimport os\r\nimport asyncio\r\nfrom dotenv import load_dotenv\r\nfrom xmlriver_pro import AsyncGoogleClient\r\n\r\nload_dotenv()\r\n\r\nasync def main():\r\n    async with AsyncGoogleClient(\r\n        user_id=int(os.getenv(\"XMLRIVER_USER_ID\")),\r\n        api_key=os.getenv(\"XMLRIVER_API_KEY\")\r\n    ) as client:\r\n        results = await client.search(\"python\")\r\n        print(f\"\u041d\u0430\u0439\u0434\u0435\u043d\u043e: {results.total_results}\")\r\n\r\nasyncio.run(main())\r\n```\r\n\r\n## \ud83d\udca1 \u041f\u0440\u0438\u043c\u0435\u0440\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f\r\n\r\n### \u041c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433 \u043f\u043e\u0442\u043e\u043a\u043e\u0432 (\u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u044b)\r\n```python\r\nasync with AsyncGoogleClient(user_id=123, api_key=\"your_key\") as client:\r\n    # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0441\u0442\u0430\u0442\u0443\u0441 \u043f\u043e\u0442\u043e\u043a\u043e\u0432\r\n    status = client.get_concurrent_status()\r\n    print(f\"\u0410\u043a\u0442\u0438\u0432\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432: {status['active_requests']}\")\r\n    print(f\"\u0414\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0445 \u0441\u043b\u043e\u0442\u043e\u0432: {status['available_slots']}\")\r\n```\r\n\r\n### \u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440\u044b\r\n```python\r\nfrom xmlriver_pro.utils import validate_coords, validate_zoom, validate_url\r\n\r\n# \u0412\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\r\ncoords = (55.7558, 37.6176)\r\nif validate_coords(coords):\r\n    print(\"\u041a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0432\u0430\u043b\u0438\u0434\u043d\u044b\")\r\n\r\n# \u0412\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f zoom\r\nif validate_zoom(12):\r\n    print(\"Zoom \u0432\u0430\u043b\u0438\u0434\u0435\u043d\")\r\n\r\n# \u0412\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f URL\r\nif validate_url(\"https://python.org\"):\r\n    print(\"URL \u0432\u0430\u043b\u0438\u0434\u0435\u043d\")\r\n```\r\n\r\n### \u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0444\u043e\u0440\u043c\u0430\u0442\u0442\u0435\u0440\u044b\r\n```python\r\nfrom xmlriver_pro.utils import format_search_response, format_ads_response\r\n\r\n# \u0424\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u043f\u043e\u0438\u0441\u043a\u0430\r\nformatted_results = format_search_response(search_results)\r\n\r\n# \u0424\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0440\u0435\u043a\u043b\u0430\u043c\u043d\u044b\u0445 \u0431\u043b\u043e\u043a\u043e\u0432\r\nformatted_ads = format_ads_response(ads_response)\r\n```\r\n\r\n## \ud83d\udcca \u0422\u0438\u043f\u044b \u0434\u0430\u043d\u043d\u044b\u0445\r\n\r\n### \u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0442\u0438\u043f\u044b \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432\r\n\r\n```python\r\nfrom xmlriver_pro.core.types import (\r\n    SearchResult, NewsResult, ImageResult, MapResult, \r\n    AdResult, AdsResponse, SearchResponse\r\n)\r\n\r\n# SearchResult - \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043e\u0440\u0433\u0430\u043d\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430\r\nresult = SearchResult(\r\n    rank=1,\r\n    url=\"https://python.org\",\r\n    title=\"Python Programming Language\",\r\n    snippet=\"Python is a programming language...\",\r\n    content_type=\"organic\",\r\n    stars=4.8\r\n)\r\n\r\n# NewsResult - \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043f\u043e\u0438\u0441\u043a\u0430 \u043d\u043e\u0432\u043e\u0441\u0442\u0435\u0439\r\nnews = NewsResult(\r\n    rank=1,\r\n    url=\"https://news.example.com\",\r\n    title=\"Python News\",\r\n    snippet=\"Latest Python updates...\",\r\n    pub_date=\"2024-01-15\"\r\n)\r\n\r\n# ImageResult - \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043f\u043e\u0438\u0441\u043a\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439\r\nimage = ImageResult(\r\n    rank=1,\r\n    url=\"https://example.com/image.jpg\",\r\n    title=\"Python Logo\",\r\n    snippet=\"Official Python logo\",\r\n    image_url=\"https://example.com/logo.png\",\r\n    image_size=\"large\"\r\n)\r\n\r\n# MapResult - \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043f\u043e\u0438\u0441\u043a\u0430 \u043f\u043e \u043a\u0430\u0440\u0442\u0430\u043c\r\nmap_result = MapResult(\r\n    rank=1,\r\n    url=\"https://maps.google.com/...\",\r\n    title=\"Python Office\",\r\n    snippet=\"Python Software Foundation office\",\r\n    coords=(37.7749, -122.4194),\r\n    address=\"San Francisco, CA\"\r\n)\r\n\r\n# AdResult - \u0440\u0435\u043a\u043b\u0430\u043c\u043d\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\r\nad = AdResult(\r\n    rank=1,\r\n    url=\"https://ad.example.com\",\r\n    title=\"Python Course\",\r\n    snippet=\"Learn Python programming\",\r\n    ad_type=\"top\"\r\n)\r\n```\r\n\r\n### \u041f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f (Enums)\r\n\r\n```python\r\nfrom xmlriver_pro.core.types import (\r\n    SearchType, TimeFilter, DeviceType, OSType\r\n)\r\n\r\n# \u0422\u0438\u043f\u044b \u043f\u043e\u0438\u0441\u043a\u0430\r\nsearch_type = SearchType.ORGANIC  # ORGANIC, NEWS, IMAGES, MAPS, ADS\r\n\r\n# \u0424\u0438\u043b\u044c\u0442\u0440\u044b \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0434\u043b\u044f \u043d\u043e\u0432\u043e\u0441\u0442\u0435\u0439\r\ntime_filter = TimeFilter.LAST_WEEK  # LAST_DAY, LAST_WEEK, LAST_MONTH, LAST_YEAR\r\n\r\n# \u0422\u0438\u043f\u044b \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\r\ndevice = DeviceType.DESKTOP  # DESKTOP, MOBILE, TABLET\r\n\r\n# \u041e\u043f\u0435\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b\r\nos_type = OSType.WINDOWS  # WINDOWS, MACOS, LINUX, ANDROID, IOS\r\n```\r\n\r\n## \u26a0\ufe0f \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043e\u0448\u0438\u0431\u043e\u043a\r\n\r\n```python\r\nimport asyncio\r\nimport logging\r\nfrom xmlriver_pro import AsyncGoogleClient\r\nfrom xmlriver_pro.core import (\r\n    XMLRiverError, AuthenticationError, RateLimitError, \r\n    NoResultsError, NetworkError, ValidationError,\r\n    InsufficientFundsError, ServiceUnavailableError\r\n)\r\n\r\nlogger = logging.getLogger(__name__)\r\n\r\nasync def main():\r\n    async with AsyncGoogleClient(user_id=123, api_key=\"key\") as google:\r\n        try:\r\n            results = await google.search(\"python\")\r\n        except AuthenticationError as e:\r\n            # \u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 (\u043a\u043e\u0434\u044b 31, 42, 45)\r\n            logger.error(f\"Authentication failed: {e}\")\r\n        except RateLimitError as e:\r\n            # \u041f\u0440\u0435\u0432\u044b\u0448\u0435\u043d \u043b\u0438\u043c\u0438\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 (\u043a\u043e\u0434\u044b 110, 111, 115)\r\n            logger.warning(f\"Rate limit exceeded: {e}\")\r\n        except NoResultsError as e:\r\n            # \u041d\u0435\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u043f\u043e\u0438\u0441\u043a\u0430 (\u043a\u043e\u0434 15)\r\n            logger.info(f\"No results found: {e}\")\r\n        except InsufficientFundsError as e:\r\n            # \u041d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0441\u0440\u0435\u0434\u0441\u0442\u0432 (\u043a\u043e\u0434 200)\r\n            logger.error(f\"Insufficient funds: {e}\")\r\n        except ServiceUnavailableError as e:\r\n            # \u0421\u0435\u0440\u0432\u0438\u0441 \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d (\u043a\u043e\u0434\u044b 101, 201)\r\n            logger.warning(f\"Service unavailable: {e}\")\r\n        except NetworkError as e:\r\n            # \u041e\u0448\u0438\u0431\u043a\u0430 \u0441\u0435\u0442\u0438 (\u043a\u043e\u0434\u044b 500, 202) - \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043f\u043e\u0432\u0442\u043e\u0440\u0430\r\n            logger.error(f\"Network error: {e}\")\r\n        except ValidationError as e:\r\n            # \u041e\u0448\u0438\u0431\u043a\u0430 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 (\u043a\u043e\u0434\u044b 2, 102-108, 120, 121)\r\n            logger.error(f\"Validation error: {e}\")\r\n\r\nasyncio.run(main())\r\n```\r\n\r\n## \ud83d\udcca \u0421\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0430 \u0438 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\r\n\r\n```python\r\nimport asyncio\r\nfrom xmlriver_pro import AsyncGoogleClient, AsyncYandexClient\r\n\r\nasync def main():\r\n    async with AsyncGoogleClient(user_id=123, api_key=\"your_key\") as google:\r\n        # \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0431\u0430\u043b\u0430\u043d\u0441\u0430 (\u043e\u0434\u0438\u043d \u043d\u0430 \u0432\u0435\u0441\u044c \u0430\u043a\u043a\u0430\u0443\u043d\u0442)\r\n        balance = await google.get_balance()\r\n        print(f\"\u0411\u0430\u043b\u0430\u043d\u0441: {balance} \u0440\u0443\u0431.\")\r\n        \r\n        # \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u0438 Google \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432\r\n        google_cost = await google.get_cost()\r\n        print(f\"\u0421\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u044c Google: {google_cost} \u0440\u0443\u0431/1000 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432\")\r\n    \r\n    async with AsyncYandexClient(user_id=123, api_key=\"your_key\") as yandex:\r\n        # \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0441\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u0438 Yandex \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 (\u0440\u0430\u0437\u043d\u0430\u044f \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b)\r\n        yandex_cost = await yandex.get_cost()\r\n        print(f\"\u0421\u0442\u043e\u0438\u043c\u043e\u0441\u0442\u044c Yandex: {yandex_cost} \u0440\u0443\u0431/1000 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432\")\r\n\r\nasyncio.run(main())\r\n```\r\n\r\n## \u26a1 \u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f API\r\n\r\n### \ud83d\udd22 **\u041f\u043e\u0442\u043e\u043a\u0438 \u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c:**\r\n- **\u041c\u0430\u043a\u0441\u0438\u043c\u0443\u043c \u043f\u043e\u0442\u043e\u043a\u043e\u0432:** 10 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b (Google, Yandex, Wordstat)\r\n- **\u0414\u043d\u0435\u0432\u043d\u044b\u0435 \u043b\u0438\u043c\u0438\u0442\u044b:**\r\n  - Google: ~200,000 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432/\u0441\u0443\u0442\u043a\u0438\r\n  - Yandex: ~150,000 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432/\u0441\u0443\u0442\u043a\u0438\r\n- **\u0421\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u043e\u0442\u0432\u0435\u0442\u0430:** 3-6 \u0441\u0435\u043a\u0443\u043d\u0434 (\u043e\u0431\u044b\u0447\u043d\u043e), \u043c\u0430\u043a\u0441\u0438\u043c\u0443\u043c 60 \u0441\u0435\u043a\u0443\u043d\u0434\r\n\r\n### \u23f1\ufe0f **\u0420\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0430\u0446\u0438\u0438 \u043f\u043e \u0442\u0430\u0439\u043c\u0430\u0443\u0442\u0430\u043c:**\r\n```python\r\nimport asyncio\r\nfrom xmlriver_pro import AsyncGoogleClient\r\n\r\nasync def main():\r\n    # \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u0442\u0430\u0439\u043c\u0430\u0443\u0442 60 \u0441\u0435\u043a\u0443\u043d\u0434 \u0434\u043b\u044f \u043d\u0430\u0434\u0435\u0436\u043d\u043e\u0441\u0442\u0438\r\n    async with AsyncGoogleClient(user_id=123, api_key=\"key\", timeout=60) as google:\r\n        results = await google.search(\"python\")\r\n        # \u041f\u0440\u0438 \u043d\u0438\u0437\u043a\u043e\u043c \u0442\u0430\u0439\u043c\u0430\u0443\u0442\u0435 \u0435\u0441\u0442\u044c \u0440\u0438\u0441\u043a \u043f\u043e\u0442\u0435\u0440\u044f\u0442\u044c \u043e\u0442\u0432\u0435\u0442\u044b\r\n        # \u0414\u0435\u043d\u044c\u0433\u0438 \u0437\u0430 \u0437\u0430\u043f\u0440\u043e\u0441 \u0441\u043d\u0438\u043c\u0430\u044e\u0442\u0441\u044f, \u043d\u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043c\u043e\u0436\u0435\u0442 \u043d\u0435 \u043f\u0440\u0438\u0439\u0442\u0438\r\n\r\nasyncio.run(main())\r\n```\r\n\r\n### \ud83d\udea8 **\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043e\u0448\u0438\u0431\u043e\u043a \u043f\u043e\u0442\u043e\u043a\u043e\u0432:**\r\n```python\r\nimport asyncio\r\nfrom xmlriver_pro import AsyncGoogleClient\r\nfrom xmlriver_pro.core import RateLimitError\r\n\r\nasync def main():\r\n    async with AsyncGoogleClient(user_id=123, api_key=\"key\") as google:\r\n        try:\r\n            results = await google.search(\"python\")\r\n        except RateLimitError as e:\r\n            if e.code in [110, 111, 115]:\r\n                # \u0412\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 \u043f\u043e\u0442\u043e\u043a\u043e\u0432 - \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\r\n                await asyncio.sleep(5)  # \u041f\u043e\u0434\u043e\u0436\u0434\u0438\u0442\u0435 5 \u0441\u0435\u043a\u0443\u043d\u0434\r\n                results = await google.search(\"python\")  # \u041f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u0435\r\n\r\nasyncio.run(main())\r\n```\r\n\r\n## \ud83e\uddea \u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\r\n\r\n### \u2705 \u0411\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 (\u0411\u0415\u0417 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0445 API \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432)\r\n\r\n```bash\r\n# \u0417\u0430\u043f\u0443\u0441\u043a \u0432\u0441\u0435\u0445 \u0442\u0435\u0441\u0442\u043e\u0432 (\u0411\u0415\u0417 real_api, \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e)\r\npytest\r\n\r\n# \u0417\u0430\u043f\u0443\u0441\u043a \u0441 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u0435\u043c\r\npytest --cov=xmlriver_pro\r\n\r\n# \u0417\u0430\u043f\u0443\u0441\u043a \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u043e\u0432\r\npytest tests/test_google.py\r\npytest tests/test_yandex.py\r\n\r\n# \u0417\u0430\u043f\u0443\u0441\u043a \u0441 \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u044b\u043c \u0432\u044b\u0432\u043e\u0434\u043e\u043c\r\npytest -v\r\n```\r\n\r\n**\u2705 \u042d\u0442\u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0411\u0415\u0417\u041e\u041f\u0410\u0421\u041d\u042b \u0438 \u041d\u0415 \u0442\u0440\u0435\u0431\u0443\u044e\u0442 API \u043a\u043b\u044e\u0447\u0435\u0439**\r\n\r\n### \u26a0\ufe0f Real API \u0442\u0435\u0441\u0442\u044b (\u0442\u0440\u0435\u0431\u0443\u044e\u0442 \u043a\u043b\u044e\u0447\u0438 \u0438 \u0421\u0422\u041e\u042f\u0422 \u0414\u0415\u041d\u0415\u0413!)\r\n\r\n```bash\r\n# \u26a0\ufe0f \u0412\u041d\u0418\u041c\u0410\u041d\u0418\u0415: \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0434\u0435\u043b\u0430\u044e\u0442 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0435 API \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0438 \u0422\u0420\u0410\u0422\u042f\u0422 \u0414\u0415\u041d\u042c\u0413\u0418!\r\npytest -m real_api -v  # \u0417\u0430\u043f\u0443\u0441\u043a \u0422\u041e\u041b\u042c\u041a\u041e real_api \u0442\u0435\u0441\u0442\u043e\u0432 (~$5-10)\r\n```\r\n\r\n\ud83d\udcd6 **\u041f\u043e\u043b\u043d\u0430\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u043f\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e:** [TESTING.md](TESTING.md)\r\n- \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u0442\u0438\u043f\u043e\u0432 \u0442\u0435\u0441\u0442\u043e\u0432\r\n- \u0418\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438 \u043f\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 Real API \u0442\u0435\u0441\u0442\u043e\u0432\r\n- \u0417\u0430\u0449\u0438\u0442\u043d\u044b\u0435 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c\u044b \u0438 best practices\r\n\r\n## \ud83d\udcda \u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f\r\n\r\n- **[README.md](README.md)** - \u043e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f (\u044d\u0442\u043e\u0442 \u0444\u0430\u0439\u043b)\r\n- **[docs/README.md](docs/README.md)** - \u043e\u0431\u0437\u043e\u0440 \u0432\u0441\u0435\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438\r\n- **[docs/examples.md](docs/examples.md)** - \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u044b \u0432\u0441\u0435\u0445 \u043c\u0435\u0442\u043e\u0434\u043e\u0432\r\n- **[docs/API_REFERENCE.md](docs/API_REFERENCE.md)** - \u043f\u043e\u043b\u043d\u044b\u0439 \u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a API\r\n- **[docs/ADVANCED_USAGE.md](docs/ADVANCED_USAGE.md)** - \u043f\u0440\u043e\u0434\u0432\u0438\u043d\u0443\u0442\u044b\u0435 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438\r\n- **[docs/SPECIAL_BLOCKS_GUIDE.md](docs/SPECIAL_BLOCKS_GUIDE.md)** - \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0435 \u0431\u043b\u043e\u043a\u0438\r\n- **[docs/VALIDATORS_REFERENCE.md](docs/VALIDATORS_REFERENCE.md)** - \u0432\u0441\u0435 \u0432\u0430\u043b\u0438\u0434\u0430\u0442\u043e\u0440\u044b\r\n- **[docs/FORMATTERS_REFERENCE.md](docs/FORMATTERS_REFERENCE.md)** - \u0432\u0441\u0435 \u0444\u043e\u0440\u043c\u0430\u0442\u0442\u0435\u0440\u044b\r\n- **[docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md)** - \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\r\n- **[\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434](https://github.com/Eapwrk/xmlriver-pro)** - \u043f\u043e\u043b\u043d\u044b\u0439 \u043a\u043e\u0434 \u0441 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u043c\u0438\r\n\r\n## \ud83e\udd1d \u0412\u043a\u043b\u0430\u0434 \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\r\n\r\nIssues \u0438 Pull Requests \u043f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0442\u0441\u044f \u043d\u0430 [GitHub](https://github.com/Eapwrk/xmlriver-pro).\r\n\r\n### \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438\r\n\r\n```bash\r\ngit clone https://github.com/Eapwrk/xmlriver-pro.git\r\ncd xmlriver-pro\r\npip install -e \".[dev]\"\r\npre-commit install\r\n```\r\n\r\n### \u0417\u0430\u043f\u0443\u0441\u043a \u0442\u0435\u0441\u0442\u043e\u0432\r\n\r\n```bash\r\npytest\r\n```\r\n\r\n## \ud83d\udcc4 \u041b\u0438\u0446\u0435\u043d\u0437\u0438\u044f\r\n\r\nMIT License. \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0441\u0442\u0438 \u0432 [LICENSE](LICENSE).\r\n\r\n## \ud83d\ude4f \u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u043d\u043e\u0441\u0442\u0438\r\n\r\n- [xmlriver.com](https://xmlriver.com) \u0437\u0430 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 API\r\n- Python \u0441\u043e\u043e\u0431\u0449\u0435\u0441\u0442\u0432\u0443 \u0437\u0430 \u044d\u043a\u043e\u0441\u0438\u0441\u0442\u0435\u043c\u0443\r\n- \u041a\u043e\u043d\u0442\u0440\u0438\u0431\u044c\u044e\u0442\u043e\u0440\u0430\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0430\r\n\r\n## \ud83d\udcde \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430\r\n\r\n- \ud83d\udce7 Email: seo@controlseo.ru\r\n- \ud83d\udc1b Issues: [GitHub Issues](https://github.com/Eapwrk/xmlriver-pro/issues)\r\n- \ud83d\udcac Discussions: [GitHub Discussions](https://github.com/Eapwrk/xmlriver-pro/discussions)\r\n\r\n---\r\n\r\n## \ud83d\udcc8 \u0421\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\r\n\r\n<div align=\"center\">\r\n\r\n![GitHub stars](https://img.shields.io/github/stars/Eapwrk/xmlriver-pro?style=social&label=Stars)\r\n![GitHub forks](https://img.shields.io/github/forks/Eapwrk/xmlriver-pro?style=social&label=Forks)\r\n![GitHub watchers](https://img.shields.io/github/watchers/Eapwrk/xmlriver-pro?style=social&label=Watchers)\r\n\r\n**XMLRiver Pro** - Professional Python client for XMLRiver API\r\n\r\n</div>\r\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Professional Python client for XMLRiver API with full coverage",
    "version": "2.0.0",
    "project_urls": {
        "Bug Reports": "https://github.com/Eapwrk/xmlriver-pro/issues",
        "Homepage": "https://xmlriver.com",
        "Original Repository": "https://github.com/KursHub-ru/xmlriver",
        "Repository": "https://github.com/Eapwrk/xmlriver-pro"
    },
    "split_keywords": [
        "xmlriver",
        " google",
        " yandex",
        " search",
        " api",
        " seo",
        " scraping",
        " search-engine",
        " news",
        " images",
        " maps",
        " ads"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "28d8efabceb4c5c6dc398f714cb3642c2955c56b3a48d569d590886fbce6eacb",
                "md5": "da6b1ac61c428505c39b874dc13a9a18",
                "sha256": "a3762b7afa8ae1d22659bdc0d58b5a430d596967cea9d2800e7cab2dac915d65"
            },
            "downloads": -1,
            "filename": "xmlriver_pro-2.0.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "da6b1ac61c428505c39b874dc13a9a18",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.10",
            "size": 85992,
            "upload_time": "2025-10-19T13:02:42",
            "upload_time_iso_8601": "2025-10-19T13:02:42.080080Z",
            "url": "https://files.pythonhosted.org/packages/28/d8/efabceb4c5c6dc398f714cb3642c2955c56b3a48d569d590886fbce6eacb/xmlriver_pro-2.0.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "fc54fac92e2f5349a255d91660abc38624adc515206f59d35d4261e3e4b61194",
                "md5": "e47d97d9a91706d473d782a49b69aae4",
                "sha256": "2ad8e0023922e46159664bed942c6a8af7d546453d1b984b11ba5fac58a86669"
            },
            "downloads": -1,
            "filename": "xmlriver_pro-2.0.0.tar.gz",
            "has_sig": false,
            "md5_digest": "e47d97d9a91706d473d782a49b69aae4",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.10",
            "size": 118461,
            "upload_time": "2025-10-19T13:02:43",
            "upload_time_iso_8601": "2025-10-19T13:02:43.449843Z",
            "url": "https://files.pythonhosted.org/packages/fc/54/fac92e2f5349a255d91660abc38624adc515206f59d35d4261e3e4b61194/xmlriver_pro-2.0.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-10-19 13:02:43",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "xmlriver-pro",
    "github_project": "xmlriver-pro",
    "github_not_found": true,
    "lcname": "xmlriver-pro"
}
        
Elapsed time: 1.30902s