Как парсить сайт: Полное руководство для начинающих и продвинутых пользователей

Парсинг сайтов (web scraping) — это процесс извлечения данных с веб-страниц с помощью программного кода. Это мощный инструмент для автоматизации сбора информации, анализа данных, мониторинга цен и многого другого. В этом уроке мы подробно разберём, как парсить сайт, от основ до продвинутых техник. Будем используем Python как основной язык, так как он наиболее популярен для этих задач благодаря библиотекам вроде requests, BeautifulSoup и Selenium.

Для примера мы выберем сайт books.toscrape.com — это тестовый сайт, специально созданный для практики веб-скрапинга. Он содержит каталог книг с категориями, страницами, описаниями, ценами и рейтингами. Сайт простой, но позволяет продемонстрировать все аспекты парсинга: статический контент, пагинацию, динамику и обработку ошибок. Важно: всегда проверяйте robots.txt сайта и соблюдайте законы (например, GDPR в ЕС). Не используйте парсинг для вредоносных целей.

1. Введение в веб-парсинг

Веб-парсинг — это автоматизированный способ получения данных из HTML-структуры сайтов. Почему это полезно? Представьте, что вам нужно собрать цены на книги из онлайн-магазина: вручную это займёт часы, а с кодом — минуты. Парсинг применяется в бизнес-аналитике (мониторинг конкурентов), науке (сбор данных для исследований), маркетинге (анализ отзывов) и даже в повседневной жизни (автоматизация уведомлений о скидках).

Основные понятия

  • HTML-структура: Сайты построены на HTML-тегах (<div>, <p>, <a>). Парсер находит нужные теги и извлекает текст или атрибуты.
  • Статический vs Динамический контент: Статический — загружается сразу (парсим с requests + BeautifulSoup). Динамический — генерируется JavaScript (нужен Selenium для эмуляции браузера).
  • API vs Парсинг: Если сайт предоставляет API (например, JSON-эндпоинты), лучше использовать его — это быстрее и легальнее. Но многие сайты API не имеют, поэтому парсинг необходим.

Этика и легальность

Перед парсингом проверьте:

Файл robots.txt

User-agent: *
Disallow: /
User-agent: *
Звёздочка означает «все поисковые роботы» (Googlebot, YandexBot, BingBot и т.д.).
Disallow: /
Запретить сканировать все страницы сайта (слэш / означает корень, т.е. весь сайт).

  • robots.txt: Файл на сайте (например, books.toscrape.com/robots.txt), не существует, так что можно парсить. Что не запрещено, то разрешено.
  • Terms of Service: Многие сайты запрещают скрейпинг (Amazon, Google). Нарушение может привести к бану IP.
  • Rate Limiting: Не перегружайте сервер — добавляйте задержки (time.sleep(1-5 сек)).
  • Легальность: В России и США парсинг публичных данных разрешён, но не личных (без согласия). Избегайте сайтов с капчей или аутентификацией.

Необходимые инструменты

Для работы нам понадобятся:

  • Python 3.x.
  • Библиотеки: requests (для HTTP-запросов), BeautifulSoup (для парсинга HTML), Selenium (для динамики), pandas (для хранения данных).

Установка библиотек:

pip install requests beautifulsoup4 selenium pandas

2. Базовый парсинг статического сайта с requests и BeautifulSoup

Начнём с простого: извлечём список книг с главной страницы books.toscrape.com.

Шаг 1: Получение HTML

Используем requests для загрузки страницы.

import requests

url = 'https://books.toscrape.com/'
response = requests.get(url)
if response.status_code == 200:
    html = response.text
    print("Страница загружена успешно!")
else:
    print(f"Ошибка: {response.status_code}")

Объяснение: requests.get() отправляет GET-запрос. response.text — HTML-код. Проверяем status_code (200 — OK).

Результат:

Шаг 2: Парсинг с BeautifulSoup

BeautifulSoup разбирает HTML в дерево тегов.

import requests
from bs4 import BeautifulSoup

url = 'https://books.toscrape.com/'
response = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'})
if response.status_code == 200:
    soup = BeautifulSoup(response.text, 'html.parser')
    title = soup.find('title').text
    print(f"Заголовок страницы: {title}")
else:
    print(f"Ошибка: {response.status_code}")

find() находит первый тег. Для нескольких — find_all().

Результат:

Шаг 3: Извлечение данных

На сайте книги в <article class=»product_pod»>. Внутри: <h3><a> — название, <p class=»price_color»> — цена, <p class=»star-rating»> — рейтинг.

import requests
from bs4 import BeautifulSoup

url = 'https://books.toscrape.com/'
response = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'})
if response.status_code == 200:
    response.encoding = 'utf-8'  # Устанавливаем кодировку UTF-8
    soup = BeautifulSoup(response.text, 'html.parser')
    books = soup.find_all('article', class_='product_pod')
    for book in books:
        name = book.find('h3').find('a')['title']
        price = book.find('p', class_='price_color').text
        rating = book.find('p', class_='star-rating')['class'][1]
        print(f"Книга: {name}, Цена: {price}, Рейтинг: {rating}")
