При выгрузке истории с блокчейн-эксплореров мы сталкивались с жёсткими ограничениями: Etherscan отдаёт максимум 10000 записей на запрос, 5 запросов в секунду на бесплатном плане, нет стриминга. Если нужно собрать все транзакции контракта USDT за последние три года — это более 30 миллионов записей. Для анализа DeFi-стратегий или отслеживания транзакций китов нужны миллионы записей — стандартные API не справляются. Простое решение не работает: пагинация через page не даст больше 10000. Нужна стратегия, и мы её нашли: пагинация по блокам с последующей дедупликацией.
В этой статье разберём основные подходы: от прямого Etherscan API до использования Alchemy и Moralis, а также парсинг HTML и работу с собственной нодой. Каждый метод отличается по скорости, полноте и стоимости. Например, Alchemy позволяет получить все транзакции адреса одним запросом без лимита записей, а собственная нода даёт доступ к внутренним вызовам.
Alchemy обходит Etherscan по всем параметрам: в 5 раз выше rate limit, нет лимита записей благодаря pageKey, поддержка WebSocket стриминга и кросс-чейн запросов. Для проектов, требующих максимальной производительности, Alchemy — безальтернативный выбор.
Если вам нужно собрать исторические данные для анализа — свяжитесь с нами, мы подберём оптимальный инструмент за один день.
Как обойти ограничение Etherscan API в 10000 записей?
Etherscan предоставляет параметры startblock и endblock. Пагинация по блокам позволяет вытянуть все транзакции. Код ниже перебирает блоки, увеличивая startblock до последнего встреченного:
import httpx import asyncio from typing import AsyncGenerator async def get_all_transactions( address: str, api_key: str, start_block: int = 0 ) -> AsyncGenerator[dict, None]: """Выгрузка ВСЕХ транзакций адреса через пагинацию по блокам""" base_url = "https://api.etherscan.io/api" current_block = start_block while True: async with httpx.AsyncClient() as client: resp = await client.get(base_url, params={ "module": "account", "action": "txlist", "address": address, "startblock": current_block, "endblock": 99999999, "sort": "asc", "apikey": api_key, "offset": 10000, "page": 1, }) data = resp.json() if data["status"] != "1" or not data["result"]: break txs = data["result"] for tx in txs: yield tx if len(txs) < 10000: break current_block = int(txs[-1]["blockNumber"]) + 1 await asyncio.sleep(0.2) Важный нюанс: если в одном блоке > 10000 транзакций на адрес — цикл зависнет. Для таких случаев нужна вложенная пагинация с параметром page внутри блока.
Почему стоит использовать Alchemy или Moralis вместо Etherscan?
Alchemy и Moralis снимают большинство ограничений Etherscan. Alchemy имеет rate limit 25 req/s, что в 5 раз превышает лимит Etherscan (5 req/s). Вот сравнение:
| Параметр | Etherscan (free) | Alchemy (free tier) | Moralis (free tier) |
|---|---|---|---|
| Лимит записей | 10000 | Нет (pageKey) | Нет (cursor) |
| Rate limit | 5 req/s | 25 req/s | 100 requests/min |
| Streaming | Нет | WebSocket (Enhanced API) | WebSocket (Real-time) |
| Кросс-чейн | Нет | Да | Да |
Alchemy getAssetTransfers возвращает ETH + ERC-20 + ERC-721 одним вызовом.
import { Alchemy, Network } from 'alchemy-sdk'; const alchemy = new Alchemy({ apiKey: process.env.ALCHEMY_KEY, network: Network.ETH_MAINNET }); const transfers = await alchemy.core.getAssetTransfers({ fromAddress: '0x...', category: ['external', 'erc20', 'erc721', 'erc1155'], withMetadata: true, maxCount: 1000, }); if (transfers.pageKey) { const more = await alchemy.core.getAssetTransfers({ pageKey: transfers.pageKey, }); } Alchemy Asset Transfers API рекомендует использовать pageKey для пагинации. Moralis дополнительно отдаёт внутренние транзакции через getWalletTransactionsVerbose. Для быстрой оценки масштаба свяжитесь с нами — мы подготовим прототип за 2 дня.
Когда парсинг HTML-страниц оправдан?
Если API не отдаёт держателей токенов или verified-контракты, используем парсинг HTML. Следующий пример собирает держателей токена с Etherscan:
import httpx from bs4 import BeautifulSoup import asyncio async def get_token_holders(token_address: str, pages: int = 10) -> list[dict]: headers = { "User-Agent": "Mozilla/5.0", } holders = [] async with httpx.AsyncClient(headers=headers) as client: for page in range(1, pages + 1): resp = await client.get( f"https://etherscan.io/token/{token_address}", params={"a": "#holders", "p": page} ) soup = BeautifulSoup(resp.text, 'html.parser') table = soup.find('table', {'id': 'holdersTable'}) if not table: break for row in table.find_all('tr')[1:]: cols = row.find_all('td') if len(cols) >= 3: holders.append({ 'rank': cols[0].text.strip(), 'address': cols[1].find('a')['href'].split('/')[-1], 'quantity': cols[2].text.strip(), }) await asyncio.sleep(2) return holders Etherscan защищается Cloudflare — для объёмного сбора нужны резидентные прокси или официальное API. Парсинг BscScan и Solscan работает по аналогии.
Как избежать дублей при параллельном сборе?
При параллельном сборе из нескольких источников дубли неизбежны. Используем ON CONFLICT в PostgreSQL:
CREATE TABLE eth_transactions ( tx_hash CHAR(66) PRIMARY KEY, block_number BIGINT NOT NULL, from_address CHAR(42) NOT NULL, to_address CHAR(42), value NUMERIC(38) DEFAULT 0, gas_used BIGINT, status SMALLINT, ts TIMESTAMPTZ ); INSERT INTO eth_transactions VALUES (...) ON CONFLICT (tx_hash) DO NOTHING; Для событий уникальный ключ — (tx_hash, log_index).
Прямая работа с нодой
Для максимальной полноты (внутренние транзакции, storage slots, MEV) ставим собственную ноду с Erigon и --tracing. Это даёт:
- Все internal transactions без лимитов
- Трассировку вызовов
- Данные из storage
curl -X POST $ETH_RPC_URL \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_getBlockReceipts","params":["0x1234567"],"id":1}' eth_getBlockReceipts EIP-1559 возвращает все receipts блока одним запросом — альтернатива N отдельных вызовов eth_getTransactionReceipt.
Какие этапы включает сбор данных?
| Этап | Описание | Срок |
|---|---|---|
| Анализ источников | Определение необходимых данных и их расположения (API, HTML, нода) | 1-2 дня |
| Выбор инструментов | Etherscan API, Alchemy, Moralis или собственная нода | 1 день |
| Написание парсера | Асинхронный сбор с учётом rate limit и пагинации | 2-5 дней |
| Дедупликация и нормализация | Склеивание данных в единую схему | 1-2 дня |
| Тестирование на реальном объёме | Миллионы транзакций, проверка на пропуски | 2-3 дня |
| Деплой | Контейнеризация, мониторинг, автоматический перезапуск | 1-2 дня |
Наш процесс занимает от 3 до 10 дней в зависимости от сложности.
Что входит в результат
- Документация по схеме данных и методам сбора.
- Готовый код на Python/TypeScript с поддержкой асинхронности.
- Настройка пагинации под каждый эксплорер.
- Деплой на сервер или в облако (AWS Lambda, Kubernetes).
- Обучение команды работе с системой.
- Поддержка в течение месяца после сдачи.
Наш опыт — более 30 проектов по сбору и анализу данных on-chain. Получите консультацию — мы поможем выбрать оптимальный инструмент под вашу задачу. Закажите проект, и мы настроим сбор данных любой сложности.







