DuckoNetДокументация 0.11.5Документация DuckoLab
Разделы

Запросы и примеры

Примеры рассчитаны на DuckoNet 0.11.5. Сначала выполните установку. Адреса example.com/api/... обозначают API вашего приложения: замените их реальными адресами, возвращающими ожидаемый ответ.

GET и параметры URL

Откройте HttpClient, передав API-ключ и название браузера. Библиотека сама подготовит установку и сессию. Список пар в params позволяет передать один параметр несколько раз.

Клиент принимает абсолютные адреса http:// и https://. Он не заменяет HTTP на HTTPS. Заголовки User-Agent, Accept, Accept-Language и Accept-Encoding берутся из профиля, если вы не задали допустимые собственные значения. Защищённые Client Hints добавляются только для HTTPS.

HttpClient предназначен для запросов приложения. Он не выполняет браузерный CORS/preflight; для такого сценария нужен отдельный браузерный API. Запросы через Client и AsyncClient с явным браузерным контекстом описаны отдельно.

import os
from duckonet import HttpClient

with HttpClient(api_key=os.environ['DUCKONET_API_KEY'], browser='chrome') as client:
    response = client.get('https://example.com/api/items', params=[('tag', 'a'), ('tag', 'b')])
    response.raise_for_status()
    print(response.status_code, response.text)

В первом примере .text возвращает текст ответа. Метод .json() применяйте к адресу, который действительно возвращает JSON.

Время жизни клиента

Запросы, cookies и capabilities доступны после входа в with или async with. Один экземпляр клиента можно открыть только один раз. После закрытия создайте новый экземпляр. close() и await close() освобождают ресурсы клиента; переданные вами файлы и цикл событий остаются под управлением приложения.

Все маршруты одного клиента используют общее хранилище cookies. Разные клиенты изолированы. Каталог state_dir хранит служебное состояние установки, но не сохраняет cookies автоматически.

Асинхронный запрос

import asyncio
import os
from duckonet import AsyncHttpClient

async def main():
    async with AsyncHttpClient(api_key=os.environ['DUCKONET_API_KEY'], browser='chrome') as client:
        response = await client.post('https://example.com/api/items', json={'title': 'Пример'})
        response.raise_for_status()
        print(response.content)

asyncio.run(main())

Методы запросов AsyncHttpClient вызываются с await, а поток открывается через async with. Отмена задачи asyncio отменяет запрос и освобождает его ресурсы. При отмене дождитесь выхода из async with, чтобы освобождение ресурсов завершилось.

JSON, формы и файлы

В следующих фрагментах client — открытый HttpClient. Поместите нужные вызовы внутрь with HttpClient(...) as client: из первого примера. Каждый вызов ниже показывает отдельный способ отправки тела.

# Ниже client уже открыт через with.
response = client.post('https://example.com/api/json', json={'name': 'Тест', 'enabled': True})
response = client.post('https://example.com/api/form', data=[('tag', 'a'), ('tag', 'b')])
response = client.post('https://example.com/api/text', data='Привет', encoding='utf-8')
response = client.post('https://example.com/api/bytes', body=b'\x00\x01')
with open('report.txt', 'rb') as source:
    response = client.post('https://example.com/api/upload', data={'title': 'Отчёт'},
                           files={'file': ('report.txt', source, 'text/plain')})

Перед отправкой тело целиком помещается в память. Максимум — 16 MiB, включая служебные разделители multipart. Файл читается с текущей позиции; библиотека его не закрывает. Можно явно указать имя файла, иначе из file.name берётся только имя без пути. Для повторяющихся полей передайте files как список пар. Несовместимые сочетания body, data, json и files отклоняются до отправки.

Добавление cookies

client.cookies.set('theme', 'dark', url='https://example.com/', secure=True, same_site='lax')
value = client.cookies.get('theme', domain='example.com', path='/')
response = client.get('https://example.com/api/items', cookies={'theme': 'light'})
# Значение theme для одного запроса не изменяет хранилище.
client.cookies.delete('theme', domain='example.com', path='/', host_only=True)

cookies_enabled=False отключает отправку cookies для этого запроса. С ним нельзя передать непустой cookies или заголовок Cookie. Параметр persist_cookies=False действует отдельно: запрещает сохранять полученные Set-Cookie, но не запрещает отправку. Подробнее — в разделе Cookies.

Скачивание по частям

with client.stream('GET', 'https://example.com/download', timeout_seconds=90) as stream:
    print(stream.status_code, stream.headers.get('Content-Type'))
    with open('download.bin', 'wb') as target:
        for chunk in stream.iter_bytes(65536):
            target.write(chunk)
    final_response = stream.result()

with open('download.bin', 'wb') as target:
    final_response = client.consume('GET', 'https://example.com/download', target.write)

Поток становится доступен после получения заголовков конечного ответа с учётом перенаправлений. Поток не накапливает тело в response.content: сохраняйте получаемые части сами. После чтения до конца result() возвращает итог запроса, но не собирает прочитанные части заново. Выход из контекста закрывает или отменяет поток. Читать его может один потребитель. Размер части — от 1 байта до 16 MiB, по умолчанию 65536 байт.

# Выполните этот фрагмент внутри async-функции с открытым AsyncHttpClient.
async with client.stream('GET', 'https://example.com/download') as stream:
    async for chunk in stream.iter_bytes():
        process_chunk(chunk)  # Обработчик bytes вашего приложения.
    final_response = await stream.result()

Для явной отмены асинхронного потока вызовите await stream.cancel(). У синхронного потока cancel() вызывает отмену базового запроса. После отмены всё равно завершите контекст. Асинхронный consume() ожидает результат обработчика, если тот вернул объект, поддерживающий await.

Прокси и параметры запроса

from duckonet import ProxyRoute

response = client.get('https://example.com/api/items',
    headers=[('Accept', 'application/json'), ('X-Trace', 'demo')],
    proxy=ProxyRoute('socks5h://127.0.0.1:1080'), protocol='h2',
    timeout_seconds=60, body_limit=10 * 1024 * 1024,
    allow_redirects=False, diagnostics=True)
print(response.protocol, response.timings)

Для этого примера нужен работающий SOCKS5-прокси на 127.0.0.1:1080; замените адрес на свой. Если прокси недоступен, клиент не переключается на прямое соединение. proxy=None явно выбирает прямое соединение. Сочетания h3 или auto с прокси отклоняются до выхода в сеть. При diagnostics=True доступны измеренные данные, которые удалось получить для этого запроса.

Обработка ошибок

from duckonet import HTTPStatusError, HttpTimeoutError, NativeError, describe_http_error

try:
    response = client.get('https://example.com/api/items', timeout_seconds=15)
    response.raise_for_status()
except HTTPStatusError as error:
    status = error.status_code
except (HttpTimeoutError, NativeError) as error:
    details = describe_http_error(error)
    print(details.group, details.code, details.partial_response_available)

Пример выводит категорию и код ошибки без текста, который может содержать данные запроса. Ошибки аргументов ValueError и TypeError, ошибки запуска, декодирования, JSON и отмена asyncio обрабатываются отдельно. describe_http_error() описывает исходное исключение, не заменяя его класс. Ответ HTTP 4xx или 5xx сам по себе не вызывает NativeError; для проверки вызовите raise_for_status().

Текст страницы