Python для извлечения данных из интернета
Обзор способов получения данных с веб-сайтов
Как получить статическую веб-страницу и извлечь из неё данные с помощью Python?
Базовое решение: библиотеки requests и BeautifulSoup
Этот подход подходит для сайтов, где весь контент отдаётся в исходном HTML без динамической подгрузки через JavaScript. Цель - быстро и просто спарсить страницу.
import requests
from bs4 import BeautifulSoup
url = 'https://example.com'
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
title = soup.find('h1').text
print(title)Python данные с сайта (получение данных с веб-сайта на python)
Example Domain
Сначала отправляется GET-запрос с помощью requests.get(). Затем полученный HTML передаётся в BeautifulSoup для парсинга. Можно искать элементы по тегам, классам, id.
Типичные проблемы:
- Ошибка 403 (доступ запрещён) - добавляйте заголовок
User-Agent. - Кодировка текста - используйте
response.encoding = 'utf-8'или определите автоматически. - Сайт блокирует частые запросы - добавьте задержки
time.sleep().
Как извлечь данные с сайта, который динамически загружает контент с помощью JavaScript?
Вариант: Selenium WebDriver
Когда данные появляются после выполнения скриптов, простой HTTP-запрос не даст нужного HTML. Selenium эмулирует работу браузера. Цель - получить полностью отрисованную страницу.
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.headless = True
driver = webdriver.Chrome(options=options)
driver.get('https://example.com')
source = driver.page_source
driver.quit()
soup = BeautifulSoup(source, 'html.parser')
print(soup.find('h1').text)
Example Domain
Запускается драйвер в фоновом режиме (headless). После загрузки страницы извлекается её HTML через page_source. Далее парсится с BeautifulSoup.
Возможные сложности:
- Необходимость установки драйвера (chromedriver) и соответствие версии браузера.
- Медленная работа по сравнению с прямыми запросами.
- Потребление памяти. Для снижения нагрузки используйте headless-режим.
Как ускорить сбор данных с нескольких страниц одновременно?
Вариант: асинхронный парсинг с aiohttp и asyncio
Если нужно обработать десятки или сотни URL, последовательные запросы будут медленными. Асинхронный подход позволяет отправлять запросы параллельно. Цель - сократить время сбора.
import aiohttp
import asyncio
from bs4 import BeautifulSoup
async def fetch(session, url):
async with session.get(url) as response:
return await response.text()
async def main():
urls = ['https://example.com/page1', 'https://example.com/page2']
async with aiohttp.ClientSession() as session:
tasks = [fetch(session, url) for url in urls]
pages = await asyncio.gather(*tasks)
for html in pages:
soup = BeautifulSoup(html, 'html.parser')
print(soup.find('h1').text if soup.find('h1') else 'No h1')
asyncio.run(main())
Создаётся сессия aiohttp.ClientSession, для каждого URL формируется задача на скачивание. asyncio.gather запускает их одновременно. Результаты обрабатываются после завершения всех запросов.
Проблемы и решения:
- Сайт может не выдержать пиковой нагрузки - ограничьте количество одновременных запросов через семафор.
- Ошибки соединения - обрабатывайте исключения в каждой задаче.
- Не все библиотеки поддерживают асинхронность (BeautifulSoup работает синхронно, но это не критично).
Как организовать масштабный сбор данных с паутиной ссылок и обработкой ошибок?
Вариант: фреймворк Scrapy
Для серьёзных проектов с тысячами страниц, следованием по ссылкам, автоматической обработкой дубликатов и паузами лучше использовать специализированный инструмент. Цель - промышленный парсинг.
# spider.py
import scrapy
class MySpider(scrapy.Spider):
name = 'example'
start_urls = ['https://example.com']
def parse(self, response):
# Извлечение данных
yield {
'title': response.css('h1::text').get(),
'url': response.url
}
# Следование по ссылкам
for link in response.css('a::attr(href)'):
yield response.follow(link, self.parse)
Запуск: scrapy runspider spider.py -o output.json. Фреймворк сам управляет запросами, обрабатывает ошибки, поддерживает паузы и прокси.
Сложности при использовании Scrapy:
- Кривая обучения - нужно понять архитектуру пауков и конвейеров.
- Избыточен для простых одноразовых задач.
- Требует настройки обработчиков для JavaScript (Splash или Selenium).
Расширенные примеры сбора данных
Пример 1: Парсинг с защитой от блокировок (User-Agent, прокси, сессия)
import requests
from bs4 import BeautifulSoup
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,*/*'
}
proxies = {
'http': 'http://user:pass@proxy:8080',
'https': 'http://user:pass@proxy:8080'
}
session = requests.Session()
session.headers.update(headers)
session.proxies.update(proxies)
response = session.get('https://httpbin.org/headers')
soup = BeautifulSoup(response.text, 'html.parser')
print(soup.prettify())
{
"headers": {
"Accept": "*/*",
"User-Agent": "Mozilla/5.0 ..."
}
}
Пример 2: Извлечение данных из API сайта вместо HTML
Многие сайты предоставляют JSON-эндпоинты, которые проще парсить, чем HTML.
import requests
import json
url_api = 'https://api.github.com/repos/psf/requests'
response = requests.get(url_api)
data = response.json()
print(f"Stars: {data['stargazers_count']}")
print(f"Forks: {data['forks_count']}")
Stars: 51000 Forks: 9500
Пример 3: Асинхронный парсинг с ограничением конкурентности и обработкой ошибок
import aiohttp
import asyncio
from bs4 import BeautifulSoup
async def fetch_with_limit(sem, session, url):
async with sem:
try:
async with session.get(url, timeout=10) as response:
return await response.text()
except Exception as e:
print(f"Ошибка {url}: {e}")
return None
async def main():
urls = [f'https://httpbin.org/delay/{i}' for i in range(1, 6)]
sem = asyncio.Semaphore(3) # не более 3 одновременных запросов
async with aiohttp.ClientSession() as session:
tasks = [fetch_with_limit(sem, session, url) for url in urls]
results = await asyncio.gather(*tasks)
for url, html in zip(urls, results):
if html:
soup = BeautifulSoup(html, 'html.parser')
print(f"{url}: длина HTML = {len(html)}")
asyncio.run(main())
https://httpbin.org/delay/1: длина HTML = 1200 https://httpbin.org/delay/2: длина HTML = 1200 https://httpbin.org/delay/3: длина HTML = 1200 https://httpbin.org/delay/4: длина HTML = 1200 https://httpbin.org/delay/5: длина HTML = 1200
Пример 4: Обработка форм и сессий с Selenium (логин, cookie)
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get('https://example.com/login')
driver.find_element(By.NAME, 'username').send_keys('user')
driver.find_element(By.NAME, 'password').send_keys('pass')
driver.find_element(By.XPATH, '//button[@type="submit"]').click()
# После логина cookie сохранятся в сессии driver
driver.get('https://example.com/protected')
print(driver.find_element(By.TAG_NAME, 'h1').text)
driver.quit()
Welcome, user!
Пример 5: Использование Scrapy с паутиной и конвейером для обработки данных
import scrapy
class BlogSpider(scrapy.Spider):
name = 'blog'
start_urls = ['https://scrapy.org/blog/']
def parse(self, response):
for article in response.css('div.article'):
yield {
'title': article.css('h2::text').get(),
'link': article.css('a::attr(href)').get(),
}
next_page = response.css('a.next::attr(href)').get()
if next_page:
yield scrapy.Request(response.urljoin(next_page))
(консольный вывод Scrapy с собранными элементами)
После запуска scrapy runspider blog_spider.py -o articles.json все данные сохранятся в JSON.