Извлечение информации с сайтов: практические решения на 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.
Дополнительные примеры и продвинутые техники
В этом разделе собраны нестандартные сценарии, которые часто возникают на практике.
-
Ротация прокси и 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Результат: успешное получение данных, даже если один из прокси перестал работать.
-
Парсинг с помощью 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)Результат: список текстов цитат, извлечённых напрямую.
-
Асинхронная обработка с сохранением в базу данных
Пример связки 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.
-
Сбор данных через 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, будут доступны.
-
Обработка капчи через сервис (антигейт)
Пример интеграции с 2captcha для разгадывания reCAPTCHA.
Примерimport requests from twocaptcha import TwoCaptcha solver = TwoCaptcha('YOUR_API_KEY') # после получения страницы с капчей result = solver.recaptcha( sitekey='SITE_KEY', url='PAGE_URL' ) # подставляем result['code'] в форму и отправляемРезультат: обход защиты, если другие способы невозможны.
Эти примеры демонстрируют, как решать реальные задачи, выходящие за рамки базовых учебников. Важно тестировать каждый блок кода на своей целевой странице и адаптировать под её структуру.