Собираете данные для обзора литературы — и после тридцати-сорока запросов 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 потоков), если параллельно работают несколько исследовательских скриптов.
Как не словить блокировку: лимиты запросов
- Google Scholar — задержка 10–15 секунд между запросами, не больше 40–50 страниц в час.
- PubMed — задержка 3–5 секунд, до 100 страниц в час.
- Web of Science — задержка 5–10 секунд, до 60 страниц в час.
- Scopus — задержка 5–10 секунд, до 60 страниц в час.
- IEEE Xplore — задержка 3–5 секунд, до 100 страниц в час.
- 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) или пишите в поддержку издателя за разрешением на программный доступ.
Похожие материалы
- Ротация исходящих IP и её влияние на CAPTCHA
- Почему решение CAPTCHA не работает в браузере, но работает через API
Подключите CaptchaAI к обзору литературы — получите API-ключ и автоматизируйте сбор академических данных.