CoinGecko отслеживает тысячи криптовалют в одном месте, а его рыночные страницы содержат именно те структурированные данные, которые необходимы для мониторинга цен, портфельных инструментов и исследований: название монеты и тикер, текущую цену, рыночную капитализацию, объём торгов за 24 часа, изменение цены за 24 часа и за 7 дней, а также рыночный рейтинг. Для тех, кто следит за корзиной монет, эти публичные рыночные данные являются исходным материалом, а ручное копирование сотен строк занимает много времени и устаревает в момент завершения работы.

В этом руководстве показано, как извлекать данные о криптовалютах из CoinGecko с помощью Python. CoinGecko предоставляет официальный публичный API, и для любой рабочей нагрузки в промышленной среде следует обращаться именно к нему. Разбор HTML, описанный здесь, является обучающим запасным вариантом для полей или страниц, которые бесплатный уровень API не покрывает, и ограничен исключительно публичными рыночными данными фактического характера, не персональными. В конце статьи есть раздел о правовых аспектах; прочитайте его прежде, чем направлять этот подход на реальные объёмы.

Что вы создадите

Скрипт на Python, который загружает отрендеренную страницу CoinGecko через Crawling API, парсит каждую строку монеты с помощью BeautifulSoup и формирует один структурированный запись на монету. В качестве рабочего примера используется список топовых монет на главной странице CoinGecko. Мы извлекаем следующие поля:

  • Name полное название монеты, например Bitcoin или Ethereum.
  • Symbol краткий тикер, например BTC или ETH.
  • Price текущая цена в выбранной фиатной валюте.
  • Market cap общая рыночная капитализация монеты.
  • Volume объём торгов за последние 24 часа.
  • Change 24h процентное изменение цены за последние 24 часа.
  • Change 7d процентное изменение цены за последние 7 дней.
  • Rank рыночный рейтинг, отображаемый в строке.

Почему обычный запрос не работает на CoinGecko

Если запросить страницу рынка CoinGecko с помощью обычного HTTP-клиента, вы часто получите ответ со статусом 200, но лишь часть таблицы в теле. Два фактора работают против вас. Во-первых, CoinGecko гидратирует большую часть своей рыночной таблицы в браузере через JavaScript: цены и процентные изменения обновляются в реальном времени, и части строки заполняются только после выполнения скриптов страницы. При парсинге первого ответа можно получить тонкую или неполную таблицу вместо полного набора строк. Во-вторых, как и любой высоконагруженный сайт, CoinGecko следит за автоматизированным трафиком, и IP-адреса датацентров, обращающиеся к нему в плотном цикле, получают ограничение по частоте или проходят проверку до того, как достигают отрендеренного контента.

Поэтому работающий скрапер требует двух вещей в одном запросе: браузера, который фактически отрисовывает страницу, и IP-адреса, который сайт воспринимает как реального посетителя. Можно собрать это самостоятельно с помощью headless-браузера и пула ротирующих резидентских прокси, но сборка и поддержание этой инфраструктуры в рабочем состоянии составляет большую часть работы. Crawling API объединяет оба компонента в один вызов: вы передаёте ему URL с JavaScript-токеном, он отрисовывает страницу за доверенным IP и возвращает готовый HTML для парсинга.

API first

CoinGecko предлагает бесплатный публичный API, который уже возвращает название, тикер, цену, рыночную капитализацию, объём, рейтинг и процентные изменения в виде чистого JSON. Для промышленного использования используйте его: это санкционированный путь, который полностью исключает необходимость парсить HTML. Прибегайте к парсингу HTML только для полей или представлений, которые недоступны на вашем уровне API, и в любом случае соблюдайте установленные CoinGecko ограничения по частоте запросов.

Предварительные требования

Прежде чем писать код, нужно подготовить несколько вещей. Всё это займёт немного времени.

