Извлечение информации с сайтов: практические решения на Python

Раздел: Веб-разработка -> Парсинг и скрапинг

Основные подходы к веб-скрапингу

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

Фреймворк Scrapy для масштабного сбора

Как организовать эффективный парсинг тысяч страниц с минимумом кода?

Scrapy предоставляет готовую архитектуру: асинхронные запросы, управление паузой, встроенные экспортёры (CSV, JSON, XML) и поддержку прокси. Он оптимален для проектов, где требуется регулярное обновление данных.

import scrapy

class QuotesSpider(scrapy.Spider):
    name = "quotes"
    start_urls = ["http://quotes.toscrape.com/page/1/"]

    def parse(self, response):
        for quote in response.css("div.quote"):
            yield {
                "text": quote.css("span.text::text").get(),
                "author": quote.css("small.author::text").get(),
            }
        next_page = response.css("li.next a::attr(href)").get()
        if next_page is not None:
            yield response.follow(next_page, self.parse)

веб скрапинг python (веб-скрапинг на python)

После сохранения скрипта в файл quotes_spider.py команда scrapy runspider quotes_spider.py -o quotes.json запускает сбор и сохраняет результат в JSON.

Цели: извлечение больших объёмов однотипных данных (каталоги товаров, списки статей).

Типичные ошибки: отсутствие корректного User-Agent (сайт блокирует), неправильное следование по ссылкам (бесконечные циклы). Решение – установка USER_AGENT в настройках и проверка условия остановки пагинации.

Также часто забывают про DOWNLOAD_DELAY, чтобы не перегружать сервер. Рекомендуется 1-2 секунды.

Как выполнить простой запрос и разобрать HTML?

Библиотеки requests и BeautifulSoup идеальны для одностраничного сбора или быстрого прототипирования.

import requests
from bs4 import BeautifulSoup

url = "http://quotes.toscrape.com/page/1/"
response = requests.get(url)
soup = BeautifulSoup(response.text, "html.parser")
for quote in soup.select("div.quote"):
    text = quote.select_one("span.text").get_text(strip=True)
    author = quote.select_one("small.author").get_text(strip=True)
    print(f"{text} - {author}")

Цель: быстрое извлечение информации с одной страницы или небольшого набора URL.

Проблемы: отсутствие обработки ошибок сети (requests.exceptions.RequestException), неверное указание селекторов. Решение – оборачивать запрос в try/except и тестировать селекторы в браузере.

Как обработать динамический контент с JavaScript?

Когда данные подгружаются после рендеринга, нужен браузерный движок. Selenium управляет полноценным браузером, но медленнее. Playwright – современная альтернатива с поддержкой всех браузеров.

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    page = browser.new_page()
    page.goto("https://example.com")
    # ждем появления элемента
    page.wait_for_selector("div.result")
    content = page.inner_text("div.result")
    print(content)
    browser.close()

Цель: сбор данных с SPA-приложений, страниц, использующих AJAX, или сайтов с капчей (требуется взаимодействие).

Ошибки: слишком короткий таймаут ожидания, утечка памяти при большом количестве страниц. Решение – настройка timeout, закрытие браузера после каждого сеанса, использование context manager.

Как ускорить сбор данных без потери гибкости?

Асинхронный подход с aiohttp и BeautifulSoup позволяет выполнять множество запросов одновременно. Подходит для сбора информации с десятков источников.

import asyncio
import aiohttp
from bs4 import BeautifulSoup

async def fetch(session, url):
    async with session.get(url) as response:
        return await response.text()

async def main():
    urls = ["http://quotes.toscrape.com/page/{}/".format(i) for i in range(1,11)]
    async with aiohttp.ClientSession() as session:
        tasks = [fetch(session, url) for url in urls]
        pages = await asyncio.gather(*tasks)
        for page in pages:
            soup = BeautifulSoup(page, "html.parser")
            # обработка

asyncio.run(main())

Цель: сбор информации с большого количества страниц за короткое время без использования тяжёлого фреймворка.

Проблемы: слишком высокая частота запросов может привести к блокировке. Решение – добавление задержки asyncio.sleep(0.5) между запросами или использование asyncio.Semaphore для ограничения количества одновременных соединений.

Как работать с защищёнными API сайтов?

