Сценарии использования

Парсинг Google News на Python: RSS, CSV и настройка SOCKS5

Парсинг Google News на Python: получите RSS-данные, удалите дубли и сохраните их в CSV. Настройте SOCKS5-прокси Socks5.IO и проверьте маршрут через Requests.

Valerie QuinnValerie Quinn18 мин. чтения

Краткий ответ: Этот пример подойдёт разработчикам, которым нужно отслеживать заголовки, источники, даты и ссылки в Google News. Сначала протестируйте RSS-парсер на локальных данных, затем, если условия источника это допускают, получите одну ленту и сохраните результат в CSV. Отдельный раздел показывает, как настроить SOCKS5 в библиотеке Requests и проверить сетевой маршрут, не смешивая параметры региона RSS с географией IP-адреса.

В инструкции собраны Python-парсер RSS, локальный тест, экспорт в CSV и необязательная настройка SOCKS5. Вы отделите запрошенные параметры региональной версии Google News от метаданных, полученных в ответе, сохраните исходные ссылки и проверите маршрут тем же клиентом, который собирает ленту. Полный код приведён в статье — отдельный архив с программой не нужен.

Какой источник Google News собирать?

У Google News есть несколько разных интерфейсов: сайт news.google.com, RSS-представление и вкладка «Новости» в поиске Google. Не считайте их одинаковыми: у них могут различаться порядок публикаций, группировка, поля и доступность.

google-news-surfaces
Способ Когда подходит Что нужно проверить
Поисковый шаблон RSS Google News Для мониторинга заголовков с небольшим набором метаданных Текущую доступность, параметры региональной версии, XML-структуру и условия сбора
HTML сайта Google News Нужны поля, показанные именно на этой странице Поддерживать адаптер под конкретную страницу; при необходимости и наличии разрешения — проверить рендеринг
Вкладка «Новости» в поиске Google Исследуется именно этот поисковый интерфейс Сохранять контекст поиска и не смешивать результаты с news.google.com
Сторонний новостной или поисковый API Нужны структурированные данные по договору Проверить поддерживаемый интерфейс, схему, пагинацию, стоимость и права на дальнейшее использование

Используемый ниже шаблон RSS — веб-интерфейс, а не официальное версионируемое API Google для разработчиков. Это не полный архив новостей и не замена лицензированному контенту издателей. Перед сбором проверьте действующие условия источника и правила robots.txt. Если нужный маршрут недоступен или сбор не разрешён, остановитесь — не пытайтесь обойти ограничение, подключая дополнительные IP-адреса.

Три статьи, приведённые для сравнения, показывают разные подходы: API поставщика может возвращать структурированные результаты, а RSS обходится без части инфраструктуры для рендеринга страниц. В этой инструкции реализован именно RSS-сценарий. Сторонний API не представляется как сервис Google или продукт Socks5.IO.

Какие данные вернёт RSS-парсер?

RSS даёт метаданные для поиска и мониторинга публикаций. Запись Google News не содержит автоматически полный текст статьи, права на повторное использование фотографии или разрешение использовать материал для обучения моделей.

reference-news-fields
Поле Что означает в этом парсере Ограничение
title Заголовок в том виде, в котором он пришёл в ленте Не удаляйте автоматически название источника, разбивая строку по дефису
publisher Метка источника, если она есть в записи Такая метка не подтверждает личность автора
publisher_homepage URL из атрибута элемента source Это может быть главная страница издания, а не ссылка на статью
google_news_url Ссылка из элемента RSS Сохраняйте промежуточную ссылку Google News и не объявляйте её подтверждённым URL издателя
published_raw / published_at_utc Исходная дата из ленты и дата после перевода в UTC Дата публикации не равна времени сбора; пропущенные и некорректные даты помечаются
item_id GUID; если его нет — ссылка элемента Идентификатор нужен для удаления дублей внутри одного снимка, но не является универсальным ID новости
query / requested_edition Запрошенная поисковая фраза и параметры региональной версии Это входные параметры, а не наблюдаемая география IP-адреса
observed metadata / retrieved time Язык и ссылка канала, время получения в UTC и run_id Совпадение данных канала не доказывает, что каждая публикация вышла именно в этом регионе