Базовые знания Python. Вы должны уверенно писать и запускать скрипты на Python, а также устанавливать пакеты с помощью pip. Если вы только знакомитесь с парсингом, руководство по BeautifulSoup станет хорошим дополнением к этому материалу.

Python 3.8 или новее. Проверьте версию командой python --version. Если Python не установлен, скачайте его с python.org или через дистрибутив Anaconda и убедитесь, что Python добавлен в PATH.

Аккаунт Crawlbase и JS-токен. Зарегистрируйтесь, откройте панель управления и скопируйте JavaScript (JS) токен со страницы документации аккаунта. Crawlbase предоставляет до 20 000 бесплатных запросов на старт, чего вполне достаточно для прохождения этого руководства. Относитесь к токену как к паролю: он аутентифицирует ваши запросы, поэтому не храните его в системе контроля версий.

Настройка проекта

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

bash
python --version

python -m venv coingecko_env
source coingecko_env/bin/activate

pip install crawlbase beautifulsoup4

В Windows активируйте окружение командой coingecko_env\Scripts\activate вместо строки с source. Две зависимости выполняют основную работу: crawlbase является официальным клиентом для Crawling API, а beautifulsoup4 парсит возвращаемый HTML, позволяя извлекать отдельные поля по CSS-селектору. Модули json и csv входят в стандартную библиотеку, поэтому дополнительно устанавливать ничего не нужно.

Шаг 1: Получение отрендеренной страницы CoinGecko

Начните с получения готовой страницы. Импортируйте класс CrawlingAPI, инициализируйте его с вашим JS-токеном и запросите URL страницы монет CoinGecko. CoinGecko обновляет части своей таблицы на стороне клиента, поэтому передайте параметры ajax_wait и page_wait, чтобы дождаться динамического контента перед захватом страницы. Проверка cb_status (legacy pc_status) от Crawlbase перед парсингом позволяет сразу замечать ошибки, а не замалчивать их.

python
from crawlbase import CrawlingAPI

api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"})

OPTIONS = {
    "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/122.0",
    "ajax_wait": "true",
    "page_wait": 5000,
}

def crawl(page_url):
    response = api.get(page_url, OPTIONS)
    if response["headers"]["cb_status"] == "200":
        return response["body"].decode("utf-8")
    print(f"Request failed: {response['headers']['cb_status']}")
    return None

if __name__ == "__main__":
    coins_url = "https://www.coingecko.com/"
    html = crawl(coins_url)
    print(html[:500] if html else "No HTML returned")

Оба параметра ожидания важны для частично клиентски-рендеримого источника. ajax_wait указывает API дождаться завершения загрузки асинхронного контента, а page_wait задерживает выполнение на фиксированное количество миллисекунд после загрузки, чтобы поздно рендеримые ячейки появились до захвата страницы. Пяти секунд достаточно для начала; увеличьте значение, если строки возвращаются неполными. Запустите скрипт командой python coingecko_scraper.py, и вы должны увидеть реальную разметку рынка CoinGecko, а не заглушку. Это подтверждает, что рендеринг работает, прежде чем вы напишете хоть один селектор.

Crawlbase Crawling API

CoinGecko требует отрендеренную страницу за доверенным IP в одном вызове, именно это и настраивают параметры ajax_wait и page_wait, описанные выше. Crawling API принимает JS-токен, запускает страницу в реальном браузере, ротирует резидентские IP на стороне сервера и возвращает готовый HTML, избавляя вас от необходимости управлять headless-флотом и пулом прокси самостоятельно. Начните с публичной рыночной страницы на бесплатном уровне.

Шаг 2: Парсинг строк монет

Страница монет CoinGecko представляет собой таблицу, в которой каждая строка соответствует одной монете. Загрузите отрендеренный HTML в BeautifulSoup, выберите строки тела таблицы и считайте каждую ячейку по её атрибуту данных. CoinGecko помечает свои рыночные ячейки значениями data-coin-table-target, что позволяет стабильно обращаться к полям по их именам, а не по хрупким позициям столбцов.

