Skip to main content

API wrapper для быстрого получения данных от Битрикс24 через REST API. Параллельные запросы к серверу, упаковка запросов в батчи, контроль скорости запросов.

Project description

fast_bitrix24

API wrapper для Питона для быстрого получения данных от Битрикс24 через REST API.

Статистика тестов Статистика загрузок Sourcery

Основная функциональность

Высокая скорость обмена данными

Тест скорости

  • На больших списках скорость обмена данными с сервером достигает тысяч элементов в секунду.
  • Использование асинхронных запросов через asyncio и aiohttp позволяет экономить время, делая несколько запросов к серверу параллельно.
  • Автоматическая упаковка запросов в батчи сокращает количество требуемых запросов к серверу и ускоряет обмен данными.

Избежание отказов сервера

  • Скорость отправки запросов к серверу Битрикс контролируется для избежания ошибки HTTP 503 Service unavailable.
  • Если сервер для сложных запросов начинает возвращать 500 Internal Server Error, можно в одну строку понизить скорость запроосов.

Удобство кода

  • Высокоуровневые списочные методы для сокращения количества необходимого кода. Большинство операций занимают только одну строку кода. Обработка параллельных запросов, упаковка запросов в батчи и многое другое убрано "под капот".
  • Позволяет задавать параметры запроса именно в таком виде, как они приведены в документации к Bitrix24 REST API. Параметры проверяются на корректность для облегчения отладки.
  • Выполнение запросов автоматически сопровождается прогресс-баром из пакета tqdm, иллюстрирующим не только количество обработанных элементов, но и прошедшее и оставшееся время выполнения запроса.

Начало

Установите модуль через pip:

pip install fast_bitrix24

Далее в python:

from fast_bitrix24 import *

# замените на ваш вебхук для доступа к Bitrix24
webhook = "https://your_domain.bitrix24.ru/rest/1/your_code/"
b = Bitrix(webhook)

Методы полученного объекта b в дальнейшем используются для взаимодействия с сервером Битрикс24.

Использование

get_all()

Чтобы получить полностью список сущностей, используйте метод get_all():

# список пользователей
users = b.get_all('user.get')

Метод get_all() возвращает список, где каждый элемент списка является словарем, описывающим одну сущность из запрошенного списка.

Вы также можете использовать параметр params, чтобы кастомизировать запрос:

# список сделок в работе, включая пользовательские поля
deals = b.get_all('crm.deal.list', params={
    'select': ['*', 'UF_*'],
    'filter': {'CLOSED': 'N'}
})

get_by_ID()

Если у вас есть список ID сущностей, то вы можете получить их свойства при помощи метода get_by_ID() и использовании методов вида *.get:

'''
получим список всех контактов, привязанных к сделкам, в виде
[
    (ID сделки1, [контакт1, контакт2, ...]),
    (ID сделки2, [контакт1, контакт2, ...]),
    ...
]
'''

contacts = b.get_by_ID('crm.deal.contact.items.get',
    [d['ID'] for d in deals])

Метод get_by_ID() возвращает список кортежей вида (ID, result), где result - ответ сервера относительно этого ID.

call()

Чтобы создавать, изменять или удалять список сущностей, используйте метод call():

# вставим в начало названия всех сделок их ID
tasks = [
    {
        'ID': d['ID'],
        fields: {
            'TITLE': f'{d["ID"]} - {d["TITLE"]}'
        }
    }
    for d in deals
]

b.call('crm.deal.update', tasks)

Метод call() возвращает список ответов сервера по каждому элементу переданного списка.

call_batch()

Если вы хотите вызвать пакетный метод, используйте call_batch():

results = b.call_batch ({
    'halt': 0,
    'cmd': {
        'deals': 'crm.deal.list', # берем список сделок
        # и берем список дел по первой из них
        'activities': 'crm.activity.list?filter[ENTITY_TYPE]=3&filter[ENTITY_ID]=$result[deals][0][ID]'
    }
})

Как это работает

  1. Перед обращением к серверу во всех методах класса Bitrix происходит проверка корректности самых популярных параметров, передаваемых к серверу, и поднимаются исключения TypeError и ValueError при наличии ошибок.
  2. Cоздаются запросы на получение всех элементов из запрошенного списка.
  3. Созданные запросы упаковываются в батчи по 50 запросов в каждом.
  4. Полученные батчи параллельно отправляются на сервер с соблюдением установленных скоростных ограничений (см. ниже "Как Битрикс24 ограничивает скорость заросов").
  5. Ответы (содержимое поля result) собираются в единый плоский список и возвращаются пользователю.
    • Поднимаются исключения класса aiohttp.ClientError, если сервер Битрикс вернул HTTP-ошибку, и RuntimeError, если код ответа был 200, но ошибка сдержалась в теле ответа сервера.
    • Происходит сортировка ответов (кроме метода get_all()) - порядок элементов в списке результатов совпадает с порядком соответствующих запросов в списке запросов.