else:
    print(f"Ошибка: {response.status_code}")

Это извлекает 20 книг с главной страницы. ['class'][1] — потому что класс «star-rating Three», второй элемент — рейтинг.

Результат:

Обработка пагинации

Сайт имеет 50 страниц. Находим ссылку «next» и циклим.

import requests
from bs4 import BeautifulSoup
import time
from urllib.parse import urljoin

all_books = []
current_url = 'https://books.toscrape.com/'
while current_url:
    response = requests.get(current_url, headers={'User-Agent': 'Mozilla/5.0'})
    if response.status_code == 200:
        response.encoding = 'utf-8'  # Устанавливаем кодировку UTF-8
        soup = BeautifulSoup(response.text, 'html.parser')
        books = soup.find_all('article', class_='product_pod')
        for book in books:
            name = book.find('h3').find('a')['title']
            price = book.find('p', class_='price_color').text
            rating = book.find('p', class_='star-rating')['class'][1]
            all_books.append({'name': name, 'price': price, 'rating': rating})
        next_button = soup.find('li', class_='next')
        if next_button:
            next_page = next_button.find('a')['href']
            current_url = urljoin(current_url, next_page)  # Корректное формирование URL
        else:
            current_url = None
        time.sleep(1)  # Задержка, чтобы не нагружать сервер
    else:
        print(f"Ошибка: {response.status_code}")
        break

print(f"Всего книг: {len(all_books)}")

Это соберёт все 1000 книг. time.sleep() — этика. Время работы от 1 до 3 минут.

Результат:

Сохранение данных

Используем pandas для CSV.

import requests
from bs4 import BeautifulSoup
import time
from urllib.parse import urljoin
import pandas as pd

all_books = []
current_url = 'https://books.toscrape.com/'
while current_url:
    response = requests.get(current_url, headers={'User-Agent': 'Mozilla/5.0'})
    if response.status_code == 200:
        response.encoding = 'utf-8'  # Устанавливаем кодировку UTF-8
        soup = BeautifulSoup(response.text, 'html.parser')
        books = soup.find_all('article', class_='product_pod')
        for book in books:
            name = book.find('h3').find('a')['title']
            price = book.find('p', class_='price_color').text
            rating = book.find('p', class_='star-rating')['class'][1]
            all_books.append({'name': name, 'price': price, 'rating': rating})
        next_button = soup.find('li', class_='next')
        if next_button:
            next_page = next_button.find('a')['href']
            current_url = urljoin(current_url, next_page)  # Корректное формирование URL
        else:
            current_url = None
        time.sleep(1)  # Задержка, чтобы не нагружать сервер
    else:
        print(f"Ошибка: {response.status_code}")
        break

df = pd.DataFrame(all_books)
df.to_csv('books.csv', index=False, encoding='utf-8')  # Сохранение с UTF-8
print("Данные сохранены в books.csv")
print(f"Всего книг: {len(all_books)}")

Теперь данные в файле. Можно анализировать: средняя цена, топ по рейтингу.

Результат:

Подробно о BeautifulSoup: методы find(), find_all(), select() для CSS-селекторов. Например, soup.select('article.product_pod h3 a') — все ссылки на книги.

Обработка ошибок: Используйте try-except для случаев, когда тег не найден.

requests.headers — для User-Agent, чтобы симулировать браузер.

requests.session() — для сохранения куки при пагинации.

3. Парсинг динамического контента с Selenium

Некоторые сайты загружают данные через JS (например, infinite scroll). Books.toscrape.com статический, но представим динамику. Selenium эмулирует браузер.

Установка и базовый запуск

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.get('https://books.toscrape.com/')
print(driver.title)
driver.quit()

webdriver_manager автоматически скачивает драйвер.

Результат:

Извлечение данных

Selenium использует find_element, find_elements.

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
import time

driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.get('https://books.toscrape.com/')
time.sleep(2)  # Задержка для полной загрузки страницы
books = driver.find_elements(By.CLASS_NAME, 'product_pod')
for book in books:
    name = book.find_element(By.TAG_NAME, 'h3').find_element(By.TAG_NAME, 'a').get_attribute('title')
    price = book.find_element(By.CLASS_NAME, 'price_color').text
    rating = book.find_element(By.CSS_SELECTOR, 'p.star-rating').get_attribute('class').split()[1]
    print(f"Книга: {name}, Цена: {price}, Рейтинг: {rating}")
driver.quit()

By — селекторы: TAG_NAME, CLASS_NAME, CSS_SELECTOR, XPATH. XPATH для сложных: //article[@class='product_pod']//h3/a/@title.

Результат:

Обработка пагинации с Selenium

Кликаем «next».

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.common.exceptions import NoSuchElementException
import time

all_books = []
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.get('https://books.toscrape.com/')
time.sleep(2)  # Ждём загрузку страницы