В description могут находиться HTML и набор связанных ссылок. Это не обязательно краткое содержание статьи. В примере описание не используется как полный текст, доказательство тональности или основание считать несколько записей отдельными новостными событиями.

Как спарсить Google News на Python: пошаговая инструкция

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

Шаг 1. Установите зависимость Python

Используйте Python 3.10 или новее и создайте отдельное виртуальное окружение:

powershellpowershell
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install "requests[socks]>=2.32,<3"

Дальше вызывайте интерпретатор из этого окружения. В macOS/Linux путь будет .venv/bin/python. Для XML, CSV и дат в примере используются модули стандартной библиотеки Python.

Шаг 2. Разберитесь с запросом и региональными параметрами

Пример шаблона RSS-запроса:

texttext
https://news.google.com/rss/search?q=artificial+intelligence&hl=en-US&gl=US&ceid=US%3Aen

Собирайте URL через urlencode: не склеивайте вручную пробелы, кавычки и амперсанды. В примере предусмотрены две комбинации региональных параметров:

Флаг hl gl ceid
US en-US US US:en
GB en-GB GB GB:en

Эти параметры задают региональную версию ленты, но не меняют IP выхода и не доказывают географический охват. В справке Google News о настройках отдельно описаны язык, регион и персонализация интерфейса для вошедших в аккаунт пользователей. Не предполагайте, что RSS без авторизации повторяет персонализированную страницу в браузере.

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

edition-and-exit-checks

Шаг 3. Сохраните полный парсер

Сохраните код ниже в файл google_news_rss.py. По умолчанию он разбирает встроенную вымышленную ленту и не отправляет сетевые запросы. Живой режим запускается только явно и проверяет маршрут до и после получения RSS.

rss-field-excerpt

Точный фрагмент XML из встроенного вымышленного тестового набора, оформленный для чтения. Это не ответ, загруженный из Google News. Ниже приведена полная копируемая программа.

pythonpython
import argparse
import csv
import ipaddress
import json
import os
import re
import sys
import xml.etree.ElementTree as ET
from datetime import datetime, timezone
from email.utils import parsedate_to_datetime
from pathlib import Path
from urllib.parse import parse_qs, quote, urlencode, urlsplit
from uuid import uuid4
from xml.sax.saxutils import escape

import requests

EDITIONS = {"US": {"hl": "en-US", "gl": "US", "ceid": "US:en"},
            "GB": {"hl": "en-GB", "gl": "GB", "ceid": "GB:en"}}
MAX_BYTES = 2_000_000
COLUMNS = ["run_id", "retrieved_at_utc", "source_kind", "query", "requested_edition",
           "observed_language", "item_id", "title", "publisher", "publisher_homepage",
           "google_news_url", "published_raw", "published_at_utc", "review_reason"]


def search_url(query, edition, rss=True):
    path = "/rss/search" if rss else "/search"
    return "https://news.google.com" + path + "?" + urlencode({"q": query, **EDITIONS[edition]})


def demo_feed(query, edition):
    item = '''<item><title>Fictional research update - Demo Publisher</title>
<link>https://news.google.com/rss/articles/DEMO-A</link><guid>demo-a</guid>
<pubDate>Tue, 20 Oct 2026 10:00:00 GMT</pubDate>
<source url="https://publisher.example.invalid/">Demo Publisher</source></item>'''
    second = '''<item><title>Another fictional headline</title>
<link>https://news.google.com/rss/articles/DEMO-B</link><guid>demo-b</guid>
<pubDate>Tue, 20 Oct 2026 11:00:00 GMT</pubDate>
<source url="https://second.example.invalid/">Second Demo</source></item>'''
    return (f'<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"><channel>'
            f'<title>Local fictional feed</title><link>{escape(search_url(query, edition, False))}</link>'
            f'<language>{EDITIONS[edition]["hl"]}</language>'
            + item + item + second + '</channel></rss>').encode("utf-8")