В случае с методом get_all() пункт 2 выше выглядит немного сложнее:

  • get_all() делает первый запрос к серверу Битрикс24 с указанным методом и параметрами.
  • Сервер возвращает первую страницу (50 элементов) и параметр total - общее количество элементов, найденных по запросу.
  • Исходя из полученного общего количества элементов, создаются запросы на каждую из страниц (всего total // 50 - 1 запросов), необходимых для получения всех запрошенных элементов.

В связи с тем, что выполнение get_all() по длинным спискам может занимать долгое время, в течение которого пользователи могут добавлять новые элементы в список, может возникнуть ситуация, когда общее полученное количество элементов может не соответствовать изначальному значению total. В таких случаях будет выдано стандартное питоновское предупреждение (warning).

Как Битрикс24 ограничивает скорость запросов

  1. Существует пул из 50 запросов, которые можно направить без ожидания.
  2. Пул пополняется со скоростью 2 запроса в секунду.
  3. При исчерпании пула и несоблюдении режима ожидания сервер выдаёт ответ 403 Too Many Requests.

Подробный справочник по классу Bitrix

Объект класса Bitrix создаётся, чтобы через него выполнять все запросы к серверу Битрикс24.

Внутри объекта ведётся учёт скорости отправки запросов к серверу, поэтому важно, чтобы все запросы приложения в отношении одного аккаунта с одного IP-адреса отправлялись из одного экземпляра Bitrix.

Метод __init__(self, webhook: str, verbose: bool = True):

Создаёт экземпляр объекта Bitrix.

Параметры

  • webhook: str - URL вебхука, полученного от сервера Битрикс.

  • verbose: bool = True - показывать прогрессбар при выполнении запроса.

Метод get_all(self, method: str, params: dict = None) -> list

Получить полный список сущностей по запросу method.

get_all() самостоятельно обрабатывает постраничные ответы сервера, чтобы вернуть полный список (подробнее см. "Как это работает" выше).

Параметры

  • method: str - метод REST API для запроса к серверу.

  • params: dict - параметры для передачи методу. Используется именно тот формат, который указан в документации к REST API Битрикс24. get_all() не поддерживает параметры start, limit и order.

Возвращает полный список сущностей, имеющихся на сервере, согласно заданным методу и параметрам.

Метод get_by_ID(self, method: str, ID_list: Sequence, ID_field_name: str = 'ID', params: dict = None) -> list

Получить список сущностей по запросу method и списку ID.

Используется для случаев, когда нужны не все сущности, имеющиеся в базе, а конкретный список поименованных ID, либо в REST API отсутствует способ получения сущностей одним вызовом.

Например, чтобы получить все контакты, привязанные к сделкам в работе, нужно выполнить следующий код:

deals = b.get_all('crm.deal.list', params={
    'filter': {'CLOSED': 'N'}
})

contacts = b.get_by_ID('crm.deal.contact.item.get',
    [d['ID'] for d in deals])

Параметры

  • method: str - метод REST API для запроса к серверу.

  • ID_list: Sequence - список ID, в отношении которых будут выполняться запросы.

  • ID_field_name: str - название поля, в которое будут подставляться значения из списка ID_list. По умолчанию 'ID'.

  • params: dict - параметры для передачи методу. Используется именно тот формат, который указан в документации к REST API Битрикс24. Если среди параметров, указанных в params, указан параметр ID, то поднимается исключение ValueError.

Возвращает список кортежей вида:

[
    (ID_1, результат_выполнения_запроса_по_ID_1),
    (ID_2, результат_выполнения_запроса_по_ID_2),
    ...
]

Первым элементом каждого кортежа будет ID из списка ID_list. Вторым элементом каждого кортежа будет результат выполнения запроса относительно этого ID. Это может быть, например, список связанных сущностей или пустой список, если не найдено ни одной привязанной сущности.

get_by_ID() гарантированно возвращает список такой же длины, как и поданный на вход ID_list.

Метод call(self, method: str, items)

Вызвать метод REST API. Самый универсальный метод, применяемый, когда get_all и get_by_ID не подходят.

Параметры

  • method: str - метод REST API

  • items - параметры вызываемого метода. Может быть списком, и тогда метод будет вызван для каждого элемента списка, а может быть одним словарем параметров для единичного вызова.

call() вызывает method, последовательно подставляя в параметры запроса все элементы items, и возвращает список ответов сервера для каждого из отправленных запросов. Либо, если items - не список, а словарь с параметрами, то происходит единичный вызов и возвращается его результат.

Метод call_batch(self, params: dict) -> dict

Вызвать метод batch (см. официальную документацию по методу batch).

Поддерживается примение результатов выполнения одной команды в следующей при помощи ключевого слова $result:

results = b.call_batch ({
    'halt': 0,
    'cmd': {
        'deals': 'crm.deal.list', # берем список сделок
        # и берем список дел по первой из них
        'activities': 'crm.activity.list?filter[ENTITY_TYPE]=3&filter[ENTITY_ID]=$result[deals][0][ID]'
    }
})

Возвращает словарь вида:

{
    'имя_команды_1': результаты_выполнения_команды_1,
    'имя_команды_1': результаты_выполнения_команды_1,
    ...
}

Контекстный менеджер slow

Иногда, когда серверу Битрикса посылается запрос, отбирающий много ресурсов сервера (например, на создание 2500 лидов), то сервер не выдерживает даже стандартных темпов подачи запросов, описанных в официальной документации, возвращая 500 Internal Server Error после нескольких первых запросов.

В такой ситуации помогает замедление запросов при помощи контекстного менеджера slow:

# временно снижаем скорость до 1.2 запроса в секунду
slower_speed = 1.2
with slow(slower_speed):
    b.call('crm.lead.add', [{} for x in range(2500)])

# а теперь несемся с прежней скоростью
leads = b.get_all('crm.lead.list')
...

slow(requests_per_second: float = 0.5)

Снижает скорость запросов. По вызовам Bitrix, происходящим внутри выхова этого контекстного менеджера:

  • скорость запросов гарантированно не превысит requests_per_second
  • механика "пула запросов" отключается - считается, что размер пула равен 0, и запросы подаются равномерно

После выхода из контекстного менеджера механика пула восстанавливается, однако пул считается пустым и начинает наполняться с обычной скоростью 2 запроса в секунду.

Параметры

  • requests_per_second: float = 0.5 - требуемая замедленная скорость запросов. По умолчанию 0.5 запросов в секунду.

Советы и подсказки

А как мне сформировать запрос к Битриксу, чтобы ...?

  1. Поищите в официальной документации по REST API.
  2. Если на ваш вопрос там нет ответа - попробуйте задать его в группе "Партнерский REST API" в Сообществе разработчиков Битрикс24.
  3. Спросите в Телеграме в группе разработчиков Битрикс24.
  4. Спросите в Телеграме в группе пользователей fast_bitrix24.
  5. Спросите на русском StackOverflow.

Я хочу добавить несколько лидов списком, но получаю ошибку сервера.

Оберните вызов call() в slow, установив скорость запросов в 1 - 1,3 в секунду:

with slow(1.2):
    results = b.call('crm.lead.add', tasks)

Я хочу вызвать call() только один раз, а не по списку.

Передавайте параметры запроса методу call(), он может делать как запросы по списку, так и единичный запрос:

method = 'crm.lead.add'
params = {'fields': {
    'TITLE': 'Чпок'
}}
b.call(method, params)

Результатом будет ответ сервера по этому одному элементу.

Однако, если такие вызовы делаются несколько раз, то более эффективно формировать из них список и вызывать call() единожды по всему списку.

Как сортируются результаты при вызове get_all()?

Пока что никак.

Все обращения к серверу происходят асинхронно и список результатов отсортирован в том порядке, в котором сервер возвращал ответы. Если вам требуется сортировка, то вам нужно делать ее самостоятельно, например:

deals = b.get_all('crm.deal.list')
deals.sort(key = lambda d: int(d['ID']))

Как связаться с автором

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Files for fast-bitrix24, version 0.4.7
Filename, size File type Python version Upload date Hashes
Filename, size fast_bitrix24-0.4.7-py3-none-any.whl (20.1 kB) File type Wheel Python version py3 Upload date Hashes View
Filename, size fast_bitrix24-0.4.7.tar.gz (23.1 kB) File type Source Python version None Upload date Hashes View

Supported by

Pingdom Pingdom Monitoring Google Google Object Storage and Download Analytics Sentry Sentry Error logging AWS AWS Cloud computing DataDog DataDog Monitoring Fastly Fastly CDN DigiCert DigiCert EV certificate StatusPage StatusPage Status page