G2, это платформа, куда покупатели программного обеспечения обращаются за мнениями реальных пользователей перед принятием решения. Страницы отзывов на ней содержат именно тот сигнал, который нужен командам по продукту, продажам и конкурентной разведке в масштабе: звёздные рейтинги, заголовки отзывов, сам текст каждого отзыва, должность или сегмент компании рецензента и дата публикации. Проблема в том, что G2 динамически отрисовывает эти страницы и использует мощную защиту в стиле Cloudflare от ботов, поэтому обычный HTTP-запрос из Node получает задание ещё до того, как увидит хоть один отзыв.

В этом руководстве показано, как надёжно парсить отзывы G2 с помощью JavaScript. Вы создадите небольшой Node.js-скрипт, который получает отрисованную страницу отзывов через Crawling API, парсит её с помощью cheerio и извлекает чистую запись по каждому отзыву. Всё руководство ограничено публичными данными отзывов, а раздел о легальности в конце, не шаблонный текст, поэтому прочитайте его, прежде чем направить это на реальный объём.

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

Node.js-скрипт, который принимает публичный URL страницы отзывов о продукте G2, получает отрисованный HTML через Crawling API и извлекает структурированный список отзывов. В качестве сквозного примера используем страницу отзывов о публичном продукте. Из каждого отзыва будем извлекать следующие поля:

  • Review title короткий заголовок, который рецензент даёт своему отзыву.
  • Star rating числовая оценка, например «4.5».
  • Review text текст отзыва.
  • Reviewer role or segment публичная должность или метка размера компании, указанная в отзыве.
  • Date дата публикации отзыва.

Наряду с этим мы также получим небольшой контекст на уровне страницы: название продукта и средний рейтинг. Это даёт каждой пачке отзывов точку отсчёта.

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

Если запросить URL страницы отзывов G2 с помощью обычного HTTP-клиента, например fetch или axios, данные отзывов не вернутся. Против вас работают два фактора. Во-первых, G2 отрисовывает большую часть контента отзывов в браузере, поэтому полученный HTML неполный до выполнения скриптов страницы. Во-вторых, что важнее, G2 применяет агрессивную защиту от ботов: IP дата-центров, отсутствующие fingerprint браузера и паттерны запросов, характерные для парсеров, получают страницу с заданием или полную блокировку вместо контента. Вы увидите 403, CAPTCHA или страницу «Проверка браузера», а не отзывы.

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

G2 is a hard target

Защита G2 от ботов сильнее, чем у типичного сайта, поэтому Crawling API использует специализированный путь для него, а не обычный запрос. Если после регистрации ваши запросы к G2 всё ещё возвращают задания, обратитесь в поддержку для включения специальной обработки G2 на вашем аккаунте. После включения приведённый ниже код работает без изменений.

Требования

Перед написанием кода необходимо подготовить несколько вещей. Это займёт немного времени.

Базовые знания JavaScript и Node. Вы должны уметь писать и запускать Node.js-скрипты, а также устанавливать пакеты через npm. Если вы только начинаете создавать парсеры в этом стеке, наше руководство по созданию веб-парсера с Node.js, хорошая отправная точка.

Node.js установлен. Проверьте версию командой node --version. Если Node.js не установлен, скачайте LTS-сборку с nodejs.org и запустите установщик для вашей операционной системы.

Аккаунт Crawlbase и токен. Зарегистрируйтесь, откройте дашборд и скопируйте токен запроса со страницы документации аккаунта. Относитесь к токену как к паролю: он аутентифицирует ваши запросы, поэтому не храните его в системе контроля версий и ни в каком зафиксированном файле.

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

Создайте новую папку проекта, инициализируйте её и установите две библиотеки, необходимые парсеру: официальный клиент Crawlbase и cheerio для парсинга.

bash
mkdir g2-reviews-scraper
cd g2-reviews-scraper
npm init --yes

npm install crawlbase cheerio

Две зависимости выполняют работу: crawlbase, официальный клиент для Crawling API, а cheerio предоставляет jQuery-подобный API для запроса возвращённого HTML, позволяя извлекать отдельные поля по CSS-селектору. Для извлечения данных не нужны Express, база данных или веб-сервер; они относятся к тому, что вы создадите вокруг парсера, а не к самому парсеру.

Шаг 1: получение отрисованной страницы отзывов