def parse_feed(data, query, edition, run_id, retrieved_at, source_kind):
    if len(data) > MAX_BYTES:
        raise ValueError("Feed exceeds tutorial size limit")
    text = data.decode("utf-8-sig")
    if "<!DOCTYPE" in text.upper() or "<!ENTITY" in text.upper():
        raise ValueError("DTD and entity declarations are not supported")
    root = ET.fromstring(text)
    channel = root.find("channel")
    if root.tag != "rss" or channel is None:
        raise ValueError("Expected RSS channel, not HTML or another feed format")
    channel_link = channel.findtext("link", "").strip()
    p = urlsplit(channel_link)
    observed = parse_qs(p.query)
    expected = {"q": query, **EDITIONS[edition]}
    if (p.scheme != "https" or p.hostname != "news.google.com" or p.path != "/search"
            or any(observed.get(k) != [v] for k, v in expected.items())):
        raise ValueError("Channel search context is missing or differs from the request")
    language = channel.findtext("language", "").strip()
    if language != EDITIONS[edition]["hl"]:
        raise ValueError("Channel language differs from the requested edition")
    rows, seen, duplicates = [], set(), 0
    items = channel.findall("item")
    for item in items:
        title = item.findtext("title", "").strip()
        link = item.findtext("link", "").strip()
        link_parts = urlsplit(link)
        if not title or link_parts.scheme != "https" or link_parts.hostname != "news.google.com":
            raise ValueError("Missing title or expected Google News item link")
        identity = item.findtext("guid", "").strip() or link
        if identity in seen:
            duplicates += 1
            continue
        seen.add(identity)
        source = item.find("source")
        publisher = "" if source is None else (source.text or "").strip()
        homepage = "" if source is None else source.get("url", "").strip()
        raw_date = item.findtext("pubDate", "").strip()
        flags, published = [], ""
        try:
            dt = parsedate_to_datetime(raw_date)
            if dt.tzinfo is None:
                raise ValueError("Publication timezone missing")
            published = dt.astimezone(timezone.utc).isoformat()
        except (ValueError, TypeError, OverflowError):
            flags.append("publication_time_unknown")
        if not publisher:
            flags.append("publisher_unknown")
        rows.append(dict(zip(COLUMNS, [run_id, retrieved_at, source_kind, query, edition,
                    language, identity, title, publisher, homepage, link, raw_date,
                    published, ";".join(flags)])))
    return rows, {"items_seen": len(items), "duplicates_removed": duplicates,
                  "records_exported": len(rows), "observed_channel_link": channel_link,
                  "observed_language": language}


def make_session(use_proxy):
    session = requests.Session()
    session.trust_env = False
    session.headers["User-Agent"] = "NewsMetadataTutorial/1.0"
    if use_proxy:
        host = os.environ["SOCKS5IO_HOST"]
        port = int(os.environ["SOCKS5IO_PORT"])
        if not re.fullmatch(r"[A-Za-z0-9.-]+", host) or not 1 <= port <= 65535:
            session.close()
            raise ValueError("Check issued proxy host and port")
        user = quote(os.environ["SOCKS5IO_USER"], safe="")
        password = quote(os.environ["SOCKS5IO_PASSWORD"], safe="")
        proxy = f"socks5h://{user}:{password}@{host}:{port}"
        session.proxies.update(http=proxy, https=proxy)
    return session


def get_body(session, url, accepted_types):
    with session.get(url, timeout=(5, 20), allow_redirects=False, stream=True) as response:
        if response.status_code != 200:
            raise ValueError(f"HTTP {response.status_code}: stop; review access, quota or redirect")
        mime = response.headers.get("Content-Type", "").split(";", 1)[0].strip().lower()
        if mime not in accepted_types:
            raise ValueError("Unexpected content type; do not parse a challenge as news")
        body = bytearray()
        for chunk in response.iter_content(65536):
            body.extend(chunk)
            if len(body) > MAX_BYTES:
                raise ValueError("Response exceeds tutorial size limit")
        return bytes(body)