python
from bs4 import BeautifulSoup

def text_of(row, selector):
    el = row.select_one(selector)
    return el.get_text(strip=True) if el else None

def parse_coins(html):
    soup = BeautifulSoup(html, "html.parser")
    rows = soup.select("table tbody tr")
    coins = []
    for row in rows:
        name = text_of(row, '[data-coin-table-target="coinName"]')
        if not name:
            continue
        coins.append({
            "rank": text_of(row, "td:nth-child(2)"),
            "name": name,
            "symbol": text_of(row, '[data-coin-table-target="coinSymbol"]'),
            "price": text_of(row, '[data-coin-table-target="price"]'),
            "change_24h": text_of(row, '[data-coin-table-target="priceChange24h"]'),
            "change_7d": text_of(row, '[data-coin-table-target="priceChange7d"]'),
            "volume_24h": text_of(row, '[data-coin-table-target="volume"]'),
            "market_cap": text_of(row, '[data-coin-table-target="marketCap"]'),
        })
    return coins

Вспомогательная функция text_of запрашивает один элемент внутри строки и возвращает его очищенный текст или None, если элемент отсутствует, поэтому монета, у которой нет какого-либо поля, не прерывает цикл. Рейтинг читается из второй ячейки столбца, название и тикер берутся из помеченных элементов, а цена, оба столбца изменений, объём и рыночная капитализация сопоставляются со значением data-coin-table-target. Защита if not name: continue пропускает строки-разделители или заголовки, не содержащие названия монеты.

Selectors drift

Имена классов и атрибуты data-coin-table-target на CoinGecko могут изменяться без предупреждения, а столбцы процентных изменений иногда располагаются иначе на узких экранах. Воспринимайте приведённые здесь селекторы как отправную точку, а не как контракт. Если поле возвращает None, заново проверьте живую страницу в инструментах разработчика браузера и обновите селектор. Периодическое обслуживание селекторов, это нормальная практика для любого промышленного скрапера, а не признак неисправности.

Шаг 3: Обработка пагинации по страницам монет

Одна рыночная страница содержит лишь часть полного списка. CoinGecko использует пагинацию с параметром запроса ?page=N, поэтому вы проходите по каждой странице, собирая монеты до установленного предела. Небольшая обёртка с повторными попытками вокруг функции получения данных не позволит одной медленной странице прервать весь процесс.

python
import time

def fetch_html(page_url, max_retries=2):
    for attempt in range(max_retries + 1):
        html = crawl(page_url)
        if html:
            return html
        if attempt < max_retries:
            print(f"Retrying ({attempt + 1}/{max_retries})...")
            time.sleep(1)
    print(f"Unable to fetch {page_url}")
    return None

def collect_all_coins(base_url, max_pages):
    all_coins = []
    for page in range(1, max_pages + 1):
        page_url = f"{base_url}?page={page}"
        html = fetch_html(page_url)
        if html:
            all_coins.extend(parse_coins(html))
        time.sleep(2)
    return all_coins

fetch_html повторяет неудачный запрос до двух раз с небольшой паузой, возвращая HTML при успехе и None, когда попытки исчерпаны. collect_all_coins проходит страницы от первой до вашего предела max_pages, чтобы длинный список не вышел за рамки, парсит каждую страницу в записи о монетах и накапливает их. Пауза time.sleep(2) между страницами задаёт темп выполнения, чтобы вы не перегружали сайт и оставались в рамках его ограничений по частоте запросов.

Шаг 4: Сборка полного скрипта

Теперь объедините все части в один запускаемый скрипт: пройдите по страницам, разберите каждую монету и экспортируйте записи в форматы JSON и CSV.

python
import csv
import json
import time
from crawlbase import CrawlingAPI
from bs4 import BeautifulSoup

api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"})

OPTIONS = {
    "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/122.0",
    "ajax_wait": "true",
    "page_wait": 5000,
}

