Важное предупреждение: Эта статья посвящена доступу к публичным данным. Всегда соблюдайте Условия использования (Terms of Service) целевой платформы и применимые законы, включая CFAA в США и GDPR в ЕС. Несанкционированный доступ к защищённым данным или обход мер безопасности может быть незаконным.
В 2023 году X (ранее Twitter) радикально изменил свою API-политику. Бесплатный tier для поиска был упразднён, а стоимость доступа к данным выросла в десятки раз. Для команд, строящих мониторинг социальных сетей, аналитику настроений или трекеры трендов, это стало критическим ударом. Многие обратились к веб-скрейпингу Twitter как к альтернативе.
Но X агрессивно защищает свою платформу. Datacenter IP блокируются массово, rate limits для незалогиненных сессий — жёсткие, а архитектура SPA на GraphQL требует особого подхода. В этом руководстве мы разберём, как скрейпить Twitter через прокси эффективно и устойчиво.
Ландшафт после API-ограничений X
До 2023 года Twitter API был одним из самых открытых среди крупных соцсетей. Бесплатный tier давал до 450 запросов в 15-минутное окно для поиска — достаточно для многих use cases. Затем всё изменилось:
- Free tier — только posting, без доступа к search или tweet lookup
- Basic tier ($100/месяц) — 10,000 tweet reads/месяц, что ничтожно мало
- Pro tier ($5,000/месяц) — 1M tweet reads/месяц, но с жёсткими ограничениями
- Enterprise — индивидуальная цена, часто $42,000+/месяц
Для стартапов, исследователей и небольших команд эти цены неприемлемы. Веб-скрейпинг стал единственной альтернативой для сбора публичных данных Twitter/X.
Что изменилось технически
X перешёл на SPA-архитектуру с GraphQL backend. Данные загружаются динамически через AJAX-запросы, а не рендерятся в HTML. Это усложняет классический scraping, но открывает возможности: GraphQL responses содержат структурированный JSON с богатыми метаданными.
Какие данные X доступны публично
Не всё требует одинакового подхода. Разберём по категориям:
Доступны без авторизации
- Профили пользователей — имя, описание, количество followers/following, дата регистрации
- Публичные твиты — текст, медиа, engagements (likes, retweets, replies), timestamp
- Reply threads — ответы на твиты и цепочки обсуждений
- Trending topics — тренды по географии
- Search results — поиск по ключевым словам и хештегам (ограниченно)
Требуют авторизации
- Protected accounts — твиты закрытых профилей (скрейпинг незаконен)
- DMs и notifications — приватные данные
- Full search history — полный архивный поиск
- Bookmarks и lists — персональные коллекции
Ключевой принцип: скрейпинг легален только для публично доступных данных. Обход paywall или login-wall для доступа к защищённому контенту нарушает ToS и может быть преследуемым.
Почему residential прокси необходимы
X применяет многоуровневую защиту от ботов:
IP-based filtering
Datacenter IP-диапазоны помечены как подозрительные. X использует базы данных вроде MaxMind для классификации IP. Запросы с DC-адресов часто получают:
- CAPTCHA на каждом запросе
- HTTP 429 (Too Many Requests) после нескольких запросов
- HTTP 403 с требованием авторизации
- Пустые responses или редирект на login
Rate limit дифференциация
X применяет разные лимиты для разных типов сессий:
| Тип сессии | Ориентировочный лимит | Поведение при превышении |
|---|---|---|
| Anonymous (без cookies) | ~50-100 запросов/час | 429 или CAPTCHA |
| With guest token | ~200-500 запросов/час | 429, token invalidation |
| Logged-in session | ~2000+ запросов/час | Soft throttle, затем 429 |
| Verified account | Высокие лимиты | Зависит от репутации |
Residential vs Datacenter прокси
Residential прокси используют IP-адреса реальных домашних устройств. Для X они выглядят как обычные пользователи:
- IP из пулов ISP, не дата-центров
- Географическое распределение соответствует реальному трафику
- Меньше CAPTCHA и блокировок
- Выше success rate для X/Twitter scraping
Mobile прокси — ещё надёжнее, но дороже. IP мобильных операторов имеют наивысший trust score.
Python + Playwright: практическая реализация
Теперь перейдём к коду. Нам нужен браузер для выполнения JavaScript и получения GraphQL responses.
Установка зависимостей
pip install playwright asyncio
playwright install chromium
Базовый скрейпер с residential прокси
import asyncio
from playwright.async_api import async_playwright
import json
PROXY_URL = "http://user-country-US:PASSWORD@gate.proxyhat.com:8080"
async def scrape_profile(username: str):
async with async_playwright() as p:
browser = await p.chromium.launch(
proxy={"server": PROXY_URL},
headless=True
)
context = await browser.new_context(
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
)
page = await context.new_page()
# Перехват GraphQL responses
graphql_data = []
async def handle_response(response):
if "graphql" in response.url:
try:
data = await response.json()
graphql_data.append(data)
except:
pass
page.on("response", handle_response)
# Навигация на профиль
url = f"https://x.com/{username}"
await page.goto(url, wait_until="networkidle")
# Ждём загрузки данных
await page.wait_for_timeout(3000)
await browser.close()
return graphql_data
# Запуск
results = asyncio.run(scrape_profile("elonmusk"))
print(json.dumps(results[0], indent=2) if results else "No data captured")
Парсинг GraphQL payload
X встраивает данные в GraphQL responses. Структура зависит от endpoint:
def extract_profile_data(graphql_response: dict) -> dict:
"""Извлечение данных профиля из GraphQL response."""
try:
user_data = graphql_response["data"]["user"]["result"]
return {
"id": user_data["rest_id"],
"username": user_data["legacy"]["screen_name"],
"name": user_data["legacy"]["name"],
"description": user_data["legacy"]["description"],
"followers_count": user_data["legacy"]["followers_count"],
"following_count": user_data["legacy"]["friends_count"],
"tweets_count": user_data["legacy"]["statuses_count"],
"verified": user_data["legacy"]["verified"],
"created_at": user_data["legacy"]["created_at"],
}
except KeyError as e:
print(f"Failed to parse: missing key {e}")
return None
Ротация IP с residential пулом
Для масштабирования нужен rotating proxy pool. ProxyHat поддерживает session-based rotation:
import random
import string
def generate_session_id(length=8):
return ''.join(random.choices(string.ascii_lowercase + string.digits, k=length))
def get_proxy_url(country="US", session_id=None):
"""Генерация URL с ротацией через residential пул ProxyHat."""
if session_id is None:
session_id = generate_session_id()
# Каждый новый session_id = новый IP из пула
return f"http://user-country-{country}-session-{session_id}:PASSWORD@gate.proxyhat.com:8080"
# Пример: ротация IP каждые 10 запросов
async def scrape_multiple_profiles(usernames: list, batch_size=10):
results = []
for i, username in enumerate(usernames):
# Новая сессия каждые batch_size запросов
if i % batch_size == 0:
session_id = generate_session_id()
proxy_url = get_proxy_url(country="US", session_id=session_id)
data = await scrape_with_proxy(username, proxy_url)
results.append(data)
# Задержка между запросами
await asyncio.sleep(random.uniform(2, 5))
return results
Обработка rate limits
X использует несколько механизмов throttling:
HTTP 429 — явный rate limit
import asyncio
from playwright.async_api import Error as PlaywrightError
async def scrape_with_retry(url: str, max_retries=3, base_delay=60):
"""Скрейпинг с exponential backoff при 429."""
for attempt in range(max_retries):
try:
data = await scrape_url(url)
return data
except Exception as e:
if "429" in str(e) or "Too Many Requests" in str(e):
delay = base_delay * (2 ** attempt)
print(f"Rate limited. Waiting {delay}s before retry {attempt + 1}")
await asyncio.sleep(delay)
# Rotate IP на новой сессии
continue
else:
raise
raise Exception(f"Max retries exceeded for {url}")
Sliding window detection
X отслеживает не только мгновенную нагрузку, но и паттерны во времени. Признаки детекции:
- Слишком регулярные интервалы между запросами
- Одинаковые User-Agent и заголовки
- Идентичные behavioural patterns (scroll, click)
Митигация:
import random
async def human_like_delay():
"""Случайная задержка, имитирующая человеческое поведение."""
# 2-8 секунд с bias к средним значениям
delay = random.gauss(5, 1.5)
return max(2, min(8, delay))
async def scroll_page(page):
"""Имитация скролла."""
for _ in range(random.randint(2, 5)):
await page.mouse.wheel(0, random.randint(300, 800))
await asyncio.sleep(random.uniform(0.5, 1.5))
Account-level vs IP-level throttling
При использовании logged-in sessions X throttles на уровне аккаунта:
- IP rotation не поможет — лимит привязан к account
- Нужны multiple accounts (risk: suspension)
- Guest tokens ротируются лучше, но имеют меньшие лимиты
Для Twitter residential proxies оптимальная стратегия — anonymous/guest sessions с частой ротацией IP.
Node.js пример для production
Для команд на JavaScript/TypeScript:
const { chromium } = require('playwright');
const PROXY_CONFIG = {
server: 'http://gate.proxyhat.com:8080',
username: 'user-country-US-session-abc123',
password: 'YOUR_PASSWORD'
};
async function scrapeTweet(tweetId) {
const browser = await chromium.launch({
proxy: PROXY_CONFIG,
headless: true
});
const context = await browser.newContext({
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36'
});
const page = await context.newPage();
let tweetData = null;
page.on('response', async (response) => {
if (response.url().includes('TweetDetail')) {
try {
tweetData = await response.json();
} catch (e) {}
}
});
await page.goto(`https://x.com/i/status/${tweetId}`, {
waitUntil: 'networkidle'
});
await page.waitForTimeout(3000);
await browser.close();
return tweetData;
}
// Usage
scrapeTweet('1234567890').then(console.log);
Правовые аспекты скрейпинга Twitter/X
Недавние судебные прецеденты
X активно судится со скрейперами:
- X v. Meta (2023) — X обвинил Meta в скрейпинге для обучения AI. Иск урегулирован.
- X v. неизвестные лица — постоянные DMCA takedown и lawsuits против tool vendors.
Однако американское право (CFAA) традиционно защищает доступ к публичным данным. Ключевой прецедент — hiQ Labs v. LinkedIn, где суд постановил, что скрейпинг публичных данных не нарушает CFAA.
ToS X — что запрещено
Условия использования X прямо запрещают:
- Доступ к платформе без авторизации (unauthorized access)
- Обход rate limits и других технических мер
- Создание производных продуктов на основе данных X
- Scraping для конкурентного анализа или AI training
Нарушение ToS ≠ автоматически незаконно, но даёт X право:
- Заблокировать IP и аккаунты
- Отправить cease-and-desist
- Подать иск за breach of contract
GDPR и data protection
В EU сбор персональных данных регулируется GDPR:
- Твиты содержат personal data их авторов
- Сбор и хранение требует legal basis
- Legitimate interest может применяться для research/journalism
- Нужна privacy policy и data minimization
Когда использовать официальное API
Скрейпинг — не всегда лучший выбор:
| Сценарий | Рекомендация |
|---|---|
| Real-time monitoring (минуты) | Official API + Enterprise tier |
| Historical data (>7 дней) | Official API или data providers |
| High-volume (>100K tweets/день) | Official API (Pro/Enterprise) |
| Low-volume research (<10K/день) | Scraping + residential proxies |
| One-time data collection | Scraping acceptable |
| Production service с SLA | Official API |
Ключевые выводы
- API X недоступен для большинства use cases из-за ценовой политики — scraping стал необходимостью
- Residential прокси критичны — datacenter IP блокируются агрессивно
- GraphQL responses содержат структурированный JSON, удобнее HTML parsing
- Rate limits требуют стратегии: IP rotation, delays, exponential backoff
- Публичные данные — легальны для сбора, но соблюдайте ToS и GDPR
- Production системы с высокими требованиями должны использовать официальный API
Готовы начать собирать данные X? Изучите тарифы ProxyHat для residential прокси с геотаргетингом и ротацией IP.
Для дополнительных материалов по веб-скрейпингу посетите наш блог или страницу use case веб-скрейпинг.