def check_exit(session):
    body = get_body(session, "https://api.ipify.org?format=json", {"application/json"})
    address = ipaddress.ip_address(json.loads(body)["ip"])
    if not address.is_global:
        raise ValueError("Exit checker returned a non-public address")
    return {"observed_ip": str(address), "checked_at_utc": datetime.now(timezone.utc).isoformat()}


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--query", default="artificial intelligence")
    parser.add_argument("--edition", choices=EDITIONS, default="US")
    parser.add_argument("--live", action="store_true")
    parser.add_argument("--approved", action="store_true")
    parser.add_argument("--proxy", action="store_true")
    parser.add_argument("--route-label", default="private-run")
    parser.add_argument("--out", type=Path, required=True)
    args = parser.parse_args()
    query = args.query.strip()
    if not query:
        parser.error("Query must not be empty")
    if args.proxy and not args.live:
        parser.error("The local fictional feed is not a proxy test")
    if args.live and not args.approved:
        parser.error("Review current source conditions and request budget before --approved")
    log_path = args.out.with_suffix(".run.json")
    if args.out.exists() or log_path.exists():
        parser.error("Use new output filenames; existing files will not be replaced")
    run_id = uuid4().hex
    url = search_url(query, args.edition)
    manifest = {"run_id": run_id, "query": query, "requested_edition": args.edition,
                "requested_feed_url": url, "proxy_requested": args.proxy,
                "route_label": args.route_label}
    if args.live:
        with make_session(args.proxy) as session:
            manifest["before"] = check_exit(session)
            data = get_body(session, url, {"application/rss+xml", "application/xml", "text/xml"})
            stamp = datetime.now(timezone.utc).isoformat()
            manifest["after"] = check_exit(session)
        manifest["same_observed_ip"] = manifest["before"]["observed_ip"] == manifest["after"]["observed_ip"]
        kind = "requested_google_news_rss"
    else:
        data = demo_feed(query, args.edition)
        stamp = datetime.now(timezone.utc).isoformat()
        kind = "synthetic_fixture"
    rows, stats = parse_feed(data, query, args.edition, run_id, stamp, kind)
    manifest.update(stats, source_kind=kind, retrieved_at_utc=stamp)
    with log_path.open("x", encoding="utf-8") as handle:
        json.dump(manifest, handle, indent=2)
    with args.out.open("x", encoding="utf-8", newline="") as handle:
        writer = csv.DictWriter(handle, fieldnames=COLUMNS)
        writer.writeheader()
        writer.writerows(rows)
    print(f"Saved {len(rows)} records; removed {stats['duplicates_removed']} within-feed duplicates.")


if __name__ == "__main__":
    try:
        main()
    except (ValueError, UnicodeError, ET.ParseError) as error:
        print("Stopped:", type(error).__name__, "- review response structure and source context.", file=sys.stderr)
        sys.exit(1)
    except (KeyError, TypeError, OSError, requests.RequestException):
        print("Stopped: inspect connection, issued fields and output paths privately.", file=sys.stderr)
        sys.exit(1)

Шаг 4. Запустите локальный тест и проверьте результат

powershellpowershell
python google_news_rss.py --out news-demo.csv
python google_news_rss.py --query "renewable energy" --edition GB --out news-gb-demo.csv

Каждый локальный запуск обрабатывает три вымышленные записи и сохраняет две уникальные строки. Повторный GUID demo-a удаляется, а заголовок с дефисом остаётся без изменений. Для обеих строк записываются общие время получения и run_id. При запуске с GB меняются параметры созданного тестового фида и контекст проверки; это не наблюдение, полученное из сети Великобритании.

actual-local-export

Результат локального запуска и поля CSV, оформленные для чтения. Значение source_kind=synthetic_fixture отделяет тестовые записи от данных, полученных из живой ленты.

Файл или поле Ожидаемое содержимое
news-demo.csv Две вымышленные записи метаданных с source_kind=synthetic_fixture
news-demo.run.json Запрос, указанный URL и регион, сводка канала, счётчики и общий run_id
published_at_utc Две распознанные даты публикации, отдельные от времени сбора
review_reason Для полного примера — пусто; при отсутствии даты или источника появится флаг