def crawl(page_url):
    response = api.get(page_url, OPTIONS)
    if response["headers"]["cb_status"] == "200":
        return response["body"].decode("utf-8")
    print(f"Request failed: {response['headers']['cb_status']}")
    return None

def fetch_html(page_url, max_retries=2):
    for attempt in range(max_retries + 1):
        html = crawl(page_url)
        if html:
            return html
        if attempt < max_retries:
            time.sleep(1)
    return None

def text_of(row, selector):
    el = row.select_one(selector)
    return el.get_text(strip=True) if el else None

def parse_coins(html):
    soup = BeautifulSoup(html, "html.parser")
    rows = soup.select("table tbody tr")
    coins = []
    for row in rows:
        name = text_of(row, '[data-coin-table-target="coinName"]')
        if not name:
            continue
        coins.append({
            "rank": text_of(row, "td:nth-child(2)"),
            "name": name,
            "symbol": text_of(row, '[data-coin-table-target="coinSymbol"]'),
            "price": text_of(row, '[data-coin-table-target="price"]'),
            "change_24h": text_of(row, '[data-coin-table-target="priceChange24h"]'),
            "change_7d": text_of(row, '[data-coin-table-target="priceChange7d"]'),
            "volume_24h": text_of(row, '[data-coin-table-target="volume"]'),
            "market_cap": text_of(row, '[data-coin-table-target="marketCap"]'),
        })
    return coins

def collect_all_coins(base_url, max_pages):
    all_coins = []
    for page in range(1, max_pages + 1):
        html = fetch_html(f"{base_url}?page={page}")
        if html:
            all_coins.extend(parse_coins(html))
        time.sleep(2)
    return all_coins

def save_outputs(records):
    with open("coingecko_coins.json", "w") as f:
        json.dump(records, f, indent=2)
    if not records:
        return
    with open("coingecko_coins.csv", "w", newline="") as f:
        writer = csv.DictWriter(f, fieldnames=records[0].keys())
        writer.writeheader()
        writer.writerows(records)

def main():
    coins_url = "https://www.coingecko.com/"
    coins = collect_all_coins(coins_url, max_pages=2)
    save_outputs(coins)
    print(f"Saved {len(coins)} coins")

if __name__ == "__main__":
    main()

Скрипт обходит до двух рыночных страниц, получает каждую с обёрткой повторных попыток, парсит в записи о монетах и задаёт темп цикла двухсекундной паузой. save_outputs записывает как JSON-файл, так и CSV, используя ключи первой записи в качестве заголовка, чтобы данные были готовы в любом формате, удобном для вашего дальнейшего инструмента. Скорректируйте max_pages и базовый URL под тот срез рынка, который вас интересует.

Как выглядит результат

Запустите полный скрипт командой python coingecko_scraper.py, и вы получите чистую структурированную запись для каждой монеты, готовую к анализу, базе данных или таблице. Значения ниже являются условными заполнителями, демонстрирующими структуру, а не реальными котировками.

json
[
  {
    "rank": "1",
    "name": "Bitcoin",
    "symbol": "BTC",
    "price": "$X,XXX.XX",
    "change_24h": "+1.2%",
    "change_7d": "-3.4%",
    "volume_24h": "$XX,XXX,XXX,XXX",
    "market_cap": "$X,XXX,XXX,XXX,XXX"
  },
  {
    "rank": "2",
    "name": "Ethereum",
    "symbol": "ETH",
    "price": "$X,XXX.XX",
    "change_24h": "+0.6%",
    "change_7d": "+2.1%",
    "volume_24h": "$XX,XXX,XXX,XXX",
    "market_cap": "$XXX,XXX,XXX,XXX"
  }
]

Соответствующий CSV содержит те же столбцы, по одной строке на монету, что позволяет напрямую загрузить его в pandas или любую таблицу для сортировки по рыночной капитализации, фильтрации по изменению за 7 дней или построения графиков объёма. Далее этот же подход применим и к другим трекерам рынка; он близок к тому, что описано в руководстве по парсингу криптоцен с CoinMarketCap.

