Use Cases

Веб-скрапинг академических исследований с решением CAPTCHA

Собираете данные для обзора литературы — и после тридцати-сорока запросов Google Scholar показывает капчу вместо результатов? Это стандартная реакция академических порталов на автоматический трафик. PubMed, Scopus, Web of Science, IEEE Xplore и JSTOR подключают reCAPTCHA v2/v3 или Cloudflare Turnstile именно там, где нагрузка выше обычной: при массовом поиске, экспорте библиографии и пакетном скачивании PDF. Дальше стоят две задачи — решать капчу программно через API и не выйти за лимит запросов, который у каждого источника свой. Ниже — рабочий сборщик цитирований на Python, чек-лист лимитов по источникам и разбор типичных сбоев.


Скрипт сбора данных о цитированиях

Класс ниже проверяет ответ сервера на признаки reCAPTCHA или Turnstile, при необходимости достаёт sitekey со страницы, отправляет его в CaptchaAI и подставляет полученный токен в повторный запрос. Он принимает произвольный прокси для сессии и сохраняет результаты в CSV.

import requests
import time
import re
from bs4 import BeautifulSoup
import csv

CAPTCHAAI_KEY = "YOUR_API_KEY"
CAPTCHAAI_URL = "https://ocr.captchaai.com"


def solve_captcha(method, sitekey, pageurl, **kwargs):
    data = {
        "key": CAPTCHAAI_KEY, "method": method,
        "googlekey": sitekey, "pageurl": pageurl, "json": 1,
    }
    data.update(kwargs)
    resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data=data)
    task_id = resp.json()["request"]
    for _ in range(60):
        time.sleep(5)
        result = requests.get(f"{CAPTCHAAI_URL}/res.php", params={
            "key": CAPTCHAAI_KEY, "action": "get",
            "id": task_id, "json": 1,
        })
        r = result.json()
        if r["request"] != "CAPCHA_NOT_READY":
            return r["request"]
    raise TimeoutError("Timeout")


class AcademicScraper:
    def __init__(self, proxy=None):
        self.session = requests.Session()
        if proxy:
            self.session.proxies = {"http": proxy, "https": proxy}
        self.session.headers.update({
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
            "AppleWebKit/537.36 Chrome/126.0.0.0 Safari/537.36",
            "Accept-Language": "en-US,en;q=0.9",
        })

    def search_papers(self, search_url, query, max_pages=10):
        """Search academic database for papers matching query."""
        all_papers = []

        for page in range(max_pages):
            url = f"{search_url}?q={query}&start={page * 10}"
            resp = self.session.get(url, timeout=30)

            # Handle CAPTCHA
            if self._has_captcha(resp.text):
                resp = self._solve_and_retry(resp.text, url)

            papers = self._parse_results(resp.text)
            if not papers:
                break  # No more results

            all_papers.extend(papers)
            print(f"Page {page + 1}: {len(papers)} papers")
            time.sleep(5)  # Respectful delay

        return all_papers

    def get_paper_details(self, paper_url):
        """Get detailed metadata for a single paper."""
        resp = self.session.get(paper_url, timeout=30)

        if self._has_captcha(resp.text):
            resp = self._solve_and_retry(resp.text, paper_url)

        soup = BeautifulSoup(resp.text, "html.parser")
        return {
            "title": self._safe_text(soup, "h1, .article-title"),
            "authors": self._safe_text(soup, ".authors, .author-list"),
            "abstract": self._safe_text(soup, ".abstract, #abstract"),
            "doi": self._safe_text(soup, ".doi, [data-doi]"),
            "journal": self._safe_text(soup, ".journal-name, .publication"),
            "year": self._safe_text(soup, ".pub-date, .year"),
            "citations": self._safe_text(soup, ".citation-count, .cited-by"),
        }

    def export_to_csv(self, papers, filename):
        """Export collected papers to CSV."""
        if not papers:
            return
        keys = papers[0].keys()
        with open(filename, "w", newline="", encoding="utf-8") as f:
            writer = csv.DictWriter(f, fieldnames=keys)
            writer.writeheader()
            writer.writerows(papers)
        print(f"Exported {len(papers)} papers to {filename}")

    def _has_captcha(self, html):
        return any(tag in html.lower() for tag in [
            'data-sitekey', 'g-recaptcha', 'cf-turnstile',
        ])

    def _solve_and_retry(self, html, url):
        match = re.search(r'data-sitekey="([^"]+)"', html)
        if not match:
            return self.session.get(url)

        sitekey = match.group(1)
        if 'cf-turnstile' in html:
            token = solve_captcha("turnstile", sitekey, url)
            return self.session.post(url, data={"cf-turnstile-response": token})
        else:
            token = solve_captcha("userrecaptcha", sitekey, url)
            return self.session.post(url, data={"g-recaptcha-response": token})

    def _parse_results(self, html):
        soup = BeautifulSoup(html, "html.parser")
        papers = []
        for item in soup.select(".gs_r, .search-result, article.result"):
            title_el = item.select_one("h3 a, .result-title a")
            if title_el:
                papers.append({
                    "title": title_el.get_text(strip=True),
                    "url": title_el.get("href", ""),
                    "snippet": self._safe_text(item, ".gs_rs, .abstract-snippet"),
                    "authors": self._safe_text(item, ".gs_a, .author-info"),
                })
        return papers

    def _safe_text(self, soup, selector):
        el = soup.select_one(selector)
        return el.get_text(strip=True) if el else ""


