Quora, один из крупнейших публичных сайтов вопросов и ответов в Интернете, и размещённые там публичные вопросы и ответы служат полезным сигналом для исследования контента и тематик. Формулировка реального вопроса, количество привлечённых ответов и то, какие из них набрали больше всего голосов, вместе рассказывают, что люди на самом деле спрашивают по той или иной теме и какие формулировки находят отклик. Это делает публичные треды Quora практическим инструментом для SEO-планирования, генерации идей для контента и исследования аудитории.

В этом руководстве показано, как парсить Quora с помощью JavaScript и Node.js, используя cheerio. Вы создадите небольшой рабочий скрапер, который получает публичную страницу вопроса Quora через Crawling API с включённым рендерингом, парсит текст вопроса, тела ответов, а также количество ответов и голосов, затем экспортирует результат в JSON и CSV. Всё руководство ограничено публичными материалами Q&A. Имена авторов мы рассматриваем как персональные данные и агрегируем, а не профилируем; раздел о законности в конце не является шаблонным текстом, поэтому прочитайте его перед тем, как направить скрапер на реальные объёмы данных.

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

Скрипт на Node.js, который принимает URL публичного вопроса Quora, получает отрендеренный HTML через Crawling API и извлекает структурированную запись вопроса и видимых ответов. Мы извлекаем следующие поля, перенесённые из исходного скрапера Quora:

  • Question text сам вопрос, например "How do I start playing video games?".
  • Question link канонический URL страницы вопроса.
  • Answer count общее количество ответов, которое сообщает вопрос, плюс количество ответов, присутствовавших на спарсенной странице.
  • Answer text тело каждого видимого ответа, собранное для агрегированного тематического анализа, а не для перепубликации.
  • Upvote count суммарное количество голосов за каждый ответ, основной сигнал популярности.
  • Answer position порядок появления ответа, позволяющий взвешивать по рангу.

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

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

Если запросить URL вопроса Quora с помощью простого HTTP-клиента, вы получите тонкую оболочку вместо треда. Quora рендерит вопрос, ответы и счётчики ответов в браузере с помощью JavaScript, поэтому исходный HTML поступает практически пустым до тех пор, пока скрипты страницы не запустятся. Вдобавок Quora оспаривает автоматизированный трафик: IP-адреса датацентров и паттерны запросов, не похожие на реальный браузер, перенаправляются на страницу входа или контентную стену прежде, чем достигают ответов.

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

Зачем нужен JS-токен

Crawlbase предлагает два типа токенов. Обычный токен получает статичный HTML; JavaScript (JS) токен предварительно рендерит страницу в реальном браузере. Quora загружает тело вопроса и все ответы на стороне клиента, поэтому именно JS-токен даёт здесь полную страницу. Обычный токен как правило возвращает пустой фрейм без ответов для парсинга.

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

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

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

Node.js версии 16 или выше. Проверьте версию командой node --version. Если она не установлена, установите с сайта Node.js или через менеджер версий, например nvm.

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

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

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

bash
node --version

mkdir quora-scraper && cd quora-scraper
npm init -y

npm install crawlbase cheerio

Две зависимости выполняют всю работу: crawlbase, официальный Node-клиент для Crawling API, а cheerio парсит возвращённый HTML с API в стиле jQuery, позволяя извлекать отдельные поля по CSS-селектору. Если селекторы для вас в новинку, руководство как краулить JavaScript-сайты, хороший дополнительный ресурс для ресурсоёмких целей, подобных этой.

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

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

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

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

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

const quoraUrl = 'https://www.quora.com/How-do-I-start-playing-video-games';
crawl(quoraUrl).then((html) => {
  console.log(html ? html.slice(0, 500) : 'No HTML returned');
});

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

Crawlbase Quora Scraper

Quora требует отрендеренного треда за доверенным IP в одном вызове, именно это вы только что увидели в функции crawl. Crawling API принимает JS-токен, запускает страницу в реальном браузере, ротирует резидентские IP на стороне сервера и возвращает готовый HTML, так что вам не нужно самостоятельно запускать headless-флот и пул прокси. Попробуйте сначала на публичной странице вопроса на бесплатном тарифе.

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