Некоторые проекты предоставляют JSON API, что упрощает сбор. Достаточно отправить запрос с правильными заголовками и параметрами.

import requests

api_url = "https://api.github.com/repos/python/cpython/contributors"
headers = {"Accept": "application/vnd.github.v3+json"}
response = requests.get(api_url, headers=headers)
data = response.json()
for contributor in data[:5]:
    print(contributor["login"], contributor["contributions"])

Цель: получение структурированных данных от сервисов, предоставляющих открытое API.

Типичные ошибки: превышение лимита запросов (необходимо обрабатывать код 429), отсутствие токена аутентификации. Решение – добавить паузу и использовать заголовок Authorization.

Дополнительные примеры и продвинутые техники

В этом разделе собраны нестандартные сценарии, которые часто возникают на практике.

  1. Ротация прокси и User-Agent

    Для обхода блокировок нужно подменять IP и заголовки. Пример с библиотекой fake-useragent и списком прокси.

    Пример
    import requests
    from fake_useragent import UserAgent
    from itertools import cycle
    
    proxies = [
        "http://proxy1:8080",
        "http://proxy2:8080",
    ]
    proxy_pool = cycle(proxies)
    ua = UserAgent()
    
    for url in urls:
        proxy = next(proxy_pool)
        headers = {"User-Agent": ua.random}
        try:
            response = requests.get(url, proxies={"http": proxy, "https": proxy}, headers=headers, timeout=5)
            # обработка
        except requests.exceptions.ProxyError:
            # переключение на другой прокси
            continue

    Результат: успешное получение данных, даже если один из прокси перестал работать.

  2. Парсинг с помощью XPath и lxml

    XPath часто мощнее CSS-селекторов. lxml работает быстрее BeautifulSoup.

    Пример
    import requests
    from lxml import html
    
    url = "http://quotes.toscrape.com/"
    response = requests.get(url)
    tree = html.fromstring(response.content)
    quotes = tree.xpath("//div[@class='quote']/span[@class='text']/text()")
    print(quotes)

    Результат: список текстов цитат, извлечённых напрямую.

  3. Асинхронная обработка с сохранением в базу данных

    Пример связки aiohttp + asyncpg для записи в PostgreSQL.

    Пример
    import asyncio
    import aiohttp
    import asyncpg
    
    async def save_to_db(pool, data):
        async with pool.acquire() as conn:
            await conn.execute("INSERT INTO quotes(text, author) VALUES($1, $2)", data["text"], data["author"])
    
    async def fetch_and_save(url):
        async with aiohttp.ClientSession() as session:
            async with session.get(url) as resp:
                html = await resp.text()
                # парсинг...
                # for each quote:
                #     await save_to_db(pool, quote_dict)
    
    async def main():
        pool = await asyncpg.create_pool("postgresql://user:pass@localhost/db")
        urls = [...]
        await asyncio.gather(*(fetch_and_save(url) for url in urls))
        await pool.close()

    Результат: данные автоматически сохраняются в таблицу, избегая дублирования через PRIMARY KEY.

  4. Сбор данных через Playwright с ожиданием динамических элементов

    На примере сайта с бесконечной прокруткой.

    Пример
    from playwright.sync_api import sync_playwright
    
    with sync_playwright() as p:
        browser = p.chromium.launch()
        page = browser.new_page()
        page.goto("https://example.com/infinite-scroll")
        for _ in range(5):
            page.wait_for_timeout(2000)
            page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
        items = page.query_selector_all(".item")
        for item in items:
            print(item.inner_text())
        browser.close()

    Результат: все элементы, подгруженные через AJAX, будут доступны.

  5. Обработка капчи через сервис (антигейт)

    Пример интеграции с 2captcha для разгадывания reCAPTCHA.

    Пример
    import requests
    from twocaptcha import TwoCaptcha
    
    solver = TwoCaptcha('YOUR_API_KEY')
    # после получения страницы с капчей
    result = solver.recaptcha(
        sitekey='SITE_KEY',
        url='PAGE_URL'
    )
    # подставляем result['code'] в форму и отправляем

    Результат: обход защиты, если другие способы невозможны.

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

Веб-скрапинг на Python - comments

En
веб скрапинг python (python)