# Usage — Literature review
scraper = AcademicScraper(
    proxy="http://user:[email protected]:5000"
)

papers = scraper.search_papers(
    "https://scholar.example.com/scholar",
    query="machine learning CAPTCHA solving",
    max_pages=5,
)

# Get details for top papers
detailed = []
for paper in papers[:20]:
    if paper["url"]:
        detail = scraper.get_paper_details(paper["url"])
        detailed.append(detail)
        time.sleep(3)

scraper.export_to_csv(detailed, "literature_review.csv")

Функция solve_captcha() опрашивает res.php раз в 5 секунд — этого достаточно для reCAPTCHA v2/v3 и Turnstile. Если чаще ловите таймаут, увеличивайте число попыток, а не частоту опроса: слишком частые запросы к CaptchaAI ничего не ускорят, а вот лимит частоты запросов у источника задеть можно.


Построение сети цитирований

Для мета-исследований часто нужен не список статей, а граф связей — кто на кого ссылается. Функция ниже рекурсивно проходит по ссылкам «cited by», решает капчу на каждой странице через тот же AcademicScraper и ограничивает глубину и ширину обхода дерева, чтобы не собрать блокировку на первой же сотне запросов.

def bibliometric_analysis(scraper, seed_papers, depth=2):
    """Follow citations to build a citation network."""
    visited = set()
    network = []

    def _crawl(paper_url, current_depth):
        if current_depth > depth or paper_url in visited:
            return
        visited.add(paper_url)

        try:
            details = scraper.get_paper_details(paper_url)
            network.append(details)

            # Follow "cited by" links
            resp = scraper.session.get(f"{paper_url}/citations", timeout=30)
            if scraper._has_captcha(resp.text):
                resp = scraper._solve_and_retry(resp.text, f"{paper_url}/citations")

            citations = scraper._parse_results(resp.text)
            for cite in citations[:5]:  # Limit breadth
                if cite["url"]:
                    _crawl(cite["url"], current_depth + 1)
                    time.sleep(3)

        except Exception as e:
            print(f"Error crawling {paper_url}: {e}")

    for paper in seed_papers:
        _crawl(paper["url"], 0)

    return network

Глубина depth=2 и ограничение в пять ссылок на узел — не произвольные числа: граф цитирований растёт экспоненциально, и уже на третьем уровне вложенности легко выйти за часовой лимит запросов к источнику.


Какие CAPTCHA стоят на академических источниках

  • Google Scholar — reCAPTCHA v3, срабатывает при частых запросах подряд; данные — цитаты и статьи.
  • PubMed — reCAPTCHA v2 при повторяющихся поисковых запросах; данные — биомедицинская литература.
  • Web of Science — Cloudflare Turnstile на массовых выгрузках; данные — метрики цитирования.
  • Scopus — reCAPTCHA v2 при экспорте библиографии; данные — библиометрические показатели.
  • IEEE Xplore — reCAPTCHA v2 на поиске и скачивании PDF; данные — инженерные статьи.
  • JSTOR — reCAPTCHA v2 при доступе к страницам; данные — гуманитарные и социальные науки.

