# XMLRiver Pro
<div align="center">
[](https://github.com/Eapwrk/xmlriver-pro)
[](https://github.com/Eapwrk/xmlriver-pro)
[](https://github.com/Eapwrk/xmlriver-pro/fork)
[](https://github.com/Eapwrk/xmlriver-pro/subscription)
[](https://github.com/sponsors/Eapwrk)
[](https://github.com/Eapwrk/xmlriver-pro/issues)
[](https://github.com/Eapwrk/xmlriver-pro/discussions)
[](https://github.com/Eapwrk/xmlriver-pro/pulls)
[](https://github.com/Eapwrk/xmlriver-pro/wiki)
[](https://github.com/Eapwrk/xmlriver-pro/actions)
[](https://github.com/Eapwrk/xmlriver-pro/releases)
[](https://github.com/Eapwrk/xmlriver-pro/security)
[](https://github.com/Eapwrk/xmlriver-pro/pulse)
[](https://github.com/Eapwrk/xmlriver-pro/settings)
[](https://github.com/Eapwrk/xmlriver-pro)
[](https://github.com/Eapwrk/xmlriver-pro/issues)
[](https://github.com/Eapwrk/xmlriver-pro/pulls)
[](https://github.com/Eapwrk/xmlriver-pro/discussions)
[](https://github.com/Eapwrk/xmlriver-pro/wiki)
[](https://github.com/Eapwrk/xmlriver-pro/actions)
[](https://pypi.org/project/xmlriver-pro/)
[](https://python.org)
[](LICENSE)
[](https://pypi.org/project/xmlriver-pro/)
[](https://github.com/Eapwrk/xmlriver-pro)
[](https://github.com/Eapwrk/xmlriver-pro)
[](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">



**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[](https://github.com/Eapwrk/xmlriver-pro)\r\n[](https://github.com/Eapwrk/xmlriver-pro)\r\n[](https://github.com/Eapwrk/xmlriver-pro/fork)\r\n[](https://github.com/Eapwrk/xmlriver-pro/subscription)\r\n[](https://github.com/sponsors/Eapwrk)\r\n[](https://github.com/Eapwrk/xmlriver-pro/issues)\r\n[](https://github.com/Eapwrk/xmlriver-pro/discussions)\r\n[](https://github.com/Eapwrk/xmlriver-pro/pulls)\r\n[](https://github.com/Eapwrk/xmlriver-pro/wiki)\r\n[](https://github.com/Eapwrk/xmlriver-pro/actions)\r\n[](https://github.com/Eapwrk/xmlriver-pro/releases)\r\n[](https://github.com/Eapwrk/xmlriver-pro/security)\r\n[](https://github.com/Eapwrk/xmlriver-pro/pulse)\r\n[](https://github.com/Eapwrk/xmlriver-pro/settings)\r\n[](https://github.com/Eapwrk/xmlriver-pro)\r\n[](https://github.com/Eapwrk/xmlriver-pro/issues)\r\n[](https://github.com/Eapwrk/xmlriver-pro/pulls)\r\n[](https://github.com/Eapwrk/xmlriver-pro/discussions)\r\n[](https://github.com/Eapwrk/xmlriver-pro/wiki)\r\n[](https://github.com/Eapwrk/xmlriver-pro/actions)\r\n[](https://pypi.org/project/xmlriver-pro/)\r\n[](https://python.org)\r\n[](LICENSE)\r\n[](https://pypi.org/project/xmlriver-pro/)\r\n[](https://github.com/Eapwrk/xmlriver-pro)\r\n[](https://github.com/Eapwrk/xmlriver-pro)\r\n[](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\r\n\r\n\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"
}