while True:
    books = driver.find_elements(By.CLASS_NAME, 'product_pod')
    for book in books:
        try:
            name = book.find_element(By.TAG_NAME, 'h3').find_element(By.TAG_NAME, 'a').get_attribute('title')
            price = book.find_element(By.CLASS_NAME, 'price_color').text.encode('utf-8').decode('utf-8')
            rating = book.find_element(By.CSS_SELECTOR, 'p.star-rating').get_attribute('class').split()[1]
            all_books.append({'name': name, 'price': price, 'rating': rating})
        except NoSuchElementException:
            print("Ошибка: Не удалось извлечь данные книги")
            continue
    try:
        next_button = driver.find_element(By.CLASS_NAME, 'next').find_element(By.TAG_NAME, 'a')
        next_button.click()
        time.sleep(2)  # Ждём загрузку следующей страницы
    except NoSuchElementException:
        break

driver.quit()
print(f"Всего книг: {len(all_books)}")

Результат:

time.sleep() — для ожидания JS, но лучше использовать WebDriverWait.

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CLASS_NAME, 'product_pod')))

Опции Selenium

Для headless (без окна):

options = webdriver.ChromeOptions()
options.add_argument('--headless')
driver = webdriver.Chrome(options=options)

Для User-Agent:

options.add_argument('--user-agent=Your UA')

Для прокси:

options.add_argument('--proxy-server=http://ip:port')

Комбинирование с BeautifulSoup

Selenium получает HTML после JS, затем парсим BS. Это полезно для динамики.

soup = BeautifulSoup(driver.page_source, 'html.parser')

Парсинг форм и взаимодействие

Selenium может кликать, вводить текст. Для поиска на сайте:

driver.find_element(By.NAME, 'q').send_keys('python')
driver.find_element(By.NAME, 'q').submit()

Обход анти-бот

  • Задержки.
  • Рандомные User-Agent.
  • Прокси.
  • Headless с опциями, чтобы скрыть автоматизацию:
options.add_argument('--disable-blink-features=AutomationControlled')

4. Продвинутые техники

Многопоточность

Для ускорения используйте threading или multiprocessing. Но будьте осторожны с лимитами.

import requests
from bs4 import BeautifulSoup
import threading
from urllib.parse import urljoin

all_books = []
base_url = 'https://books.toscrape.com/'
pages = [f'catalogue/page-{i}.html' for i in range(1, 51)]  # 50 страниц

def parse_page(page):
    url = urljoin(base_url, page)
    response = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'})
    if response.status_code == 200:
        response.encoding = 'utf-8'
        soup = BeautifulSoup(response.text, 'html.parser')
        books = soup.find_all('article', class_='product_pod')
        for book in books:
            name = book.find('h3').find('a')['title']
            price = book.find('p', class_='price_color').text
            rating = book.find('p', class_='star-rating')['class'][1]
            all_books.append({'name': name, 'price': price, 'rating': rating})

threads = []
for page in pages:
    t = threading.Thread(target=parse_page, args=(page,))
    t.start()
    threads.append(t)
for t in threads:
    t.join()

print(f"Всего книг: {len(all_books)}")

Обработка JSON/API

Если сайт имеет API, парсите JSON.

response = requests.get('api.url')
data = response.json()

Парсинг с lxml

Альтернатива BS, быстрее.

from lxml import html
tree = html.fromstring(response.text)
names = tree.xpath('//h3/a/@title')

Работа с куки и сессиями

requests.Session() для куки.

session = requests.Session()
session.cookies.set('key', 'value')

Парсинг таблиц

Для таблиц:

df = pd.read_html(url)

Обработка ошибок

try-except для Timeout, NoSuchElement.

from requests.exceptions import RequestException

Хранение в БД

Используйте SQLite или MongoDB.

import sqlite3
conn = sqlite3.connect('books.db')
df.to_sql('books', conn)

Scrapy — фреймворк для больших проектов

Scrapy — для сложных скрейперов с пауками.

pip install scrapy
scrapy startproject myproject

В spider.py:

class BookSpider(scrapy.Spider):
    name = 'book'
    start_urls = ['https://books.toscrape.com/']

    def parse(self, response):
        # извлечение
        yield {'name': ...}
        next_page = response.css('li.next a::attr(href)').get()
        if next_page:
            yield response.follow(next_page, self.parse)

Запуск:

scrapy crawl book -o books.json

Это автоматизирует пагинацию, обработку.

5. Примеры на других сайтах

Парсинг Wikipedia: Извлечение инфобокса.

soup.find('table', class_='infobox')

Парсинг новостей: RSS или прямой парсинг.

Парсинг e-commerce: Цены, но осторожно с TOS.

6. Проблемы и решения

  • Капча: Используйте сервисы вроде 2captcha (платно).
  • Блокировка IP: Прокси-пулы (Bright Data).
  • Изменение структуры: Регулярные тесты кода.
  • JS-heavy: Headless browsers like Puppeteer (JS версия Selenium).

7. Заключение

Парсинг — мощный инструмент, но используйте его ответственно. Начните с простых сайтов, практикуйтесь на books.toscrape.com.
Смотрите примеры и изучайте уроки на https://parsertools.ru Удачи!

Похожие темы: