XCRAP.CC

Клиент для Python

Тонкая типизированная обёртка над теми же HTTP-эндпоинтами. Ничего невозможного без fetch она не делает — просто избавляет от того, чтобы заново писать классы ошибок, повторы и согласование формата.

Установка

pip install xcrap-sdk

Или тем, чем вы на самом деле пользуетесь:

  • uv add xcrap-sdk
  • poetry add xcrap-sdk
  • pdm add xcrap-sdk

Требуется · Python 3.9 или новее. Одна зависимость: httpx.

  • Ключа не будет никогда

    Не на что регистрироваться, нечего класть в переменную окружения и нечего менять, когда это случайно закоммитят.

  • Типы на всю глубину

    Полные объявления для каждого метода и каждой структуры ответа: опечатка в имени поля — красное подчёркивание, а не сюрприз во время работы.

  • Markdown встроен

    Один флаг превращает любой ответ в готовый для промпта markdown и говорит, во сколько это обойдётся контекстному окну.

Две строки до поста

Ключ передавать не нужно, настраивать клиент — тоже. Создали и вызвали.

from xcrap import Xcrap

with Xcrap() as xcrap:
    tweet = xcrap.tweet("https://x.com/jack/status/20")
    print(tweet.text)            # just setting up my twttr
    print(tweet.metrics.likes)   # 308067

Markdown вместе с ценой в токенах

Каждый метод чтения принимает флаг markdown. Ответ приходит строкой, готовой для промпта, а клиент запоминает, во сколько токенов эта строка, скорее всего, обойдётся, — чтобы сверить бюджет до того, как его потратить.

# Markdown, ready to drop into a prompt.
md = xcrap.tweet("https://x.com/jack/status/20", markdown=True)

# How many tokens that will cost, before you spend them.
print(xcrap.last_meta.markdown_tokens)

Треды и ленты

Тред возвращается развёрнутым и по порядку, начиная с первого поста, а не с последнего. Лента листается по курсорам, так что хранить их не нужно.

thread = xcrap.thread(
    "https://x.com/naval/status/1002103360646823936",
)

print(thread.count)                  # 31
for post in thread.tweets:
    print(post.text)

# And a timeline paginates itself.
for post in xcrap.iter_user_tweets("naval", limit=200):
    print(post.id)

Ошибки — это типы, а не коды

Каждый сбой — класс, который ловится по имени. 404 — удалённый или закрытый пост, 429 несёт число секунд до сброса окна, а сетевой сбой отличим от отказа.

from xcrap import Xcrap, XcrapNotFound, XcrapRateLimited

try:
    xcrap.tweet("https://x.com/jack/status/1")
except XcrapNotFound:
    return None
except XcrapRateLimited as error:
    # The client already knows when the window resets.
    time.sleep(error.retry_after)
    raise

Все методы

Все принимают формат и флаг markdown и возвращают те же структуры, что и HTTP API.

tweet(url, **opts)
Один пост целиком: текст, автор, метрики, медиа, опрос и цитируемый пост.
thread(url, **opts)
Весь тред от любого поста в нём, развёрнутый в порядке публикации.
search(query, **opts)
Посты по запросу — новые, популярные, с фото или видео — в необязательном диапазоне дат.
replies(url, **opts)
Страница прямых ответов на пост: сначала самые популярные или самые новые.
user(handle, **opts)
Профиль: описание, счётчики, дата регистрации, верификация, город и сайт.
user_tweets(handle, **opts)
Одна страница постов аккаунта и курсор на следующую.
iter_user_tweets(handle, limit=…)
То же самое в виде генератора, который сам идёт по курсорам и останавливается на лимите.
user_history(handle, **opts)
До тысячи постов аккаунта за один вызов, от новых к старым, в необязательном диапазоне дат.
followers(handle, **opts)
Страница аккаунтов, которые подписаны на аккаунт.
following(handle, **opts)
Страница аккаунтов, на которые подписан аккаунт.
trends(**opts)
Что в трендах, с числом постов там, где X его отдаёт.
media(url, **opts)
Все вложения во всех битрейтах, которые закодировал X, прямыми ссылками.
download_media(url, **opts)
Отдаёт один из этих файлов байтами, а не ссылкой.
bulk(urls, **opts)
До пятидесяти постов за один запрос. Битая ссылка роняет только свою запись.

Все параметры — в справочнике API Есть ещё клиент для Node.