Начните с получения страницы. Импортируйте класс CrawlingAPI, инициализируйте его с помощью своего токена и запросите URL страницы отзывов о продукте. Проверка кода статуса перед парсингом позволяет обнаруживать ошибки явно, а не молча, это особенно важно на ресурсе, который при небрежной работе возвращает страницы с заданиями с кодом 200.

javascript
const { CrawlingAPI } = require("crawlbase");

const api = new CrawlingAPI({ token: "YOUR_CRAWLBASE_TOKEN" });

async function crawl(pageUrl) {
  const response = await api.get(pageUrl);
  if (response.statusCode === 200) {
    return response.body;
  }
  console.error(`Request failed: ${response.statusCode}`);
  return null;
}

(async () => {
  const pageUrl = "https://www.g2.com/products/xcode/reviews";
  const html = await crawl(pageUrl);
  console.log(html ? html.slice(0, 500) : "No HTML returned");
})();

Сохраните файл как scraper.js и запустите командой node scraper.js. Если всё настроено правильно, вы увидите реальную разметку отзывов в первых 500 символах, а не страницу с заданием. Эта единственная проверка подтверждает самое сложное, прохождение защиты G2, ещё до того, как вы напишете хоть один селектор. Если G2 когда-либо вернёт страницу, которой нужна отрисовка на стороне клиента для заполнения, добавьте JavaScript-токен и параметры ajax_wait и page_wait в запрос; для стандартной страницы отзывов обычной выборки через API обычно достаточно.

Crawlbase Crawling API

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

Шаг 2: парсинг отзывов с помощью cheerio

Получив пригодный HTML, загрузите его в cheerio и пройдитесь по списку отзывов. G2 размещает каждый отзыв в виде повторяющейся карточки, поэтому паттерн такой: один раз выберите контекст уровня страницы, затем итерируйте элементы отзывов и извлекайте одни и те же поля из каждого. Оберните извлечение в try/catch, чтобы одна некорректная карточка не прервала весь процесс.

javascript
const cheerio = require("cheerio");

function parseReviews(html) {
  try {
    const $ = cheerio.load(html);
    const data = {
      productName: $(".product-head [itemprop=name]").text().trim(),
      averageStars: $("#products-dropdown .fw-semibold").first().text().trim(),
      reviews: [],
    };

    $(".nested-ajax-loading > div.paper").each((_, el) => {
      const card = $(el);
      const title = card.find("[itemprop=name]").first().text().trim();
      const stars = card.find("[itemprop='ratingValue']").attr("content");
      const text = card.find(".pjax").text().trim();
      const role = card.find("[ue=tooltip]")
        .map((_, label) => $(label).text().trim())
        .get()
        .join(", ");
      const date = card.find(".x-current-review-date").text().trim();

      data.reviews.push({ title, stars, text, role, date });
    });

    return data;
  } catch (error) {
    console.error("Parse error:", error.message);
    return null;
  }
}

Несколько моментов заслуживают внимания. Звёздный рейтинг считывается из атрибута content элемента ratingValue, а не из видимого текста, поскольку G2 чисто представляет числовую оценку именно там. Должность или сегмент рецензента извлекается из меток подсказок, которые G2 прикрепляет к каждой карточке, и объединяется в одну строку, поскольку отзыв может иметь более одной публичной метки, например должность плюс полоса размера компании. Все значения обрезаются, чтобы не хранить отзывы с пробелами.

Selectors drift

Имена классов и разметка G2 меняются без предупреждения. Относитесь к приведённым выше селекторам как к начальному шаблону, а не к контракту. Если поле возвращается пустым для каждого отзыва, проверьте живую страницу в инструментах разработчика браузера и обновите селектор. Периодическое обслуживание селекторов, это норма для любого производственного парсера, а не признак неисправности.

Шаг 3: сборка воедино

Теперь соедините получение и парсинг в один запускаемый скрипт. Получите отрисованный HTML, передайте его парсеру и выведите структурированный результат. Это весь парсер в одном файле.

javascript
const { CrawlingAPI } = require("crawlbase");
const cheerio = require("cheerio");

const api = new CrawlingAPI({ token: "YOUR_CRAWLBASE_TOKEN" });

async function crawl(pageUrl) {
  const response = await api.get(pageUrl);
  if (response.statusCode === 200) {
    return response.body;
  }
  console.error(`Request failed: ${response.statusCode}`);
  return null;
}