Сохранение работоспособности при масштабировании

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

  • Задавайте темп запросов. Плотный цикл запросов, быстрейший способ получить ограничение скорости или блокировку. Двухсекундные паузы, указанные выше, являются нижней границей, а не потолком; увеличивайте их для больших задач и соблюдайте опубликованные сайтом ограничения по частоте.
  • Используйте ротацию. Пул резидентских IP распределяет запросы по множеству реальных пользовательских адресов, чтобы ни один из них не превысил лимит. Crawling API делает это за вас; если вы собираете собственный стек, именно эту часть нужно правильно реализовать.
  • Следите за кодами статусов. Прогон, в котором начинают появляться значения cb_status, отличные от 200, сигнализирует о том, что текущая частота или уровень IP больше недостаточны. Воспринимайте это как сигнал для снижения активности, а не как шум, который можно игнорировать.

Для крупных обходов асинхронный Crawler ставит запросы в очередь и доставляет результаты на вебхук, что подходит для обработки большого количества страниц без удержания открытых соединений. Общую стратегию см. в руководстве как скрапить сайты без блокировок.

Законно ли скрапить CoinGecko?

Начните с очевидного лучшего пути: CoinGecko публикует официальный публичный API, который возвращает название монеты, тикер, цену, рыночную капитализацию, объём за 24 часа, процентные изменения и рейтинг в виде чистого JSON с задокументированными ограничениями по частоте и платными уровнями для большей пропускной способности. Для любого промышленного или коммерческого использования этот API является санкционированным маршрутом, и он намного стабильнее и уважительнее по отношению к платформе, чем парсинг отрендеренного HTML. Разбор HTML в этом руководстве представляет собой обучающий запасной вариант для полей, доступных только на странице, или разовых исследований, а не замену API там, где он существует.

Допустимость парсинга самого сайта зависит от Условий использования CoinGecko, вашей юрисдикции и того, что вы делаете с данными. Ознакомьтесь с Условиями CoinGecko и его файлом robots.txt, считая оба документа границей дозволенного в отношении того, что вы собираете и с какой скоростью. Поддерживайте объём запросов на достаточно низком уровне, чтобы не перегружать серверы, и отдавайте предпочтение API для всего, что выходит за рамки лёгкого, нерегулярного использования. Рыночные данные здесь носят фактический и неперсональный характер, что ставит их в более безопасное положение, чем пользовательский контент, однако условия использования по-прежнему регулируют автоматизированный доступ.

Это руководство намеренно ограничено публичными рыночными данными: названиями монет, тикерами, ценами, рыночными капитализациями, объёмами, процентными изменениями и рейтингами, доступными любому пользователю без учётной записи. Оно не касается ничего, находящегося за логином или платным доступом, никаких персональных или аккаунтных данных, а также перераспределения фирменной символики, логотипов или редакционного контента CoinGecko, которые остаются его собственностью. Если для вашего проекта требуется надёжный высокообъёмный доступ, правильным путём является официальный API CoinGecko или лицензированный поток данных, а не более мощный скрапер. Обзор поставщиков см. в статье лучшие поставщики финансовых данных в мире.

Итоги

Ключевые выводы

  • Используйте API в первую очередь. Официальный публичный API CoinGecko возвращает название, тикер, цену, рыночную капитализацию, объём, изменение и рейтинг в формате JSON и является правильным выбором для любой промышленной задачи.
  • Парсинг HTML является запасным вариантом. При парсинге страницы учтите, что она частично рендерится на стороне клиента, поэтому перед парсингом используйте JS-токен Crawling API; параметры ajax_wait и page_wait управляют временем ожидания.
  • Обращайтесь к полям по атрибутам данных. CoinGecko помечает свои рыночные ячейки значениями data-coin-table-target, поэтому читайте каждое поле по атрибуту, а не по хрупкой позиции столбца.
  • Реализуйте пагинацию и экспорт. Проходите страницы CoinGecko по параметру ?page=N до установленного предела, задавайте темп прогона короткими паузами и записывайте записи в JSON и CSV.
  • Оставайтесь в рамках публичных данных. Соблюдайте Условия использования CoinGecko, robots.txt и ограничения по частоте, работайте только с публичными рыночными данными и никогда не обращайтесь к логинам, аккаунтам или защищённому авторским правом контенту.