Чтобы проверить обработку ошибки, замените ссылку элемента demo-a внутри тестовых данных с https://news.google.com/ на https://publisher.example.invalid/ и запустите скрипт с новым именем выходного файла. Парсер должен остановиться до создания файлов. Чтобы проверить неполные метаданные, удалите у одной записи элемент pubDate: строка останется в CSV с пустым published_at_utc и меткой publication_time_unknown. Скрипт не будет придумывать дату.

missing-date-review

Результат после удаления pubDate из вымышленной записи, оформленный для чтения. Время сбора не подменяет неизвестную дату публикации.

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

Шаг 5. Отправьте один разрешённый запрос к живой ленте

Сначала проверьте актуальные условия доступа и задайте допустимый лимит сбора. Только после этого запускайте запрос:

powershellpowershell
python google_news_rss.py --live --approved --query "renewable energy" --edition US --route-label direct-US --out news-live-us.csv

Скрипт обращается к публичному JSON-сервису, описанному ipify, запрашивает одну RSS-ленту Google News, а затем повторно проверяет IP выхода через тот же объект Session. Если проверка не пройдёт, выполнение остановится. До использования изучите условия стороннего сервиса и последствия для конфиденциальности. Чтобы заменить его на разрешённый внутренний сервис, нужно реализовать обработку его фактического формата ответа. ipify не является API проверки Socks5.IO.

same-client-route-check

Фрагмент кода, а не подтверждение успешного живого подключения. Одна сессия не фиксирует выходной IP, если он меняется для каждого запроса.

Проверяйте CSV и журнал запуска с ограниченным доступом. Смотрите не только на HTTP-статус, но и на source_kind, query, observed_channel_link, observed_language и временные метки. Совпавшая сводка канала не подтверждает полноту результатов, локальное происхождение всех публикаций или идентичность персонализированному интерфейсу. Валидный пустой канал создаст CSV только с заголовками, но это не доказывает отсутствие релевантных новостей в других источниках.

Результат живого сбора зависит от сети и текущих условий источника. Локальный тест от них не зависит, но успешный разбор тестовой ленты не доказывает, что запрос Google или прокси-маршрут сработали.

Как подключить Socks5.IO, не меняя парсер?

Настройку прокси и разбор новостей нужно проверять независимо. За сетевой маршрут отвечает make_session(), а функции parse_feed() не нужны прокси-учётные данные и информация о том, какой IP передал ответ.

Откройте инструкцию Socks5.IO по подключению к резидентским прокси по логину и паролю — документация доступна на английском. В панели выберите доступную страну и режим сессии, затем скопируйте параметры подключения: хост, порт, имя пользователя и пароль прокси. Логин для панели управления не является логином прокси. Не переносите хост, порт или формат сессии из инструкции другого поставщика.

socks5io-subaccount

Снимок официальной документации, получен 14 октября 2026 года; области с учётными данными закрыты. Управление пользователем прокси отделено от логина в панель управления.

Задайте выданные параметры в отдельном процессе PowerShell:

powershellpowershell
$env:SOCKS5IO_HOST = Read-Host 'Issued proxy host'
$env:SOCKS5IO_PORT = Read-Host 'Issued proxy port'
$env:SOCKS5IO_USER = Read-Host 'Issued proxy username'
$newsProxySecret = Read-Host 'Issued proxy password' -AsSecureString
$env:SOCKS5IO_PASSWORD = [System.Net.NetworkCredential]::new('', $newsProxySecret).Password

Затем выполните тот же разрешённый запрос через прокси:

socks5io-endpoint

Снимок официальной документации, получен 14 октября 2026 года; области с учётными данными закрыты. Сгенерируйте собственные параметры — не копируйте значения с картинки.

powershellpowershell
python google_news_rss.py --live --approved --proxy --query "renewable energy" --edition US --route-label residential-US --out news-proxy-us.csv