Имея в распоряжении отрендеренный HTML, загрузите его в cheerio и считайте поля. Текст вопроса и ссылка находятся в верхней части страницы; каждый ответ, повторяющийся блок ниже. Quora раскладывает ответы по контейнерам div.q-box, поэтому вы выбираете блоки ответов, а затем считываете тело и количество голосов из каждого. Защитное чтение каждого поля предотвращает сбой всего запуска из-за одного отсутствующего значения.

javascript
const cheerio = require('cheerio');

function parseQuestion(html, pageUrl) {
  const $ = cheerio.load(html);

  const questionText = $('div.puppeteer_test_question_title')
    .first()
    .text()
    .trim() || $('title').text().trim();

  const answers = [];
  $('div.q-box.qu-borderAll').each((i, el) => {
    const block = $(el);
    const answerText = block.find('.q-text').first().text().trim();
    if (!answerText) return;

    const upvoteRaw = block
      .find('.q-click-wrapper')
      .first()
      .text()
      .trim();

    answers.push({
      answerText,
      answerUpvoteCount: upvoteRaw || null,
      answerPosition: i + 1,
    });
  });

  return {
    question: {
      text: questionText,
      link: pageUrl,
      answerCountScraped: answers.length,
      answers,
    },
  };
}

Имена полей здесь перенесены напрямую из исходных выходных данных скрапера Quora: question.text, question.link, answerCountScraped, answers, answerText, answerUpvoteCount и answerPosition. Тело ответа мы считываем из .q-text, а суммарное количество голосов из элемента управления голосованием, затем индексируем каждый ответ по позиции, чтобы впоследствии взвешивать популярные ответы. Мы намеренно не фиксируем имя автора или ссылку на профиль в записи; следующий раздел объясняет этот выбор.

Селекторы устаревают

Имена классов Quora (q-box, q-text, q-click-wrapper и маркеры puppeteer_test_) обфусцированы и меняются без предупреждения. Считайте приведённые выше селекторы отправной точкой, а не постоянным контрактом. Когда поле возвращается пустым, повторно проинспектируйте живую страницу в инструментах разработчика браузера и обновите селектор. Периодическое обслуживание селекторов нормально для любого продакшн-скрапера, а не признак поломки.

Если вы хотите полностью избежать обслуживания селекторов, Crawling API также поставляется с готовым скрапером данных quora-question. Передайте { scraper: 'quora-question' } в объект параметров, и API вернёт разобранный JSON в response.json.body вместо сырого HTML, избавив вас от необходимости писать cheerio. Ручной путь через cheerio выше стоит изучить, поскольку он даёт вам точный контроль над тем, какие поля вы сохраняете, что важно в контексте приведённых ниже рекомендаций по конфиденциальности.

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

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

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

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

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

function parseQuestion(html, pageUrl) {
  const $ = cheerio.load(html);
  const questionText = $('div.puppeteer_test_question_title')
    .first().text().trim() || $('title').text().trim();

  const answers = [];
  $('div.q-box.qu-borderAll').each((i, el) => {
    const block = $(el);
    const answerText = block.find('.q-text').first().text().trim();
    if (!answerText) return;
    const upvoteRaw = block.find('.q-click-wrapper').first().text().trim();
    answers.push({
      answerText,
      answerUpvoteCount: upvoteRaw || null,
      answerPosition: i + 1,
    });
  });

  return {
    question: {
      text: questionText,
      link: pageUrl,
      answerCountScraped: answers.length,
      answers,
    },
  };
}

async function main() {
  const quoraUrl = 'https://www.quora.com/How-do-I-start-playing-video-games';
  const html = await crawl(quoraUrl);
  if (!html) return;
  const data = parseQuestion(html, quoraUrl);
  console.log(JSON.stringify(data, null, 2));
}

main();

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

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