function parseReviews(html) {
  const $ = cheerio.load(html);
  const data = {
    productName: $(".product-head [itemprop=name]").text().trim(),
    averageStars: $("#products-dropdown .fw-semibold").first().text().trim(),
    reviews: [],
  };

  $(".nested-ajax-loading > div.paper").each((_, el) => {
    const card = $(el);
    data.reviews.push({
      title: card.find("[itemprop=name]").first().text().trim(),
      stars: card.find("[itemprop='ratingValue']").attr("content"),
      text: card.find(".pjax").text().trim(),
      role: card.find("[ue=tooltip]")
        .map((_, label) => $(label).text().trim()).get().join(", "),
      date: card.find(".x-current-review-date").text().trim(),
    });
  });

  return data;
}

(async () => {
  const pageUrl = "https://www.g2.com/products/xcode/reviews";
  const html = await crawl(pageUrl);
  if (!html) return;
  const data = parseReviews(html);
  console.log(JSON.stringify(data, null, 2));
})();

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

Запустите полный скрипт командой node scraper.js и получите чистый структурированный объект: продукт, его средний рейтинг и массив отзывов, каждый из которых готов для записи в JSON, CSV или базу данных.

json
{
  "productName": "Xcode",
  "averageStars": "4.5",
  "reviews": [
    {
      "title": "Solid IDE for native Apple development",
      "stars": "5",
      "text": "The integration with the Apple toolchain is seamless...",
      "role": "Software Engineer, Small-Business",
      "date": "Aug 12, 2025"
    }
  ]
}

Масштабирование на несколько страниц отзывов

Одна страница, это демонстрация; реальная задача охватывает каждую страницу отзывов о продукте, а зачастую и несколько продуктов. G2 разбивает отзывы на страницы, и нужная страница доступна по номеру, добавляемому к URL отзывов. Структура остаётся той же: формируйте URL страницы, получайте его через Crawling API, парсите той же функцией и собирайте строки до тех пор, пока страница не вернёт пустой результат.

javascript
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function scrapeAllPages(productSlug, maxPages) {
  const base = `https://www.g2.com/products/${productSlug}/reviews`;
  const all = [];

  for (let page = 1; page <= maxPages; page++) {
    const url = page === 1 ? base : `${base}?page=${page}`;
    const html = await crawl(url);
    if (!html) break;

    const { reviews } = parseReviews(html);
    if (!reviews.length) break;

    all.push(...reviews);
    await sleep(2000);
  }

  return all;
}

Два момента поддерживают здоровую работу. Цикл останавливается, как только страница возвращает пустой список отзывов, чтобы не делать запросы после конца списка. А вызов sleep между запросами задаёт темп; непрерывная нагрузка на G2 в плотном цикле, самый быстрый способ получить ограничение скорости даже при наличии управляемого API. Две секунды, разумная нижняя граница. Если вы получаете данные о многих продуктах, асинхронный Crawler позволяет передавать URL и получать результаты через вебхук, а не блокировать процесс при каждой выборке.

Как оставаться незаблокированным

Даже когда Crawling API поглощает большую часть защиты G2, несколько привычек помогают поддерживать долгосрочную работоспособность парсера и применимы к любому жёсткому коммерческому ресурсу.

  • Задавайте темп запросов. Распределяйте запросы с задержкой между страницами, а не отправляйте их так быстро, как позволяет цикл. Стабильный и медленный, значит дойдёт до конца; быстрый и жадный, получит задание.
  • Опирайтесь на ротацию. Пул резидентных IP распределяет запросы по множеству реальных пользовательских адресов, чтобы ни один из них не активировал ограничение частоты. Crawling API берёт это на себя; если вы когда-нибудь создадите собственный стек, именно это место стоит сделать правильно.
  • Читайте коды статусов. Запуск, при котором начинают возвращаться 403 или страницы с заданиями, сигнализирует о том, что текущая частота слишком высока. Снижайте нагрузку, а не продолжайте попытки агрессивнее.

Более широкий план описан в статьях как обойти Cloudflare и избежать обнаружения ботов и подробном руководстве как обойти CAPTCHA при парсинге. Если вы предпочитаете маршрутизировать собственный трафик из Node через ротирующий пул вместо использования управляемого API, Smart AI Proxy обеспечивает ту же ротацию резидентных IP как прокси-эндпоинт. А если следующими в вашем списке стоят отзывы на других сайтах, наше руководство по парсингу отзывов покупателей охватывает общий паттерн.

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

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