Код экранирует учётные данные для URL и использует socks5h, чтобы DNS-запрос выполнялся на стороне прокси; это соответствует документации библиотеки Requests по SOCKS, доступной на английском. SOCKS5 — способ передать трафик, а не гарантия низкой задержки, высокой параллельности или полной анонимности. HTTP(S)-прокси тоже может подойти, если клиент и выданные параметры это позволяют.

Проверка TLS-сертификатов остаётся включённой. При ошибке прокси скрипт не переключается на прямое соединение. Параметр trust_env=False не даёт настройкам прокси из окружения незаметно изменить маршрут, но одновременно отключает чтение системной настройки доверенных сертификатов. При необходимости задайте разрешённый центр сертификации явно. Таймауты соединения и чтения вместе с ограничением размера ответа не задают общий дедлайн всей задачи.

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

В CSV могут храниться темы исследования, а журнал живого запуска содержит наблюдаемые IP-адреса. Ограничьте доступ к файлам и удаляйте чувствительные поля перед передачей. В отличие от журнала только с URL, этот скрипт сохраняет целиком сформированный URL RSS-запроса, включая поисковую тему; учётные данные прокси в него не входят. Не помещайте в запрос токены, закрытые темы исследования или персональные данные. Переменные окружения — не зашифрованное хранилище секретов. После работы удалите их:

powershellpowershell
Remove-Item Env:SOCKS5IO_PASSWORD
Remove-Variable newsProxySecret

Какой прокси и режим сессии выбрать для мониторинга новостей?

Не исходите из того, что каждому запросу Google News нужен резидентский IP-адрес. Начните с условий источника, нужных полей и небольшого разрешённого объёма.

Сценарий Что проверить Критерий приёмки
Небольшой разрешённый снимок RSS Сначала прямое подключение Структура ленты, контекст запроса и пригодность записей
Разрешённые наблюдения с заданным регионом выхода Резидентский прокси с поддержкой нужной локации Географию выхода и параметры ленты проверять раздельно
Короткая серия запросов, где важна непрерывность При необходимости — закреплённый или фиксированный маршрут Лимиты и фактическую стабильность сессии; Файлы cookie проверять отдельно
Источник принимает трафик из хостинговой сети Динамический прокси дата-центра Socks5.IO Корректность метаданных и стоимость для этого источника
Структурированный API по договору Интеграцию по документации API Права, поддерживаемый интерфейс, смысл полей и квоты

Если периодические наблюдения разрешены, для независимых запусков можно настроить отдельные сессии. Ротация не отменяет отказ источника и не добавляет квоту. Закреплённый IP не нужен только потому, что у двух записей есть ссылки: этот парсер по ним не переходит. Доступ к сайтам издателей — отдельный сценарий со своими условиями и парсером.

До покупки проверьте доступность стран, срок действия, минимальный объём заказа, поддержку протоколов, правила сессий и текущие условия тарифа. Заявленный размер глобального пула или показатель доступности не является измеренной долей успешных запросов к Google News. Для мониторинга оцените необходимые поля, свежесть данных и конкретный интерфейс источника. Динамический резидентский прокси Socks5.IO можно рассматривать, если задаче действительно нужны поддерживаемая локация или определённый маршрут.

Когда выбирать HTML, отрисовку в браузере или новостной API?

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

Если сбор HTML разрешён, сначала изучите конкретный интерфейс. Сохраните разрешённый ответ, найдите контейнеры статей и отдельно сопоставьте заголовок, источник, дату и ссылку. Не считайте HTTP 200 признаком успеха, пока не проверите, что вместо результатов не пришли экран согласия, входа в аккаунт или страница проверки. Относительное время вроде «2 часа назад» зависит от момента наблюдения и не является точной датой публикации. Если нужные элементы исчезли, остановите адаптер на обслуживание, а не выгружайте пустые поля как успешный результат.

Если отрисовка JavaScript необходима и разрешена, отдельно убедитесь, что браузерный клиент поддерживает нужные протокол и способ авторизации. SOCKS5 URL с учётными данными, который работает в Requests, не обязательно подойдёт Chromium. В этой статье нет непроверенного примера прокси для браузера и утверждения, что один CSS-селектор подходит всем интерфейсам Google News.