json
{
  "question": {
    "text": "How do I start playing video games?",
    "link": "https://www.quora.com/How-do-I-start-playing-video-games",
    "answerCountScraped": 3,
    "answers": [
      {
        "answerText": "Playing video games is simple, the game will give you some rules, and you play by them.",
        "answerUpvoteCount": "7",
        "answerPosition": 1
      },
      {
        "answerText": "Start with a genre you already enjoy, then pick a beginner-friendly title and learn the controls slowly.",
        "answerUpvoteCount": "3.7K",
        "answerPosition": 2
      }
    ]
  }
}

Обратите внимание, что запись не содержит имён авторов или ссылок на профили. Количество голосов остаётся строкой, поскольку Quora сокращает большие числа ("3.7K"), и сохранение исходной метки позволяет избежать потерь при конвертации. Для тематических исследований текст вопроса плюс ранжирование по голосам, как правило, всё, что нужно.

Экспорт в JSON и CSV

Для исследования контента обычно нужны данные на диске, а не только в консоли. Встроенный модуль Node fs записывает JSON в одну строку, а небольшая вспомогательная функция разворачивает ответы в строки CSV, чтобы их можно было открыть в электронной таблице и отсортировать по голосам. Каждая строка CSV, один ответ, с повторяющимся текстом вопроса в качестве контекста.

javascript
const fs = require('fs');

function saveJson(data, file) {
  fs.writeFileSync(file, JSON.stringify(data, null, 2));
}

