Руководство по Python · 2 мин чтения

Веб-скрапинг на Python requests через прокси

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

Скрапер — это сессия, а не цикл из get

Разница между скрапером, который живёт десять минут, и тем, что работает часами, почти никогда не в парсинге — она в том, как вы делаете запросы. Наивный for url in urls: requests.get(url) каждый раз открывает новое TCP-соединение, не шлёт никаких правдоподобных заголовков, не держит кук и долбит цель с одного IP, пока не получит 429 или блокировку.

Лечится это связкой requests.Session плюс прокси. Сессия держит пул соединений и хранит куки, поэтому пачка страниц грузится быстрее и выглядит как один связный клиент. Прокси с авторизацией подменяет ваш настоящий адрес дата-центровым выходом: сайт никогда не видит ваш исходный IP, а один заблокированный адрес не ставит крест на всей задаче. Прокси s4m говорят SOCKS5 на порту 1080 и HTTP на порту 3128, оба с авторизацией USER:PASS. Используйте socks5h://, чтобы DNS резолвился удалённо и имена хостов не утекали. Для одноразовых экспериментов есть отдельный бесплатный публичный список прокси — на ваш собственный риск.

Устойчивый скрапер на requests

Сессия, правдоподобные заголовки, повторы с backoff, ротация прокси и разбор через BeautifulSoup — всё в одном файле. Подставьте свои USER:PASS.

python
import random
import time
import requests
from bs4 import BeautifulSoup
from requests.exceptions import RequestException

USER, PASS, HOST = "USER", "PASS", "proxy.s4m.online"
POOL = [
    f"socks5h://{USER}:{PASS}@{HOST}:1080",  # needs requests[socks]
    f"http://{USER}:{PASS}@{HOST}:3128",
]

HEADERS = {
    "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36",
    "Accept-Language": "en-US,en;q=0.9",
}

def make_session(proxy):
    s = requests.Session()
    s.headers.update(HEADERS)
    s.proxies = {"http": proxy, "https": proxy}
    return s

def fetch(url, retries=4):
    for attempt in range(1, retries + 1):
        proxy = random.choice(POOL)
        try:
            s = make_session(proxy)
            r = s.get(url, timeout=(5, 20))
            if r.status_code == 429:
                raise RequestException("rate limited")
            r.raise_for_status()
            return r
        except RequestException as e:
            wait = 2 ** attempt + random.random()  # exponential backoff
            print(f"[{attempt}/{retries}] {proxy} -> {e}; sleeping {wait:.1f}s")
            time.sleep(wait)
    raise SystemExit(f"giving up on {url}")

html = fetch("https://quotes.toscrape.com/").text
soup = BeautifulSoup(html, "html.parser")
for q in soup.select(".quote"):
    text = q.select_one(".text").get_text(strip=True)
    who = q.select_one(".author").get_text(strip=True)
    print(f"{who}: {text}")

Что держит краулер живым

Четыре привычки, которые значат больше выбора парсера.

Шлите настоящий User-Agent

Стандартный UA от python-requests — это мгновенный флаг. Поставьте обычный браузерный User-Agent и заголовок Accept-Language, чтобы запросы не выделялись на фоне обычного трафика.

Отступайте, а не долбите повторами

На 429 или таймауте засыпайте с экспоненциальным backoff и джиттером и только потом повторяйте. Плотные циклы повторов лишь углубляют блокировку и жгут трафик прокси.

Меняйте точку выхода, а не только повтор

На каждой попытке берите свежий прокси из пула, чтобы один помеченный IP не останавливал всю задачу. С дата-центровыми прокси с оплатой за трафик масштабировать это дёшево.

Уважайте цель

Читайте robots.txt, ограничивайте параллельность и собирайте только то, что вам разрешено. Вежливый краулер к тому же и блокируется тяжелее.

От одного скрипта к настоящему конвейеру

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

Когда разовых скриптов станет мало, тот же эндпоинт прокси без изменений подставляется в Scrapy или в асинхронный клиент — про пул на aiohttp читайте в ротации прокси на Python. Всё строится из тех же кирпичиков s4m: дата-центровые SOCKS5 и HTTP-прокси с авторизацией, оплата за фактический трафик, плюс API и инструменты, чтобы проверять исходящие IP и порты. Если нужно лишь быстро сверить страну или протокол, начните с бесплатного списка.

FAQ

Ответы на вопросы

Нужен ли PySocks для примеров с SOCKS5?

Да. Поддержку SOCKS в requests даёт именно PySocks — ставьте его командой pip install "requests[socks]". HTTP-эндпоинт на порту 3128 работает с обычным requests без каких-либо дополнительных зависимостей.

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

Шлите правдоподобный User-Agent, сдерживайте частоту запросов, отступайте на ответах 429 и меняйте исходящие IP через пул прокси. Блокировки почти всегда о поведении и репутации адреса, а не о том, каким парсером вы пользуетесь.

Являются ли прокси s4m резидентными?

Нет. s4m продаёт дата-центровые SOCKS5 и HTTP-прокси с авторизацией и оплатой за фактический трафик. Отдельный бесплатный публичный список прокси — сторонний и на ваш собственный риск, он годится только для быстрых проверок.

Почему socks5h, а не socks5?

socks5h просит резолвить DNS сам прокси, поэтому имена хостов, которые вы обходите, никогда не утекают с вашей машины. Обычный socks5 сначала резолвит локально, а это выдаёт ваши цели.

Прокси, сделанные под скрапинг

Нацельте requests на proxy.s4m.online, крутите дата-центровые SOCKS5 и HTTP-выходы с авторизацией и платите только за израсходованный трафик. А когда цель ждёт один адрес — добавьте выделенный IP.

Эту страницу нашли по запросу

Реальные поисковые фразы, на которые отвечает эта страница — по ссылкам откроются материалы, раскрывающие тему глубже.