Посібник з 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.

Цю сторінку знаходять за запитами

Реальні пошукові фрази, на які відповідає ця сторінка — за посиланнями відкриються матеріали, що розкривають тему глибше.