Сторонний Google News API может сократить объём поддержки парсера и рендеринга. Это не разрешение, выданное Google, и не часть подписки на прокси Socks5.IO. До покупки уточните, какие интерфейсы он охватывает, как задаются регионы и даты, как устроена пагинация, за что взимается плата и какие правила действуют для повторного использования данных. Сравнивайте не только цену запроса, но и полезность метаданных и расходы на поддержку.

Что делать при ответе 429, капче и изменении RSS?

Ошибки и неожиданные ответы не следует считать пустой лентой или обходить сменой IP. Остановитесь и проверьте условия доступа, тип ответа и формат данных.

Ситуация Действие
HTTP 401/403 Остановить сбор и проверить условия доступа; не менять IP-адрес автоматически
HTTP 429 Остановиться, проверить квоту и Retry-After; повторять запрос только в разрешённых пределах
Редирект Проверить адрес назначения и изменение источника; пример не следует за редиректом автоматически
Вместо XML пришёл HTML или страница проверки Отклонить ответ; не считать его пустой новостной лентой
Контекст канала отсутствует или изменился Остановить обработку и проверить адаптер; не подставлять предполагаемые значения
Нет источника или даты Сохранить исходные поля и отметить проблемную запись
Повторился ID записи Удалить дубль в пределах текущего снимка и сохранить счётчик
Таймаут сети Отдельно проверить маршрут и доступность точки подключения, не смешивая это с ошибкой разбора

Универсального числа запросов или срока, после которого Google блокирует клиента, нет. Пример не повторяет отказы, не меняет браузерные идентификаторы и не решает капчу. User-Agent в Requests сообщает приложению тип клиента, но не превращает скрипт в настоящий браузер. Случайная смена заголовков не заменяет разрешение на сбор и не исправляет парсер.

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

Пример: пилот мониторинга бренда в двух региональных версиях

Для планирования считайте комбинации поисковой темы, региональной версии и времени запуска — не гарантированные успешные запросы и не полное покрытие упоминаний. Например, команда коммуникаций проверяет 10 публичных тем в англоязычных версиях US и GB дважды в день. Это 40 комбинаций «запрос — регион — время» в сутки. Живой режим также делает две отдельные проверки IP на один запуск.

Команда фиксирует темы и две комбинации параметров региона. Если география IP важна для исследования, каждый маршрут настраивается и проверяется отдельно. Система хранит запрошенный и наблюдаемый контекст, ID записей, метки источников, даты, время получения и предупреждения. Дубли внутри одного ответа удаляются; публикации разных изданий остаются отдельными, пока команда не определит и не задокументирует метод группировки.

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

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

Итоги

Парсинг Google News на Python начинается с выбора нужного интерфейса и проверки метаданных, а не с увеличения числа запросов. Сначала запустите локальный тест RSS, проверьте дедупликацию и обработку дат. Затем, если это допускают актуальные условия источника, выполните небольшой живой запрос.

Если разрешённой задаче нужны конкретный сетевой маршрут или непрерывность сессии, настройте Socks5.IO по выданным учётным данным и проверьте соединение из клиента сбора. Храните отдельно параметры региональной версии, наблюдаемый IP выхода, контекст RSS и права на контент издателей. Увеличивайте объём только после того, как качество метаданных, их свежесть и контролируемые затраты соответствуют требованиям.

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

Сначала выберите интерфейс и набор полей. RSS позволяет собирать заголовки без браузерного рендеринга; для HTML нужен отдельный адаптер, а для стороннего API — отдельная интеграция. Безопасно соберите URL, проверьте контекст в ответе, сохраните даты и исходные ссылки. До разрешённого сетевого запроса протестируйте парсер на локальных данных. Прокси настраивайте и проверяйте отдельно: успешная передача трафика не подтверждает полноту новостей или право использовать публикации.

Последние статьи

Иллюстрация глобальной прокси-сети

Подключитесь к глобальной прокси-сети — каждое соединение будет быстрее и стабильнее

Начни бесплатно