function csvCell(value) {
  const text = (value == null ? '' : String(value)).replace(/"/g, '""');
  return `"${text}"`;
}

function saveCsv(data, file) {
  const header = ['question', 'answerText', 'answerUpvoteCount', 'answerPosition'];
  const rows = data.question.answers.map((a) =>
    [data.question.text, a.answerText, a.answerUpvoteCount, a.answerPosition]
      .map(csvCell)
      .join(','),
  );
  fs.writeFileSync(file, [header.join(','), ...rows].join('\n'));
}

// In main(), after building `data`:
saveJson(data, 'quora_scraped.json');
saveCsv(data, 'quora_scraped.csv');

Столбцы CSV, вопрос, текст ответа, количество голосов и позиция, именно те поля, которые нужны для анализа контента или тематик. Идентичность автора намеренно отсутствует в обоих экспортах.

Масштабирование на множество вопросов

Один вопрос, это демо; тематическое исследование обычно подразумевает список вопросов по теме. Соберите интересующие вас URL вопросов (из поиска Quora, sitemap или собственного списка), затем обойдите их в цикле, получая каждый через Crawling API, парсите той же функцией и объединяйте записи. Поскольку каждая страница вопроса имеет одинаковую структуру, уже написанный парсер работает для всех без изменений.

javascript
async function scrapeMany(urls) {
  const all = [];
  for (const url of urls) {
    const html = await crawl(url);
    if (html) all.push(parseQuestion(html, url).question);
  }
  return all;
}

const questions = [
  'https://www.quora.com/How-do-I-start-playing-video-games',
  'https://www.quora.com/What-is-Quora',
];

scrapeMany(questions).then((rows) => {
  console.log(`Collected ${rows.length} questions`);
});

Регулируйте скорость цикла и держите объём умеренным. Quora следит за трафиком, похожим на скрапинг, поэтому распределение запросов во времени и их маршрутизация через ротирующиеся резидентские IP, Crawling API делает это за вас, поддерживают работоспособность запуска. Для общей методики см. руководство как парсить сайты, не попадая под блокировку. Если вы превращаете ранжирование по голосам в карту ключевых слов или тем, рабочий процесс из статьи как извлечь и проанализировать данные Google SEO хорошо дополнит эти результаты.

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

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

Главная проблема на такой платформе, как Quora, персональные данные. Имена авторов, ссылки на профили и учётные данные, которые люди прикрепляют к своим ответам, являются персональными данными, а написанный пользователем ответ, его контент. Именно поэтому скрапер в данном руководстве сохраняет только текст вопроса, тела ответов для агрегированного анализа, количество голосов и позицию ответа, намеренно отбрасывая имена авторов и ссылки на профили. Используйте результаты для анализа трендов, частотности тем и того, какие формулировки привлекают внимание. Не составляйте профили идентифицируемых людей, не перепубликуйте ответ конкретного человека с привязкой к его имени и не создавайте набор данных, выделяющий кого-либо конкретно. Если вы находитесь в ЕС или Калифорнии, GDPR и CCPA применяются, как только речь идёт о персональных данных: вам нужно законное основание для их обработки и необходимо соблюдать запросы на удаление. Это веская причина агрегировать и отбрасывать имена, а не хранить их.

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

Итоги

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

  • Quora рендерит треды на клиенте. Обычный запрос возвращает пустой фрейм, поэтому необходимо рендерить страницу с JS-токеном перед парсингом.
  • Один вызов обеспечивает и рендеринг, и доверенный IP. Crawling API с JS-токеном делает и то, и другое; ajax_wait и page_wait контролируют время ожидания загрузки ответов.
  • cheerio извлекает поля. Считайте текст вопроса, затем сопоставьте каждый блок ответа с его телом, количеством голосов и позицией, ожидая, что обфусцированные селекторы будут меняться.
  • Агрегируйте, не профилируйте. Сохраняйте текст вопроса, тела ответов и количество голосов для тематических исследований; отбрасывайте имена авторов и ссылки на профили, чтобы не составлять профили идентифицируемых людей.
  • Оставайтесь на публичных данных. Соблюдайте ToS и robots.txt Quora, держите объём умеренным, учитывайте GDPR и CCPA при работе с персональными данными и предпочитайте официальное соглашение для всего, что выходит за рамки лёгкого публичного исследования.

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

Почему обычный запрос возвращает пустую страницу от Quora?

Потому что Quora рендерит вопрос и все ответы на клиенте с помощью JavaScript. Исходный HTML почти пуст до тех пор, пока скрипты страницы не запустятся в браузере, а неаутентифицированные автоматизированные запросы нередко перенаправляются на страницу входа или контентную стену. Чтобы получить полный тред, нужно отрендерить его за доверенным IP, именно это делает за вас JS-токен Crawling API.

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

Используйте JS-токен. Обычный токен получает статичный HTML, который на Quora возвращается как пустой фрейм без ответов. JS-токен рендерит страницу в реальном браузере прежде, чем вернуть HTML, поэтому тело вопроса, ответы и количество голосов присутствуют при парсинге cheerio.

Можно ли использовать готовый скрапер данных Quora вместо написания cheerio?

Да. Crawling API предлагает скрапер данных quora-question. Передайте { scraper: 'quora-question' } в объект параметров, и API вернёт разобранный JSON в response.json.body вместо сырого HTML, так что вы полностью минуете cheerio. Собственный парсер всё равно стоит написать, когда вам нужен жёсткий контроль над тем, какие поля вы сохраняете, что важно для исключения персональных данных авторов из набора данных.

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

Почти наверняка разметка Quora. Её имена классов обфусцированы (q-box, q-text, маркеры puppeteer_test_) и меняются без предупреждения, поэтому селекторы, работавшие в прошлом месяце, могут сломаться. Повторно проинспектируйте живую страницу вопроса в инструментах разработчика браузера и обновите селекторы. Периодическое обслуживание селекторов нормально для любого продакшн-скрапера.

Можно ли хранить имена авторов, которые видны в ответах?

Относитесь к именам авторов, ссылкам на профили и учётным данным как к персональным данным и избегайте их хранения. Скрапер в данном руководстве намеренно их отбрасывает и сохраняет только вопрос, текст ответа для агрегированного анализа и количество голосов. Если вы всё же вынуждены работать с персональными данными, применяются GDPR и CCPA: вам нужно законное основание и необходимо соблюдать запросы на удаление, поэтому агрегирование и отбрасывание идентичностей, более безопасный подход по умолчанию.

Как не попасть под блокировку при парсинге Quora?

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

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

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

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

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