Если собираете данные из пяти-шести источников параллельно, закладывайте отдельный поток CaptchaAI на каждый: тарифа BASIC ($15/мес, 5 потоков) хватает на пилотный прогон по одной базе, а для регулярного сбора по всему списку выше подойдёт STANDARD ($30/мес, 15 потоков) или ADVANCE ($90/мес, 50 потоков), если параллельно работают несколько исследовательских скриптов.


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

  1. Google Scholar — задержка 10–15 секунд между запросами, не больше 40–50 страниц в час.
  2. PubMed — задержка 3–5 секунд, до 100 страниц в час.
  3. Web of Science — задержка 5–10 секунд, до 60 страниц в час.
  4. Scopus — задержка 5–10 секунд, до 60 страниц в час.
  5. IEEE Xplore — задержка 3–5 секунд, до 100 страниц в час.
  6. JSTOR — задержка 5–10 секунд, до 60 страниц в час.

Академические порталы банят IP-адреса быстро, обычно после первого же всплеска частоты — пороги считаются по скользящему окну, а не по разовому пику, поэтому держитесь консервативных цифр из списка, даже если тестовый прогон прошёл без единой капчи. И если помимо метаданных статьи вы сохраняете имена и аффилиации авторов, это уже персональные данные: собирайте только то, что реально нужно для анализа, и сверьтесь с требованиями 152-ФЗ «О персональных данных» или GDPR — в зависимости от того, откуда вы работаете и куда данные попадут.


Типичные сбои и как их чинить

  • CAPTCHA при каждом запросе — портал пометил IP-адрес; смените прокси и увеличьте задержку до 15+ секунд.
  • Пустой результат поиска — вместо выдачи вернулась страница с CAPTCHA; проверяйте признаки CAPTCHA до парсинга, а не после.
  • Нет аннотации — материал за платным доступом; используйте институциональный прокси или ищите открытую версию статьи.
  • Google Scholar блокирует IP-адрес — превышен лимит запросов; подождите 30 минут и смените IP-адрес.
  • Экспорт обрывается на середине — портал ограничивает размер выгрузки; выгружайте небольшими пакетами по 20–50 записей.

Большинство этих сбоев ловится одной проверкой перед парсингом: если в ответе есть data-sitekey, g-recaptcha или cf-turnstile, это CAPTCHA, а не пустая выдача — код выше уже учитывает это через _has_captcha().


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

Сколько запросов в час безопасно для Google Scholar?

Ориентируйтесь на 40–50 страниц в час с задержкой 10–15 секунд между запросами. Google Scholar агрессивно реагирует на автоматический трафик и банит IP-адрес быстрее, чем большинство других академических источников.

Законно ли собирать данные из PubMed, Scopus и IEEE Xplore?

Публичные метаданные — названия, авторы, аннотации — обычно доступны для сбора. Полнотекстовый доступ зависит от лицензии издателя. У PubMed есть официальный API E-utilities для программного доступа — если он покрывает вашу задачу, используйте его вместо парсинга страниц.

Что делать, если аннотация недоступна из-за платного доступа?

CAPTCHA тут ни при чём — это ограничение по лицензии издателя. Используйте институциональный прокси университета или библиотеки, либо ищите открытую версию статьи: препринт, авторский архив или PMC.

Нужен ли отдельный поток CaptchaAI на каждый источник?

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

Что делать, если robots.txt источника запрещает автоматический доступ?

Уважайте robots.txt и условия использования конкретного портала — CaptchaAI решает техническую задачу CAPTCHA, но не отменяет политику владельца сайта. Если правила запрещают автоматический сбор данных, ищите официальный API (как E-utilities у PubMed) или пишите в поддержку издателя за разрешением на программный доступ.


Похожие материалы


Подключите CaptchaAI к обзору литературы — получите API-ключ и автоматизируйте сбор академических данных.

Комментарии для этой статьи отключены.