Часто задаваемые вопросы

Что лучше: использовать API CoinGecko или парсить HTML?

Используйте API. CoinGecko предлагает бесплатный публичный API, который возвращает название монеты, тикер, цену, рыночную капитализацию, объём за 24 часа, процентные изменения и рейтинг в виде чистого JSON с задокументированными ограничениями по частоте. Он стабильнее парсинга HTML и является санкционированным путём для промышленного использования. Парсите отрендеренную страницу только для полей или представлений, недоступных на вашем уровне API, соблюдая те же ограничения по частоте.

Почему обычный запрос возвращает только часть таблицы CoinGecko?

Потому что CoinGecko гидратирует части своей рыночной таблицы на стороне клиента с помощью JavaScript: цены и процентные изменения обновляются в реальном времени, и некоторые ячейки заполняются только после выполнения скриптов страницы в браузере. Обычный HTTP-запрос может вернуть статус 200 с тонкой или неполной таблицей. Чтобы получить полный набор строк, необходимо сначала отрендерить страницу, что и обеспечивает JS-токен Crawling API.

Нужен ли обычный токен или JS-токен для CoinGecko?

JS-токен. Обычный токен получает статичный HTML, который на частично клиентски-рендеримой странице может не содержать живые ячейки. JS-токен рендерит страницу в реальном браузере перед возвратом HTML, поэтому строки монет с ценами, изменениями, объёмом и рыночной капитализацией присутствуют, когда BeautifulSoup их парсит.

Какие данные можно извлечь из CoinGecko?

Публичные рыночные данные: название монеты и тикер, текущую цену, рыночную капитализацию, объём за 24 часа, процентное изменение за 24 часа и 7 дней, а также рыночный рейтинг. Это фактические, неперсональные рыночные данные, видимые любому посетителю. Избегайте всего, что находится за логином или платным доступом, и не перераспределяйте логотипы, фирменную символику или редакционный контент CoinGecko.

Мои селекторы возвращают None. Что изменилось?

Почти наверняка изменилась разметка CoinGecko. Имена классов и атрибуты data-coin-table-target могут меняться без предупреждения, а столбцы процентных изменений по-разному расположены на узких экранах. Заново проверьте живую страницу в инструментах разработчика браузера и обновите селекторы. Периодическое обслуживание селекторов является нормальной практикой для любого промышленного скрапера, что является ещё одной причиной отдавать предпочтение официальному API, когда это возможно.

Можно ли использовать извлечённые данные CoinGecko в коммерческих целях?

Относитесь к этому как к правовому вопросу, а не техническому. Условия использования CoinGecko регулируют автоматизированный доступ и повторное использование данных, а коммерческое или высокообъёмное использование, как правило, следует осуществлять через официальный API или платный уровень, а не через скрапер. Изучите условия, используйте API или лицензированный поток данных для любого масштабирования и обратитесь за юридической консультацией, прежде чем строить продукт на основе этих данных.

Начать создавать

Обходите любой сайт в масштабе, без борьбы с инфраструктурой.

Crawlbase берёт на себя прокси, отпечатки и CAPTCHA, чтобы ваша команда выпускала конвейеры данных вместо поддержки обвязки краулинга. 1 000 запросов бесплатно, без карты.

Самообслуживание · Звонок отдела продаж не требуется · Доступны корпоративные объёмы краулинга