Несколько правил, которых стоит придерживаться. Собирайте только публично отображаемые данные отзывов: заголовок отзыва, звёздный рейтинг, текст отзыва, публичную метку должности или сегмента и дату, которые любой пользователь может видеть на публичной странице отзывов без входа в систему. Уважайте заявленные ожидания G2 и поддерживайте объём запросов достаточно низким, чтобы не перегружать серверы. Не собирайте персональные или контактные данные рецензентов, помимо публично отображаемых, и не пытайтесь обогатить публичный отзыв частной идентификацией.

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

Итоги

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

  • G2 блокирует обычные запросы. Его защита в стиле Cloudflare возвращает задания и 403 обычным HTTP-клиентам, поэтому самое сложное, получить чистый HTML, а не распарсить его.
  • Crawling API делает тяжёлую работу. Он получает страницу за ротирующими резидентными IP со специальной обработкой G2 в одном вызове, избавляя от необходимости самостоятельно запускать headless-парк и пул прокси.
  • cheerio выполняет извлечение. Итерируйте карточки отзывов и сопоставляйте заголовок, рейтинг, текст, публичную должность или сегмент и дату с актуальными селекторами, учитывая их возможное изменение.
  • Масштабируйтесь через цикл по страницам. Добавляйте номер страницы, получайте, парсите и добавляйте задержку для каждого запроса, чтобы долгий запуск не активировал ограничение скорости.
  • Оставайтесь в рамках публичных данных. Соблюдайте Условия использования G2 и robots.txt, собирайте только публично отображаемые поля отзывов и никогда не трогайте авторизационные данные, закрытые данные или персональную информацию рецензентов.

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

Почему обычный запрос не возвращает отзывы с G2?

Потому что G2 применяет агрессивную защиту от ботов и частично отрисовывает контент отзывов на стороне клиента. Прямой HTTP-запрос из Node попадает на страницу с заданием, CAPTCHA или 403 ещё до получения какой-либо разметки отзывов. Чтобы получить реальные данные, необходимо получить страницу с IP, который G2 воспринимает как реального посетителя, с отрисовкой при необходимости, именно это обеспечивает Crawling API.

Нужна ли специальная настройка именно для G2?

Да. Защита G2 сильнее, чем у типичного сайта, поэтому Crawling API использует специализированный путь для него. Если после регистрации ваши запросы к G2 всё ещё возвращают задания, обратитесь в поддержку для включения специальной обработки G2 на вашем аккаунте. После включения код из этого руководства работает без изменений.

Как обрабатывать пагинацию на страницах отзывов G2?

G2 разбивает отзывы на страницы, поэтому следующая страница доступна по номеру, добавляемому к URL отзывов, например ?page=2. Зациклитесь с первой страницы вверх, получайте и парсите каждую страницу той же функцией и останавливайтесь, как только страница не вернёт отзывов. Добавьте задержку между запросами, чтобы не получить ограничение скорости.

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

Почти наверняка разметка G2. Её имена классов и структура карточек меняются без предупреждения, поэтому селекторы, работавшие в прошлом месяце, могут перестать работать. Проверьте живую страницу отзывов в инструментах разработчика браузера и обновите селекторы. Периодическое обслуживание селекторов, это норма для любого производственного парсера, а не признак неправильного подхода.

Можно ли парсить имена рецензентов, электронные адреса или другие персональные данные с G2?

Нет, и это руководство этого не охватывает. Ограничьте сбор публично отображаемыми полями отзывов: заголовком, звёздным рейтингом, текстом отзыва, публичной меткой должности или сегмента и датой. Персональные или контактные данные рецензентов, помимо публично отображаемых, любые данные за авторизационной стеной и любые попытки обойти аутентификацию выходят за рамки данного руководства и нарушают условия G2. Для получения данных, кроме публичных отзывов, правильный путь, официальное соглашение о данных.

В какую базу данных хранить отзывы?

В любую, подходящую для вашего стека. Парсер возвращает простые JSON-объекты, которые легко помещаются в PostgreSQL, MySQL, MongoDB, облачное хранилище или даже в плоский JSON- или CSV-файл для небольшого запуска. Извлечение намеренно отделено от хранилища, чтобы вы могли выбрать последнее позже, не трогая парсер.

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

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

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

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