В современном цифровом мире данные являются одним из самых ценных ресурсов. Они скрыты повсюду в интернете — в каталогах интернет-магазинов, новостных лентах, на форумах, в правительственных порталах и научных публикациях. Веб-парсинг, или парсинг, — это технология, которая позволяет автоматически извлекать эти данные с веб-сайтов и преобразовывать их в структурированный, удобный для анализа формат.1
Представьте, что у вас есть помощник, способный с невероятной скоростью просматривать тысячи веб-страниц, находить нужную информацию — будь то цены на товары, контактные данные компаний или результаты спортивных матчей — и аккуратно записывать все это в таблицу. Именно эту роль и выполняет программа-парсер. Это не взлом и не кибератака, а работа с информацией, которая и так находится в открытом доступе.3 Процесс сбора данных с веб-ресурсов автоматизируется с помощью специальных программ, которые часто называют ботами или парсерами.3
Чтобы полностью понять, как работает веб-парсинг, важно различать несколько ключевых терминов, которые часто используются как взаимозаменяемые, но имеют разные значения.
На практике современный инструмент для веб-парсинга, как правило, выполняет все три задачи: он находит страницы (краулинг), анализирует их структуру (парсинг) и извлекает нужные данные (парсинг).5
Важно понимать, что парсинг — это не какая-то нишевая или сомнительная деятельность. Это фундаментальная технология, на которой построена значительная часть современного интернета. Крупнейшие технологические компании, включая Google и Facebook, являются, по сути, самыми масштабными веб-скрейперами в мире.6 Они каталогизируют информацию, чтобы сделать ее доступной через поиск или для построения социальных связей. Это понимание смещает фокус с вопроса «Можно ли это делать?» на вопрос «Как делать это ответственно и профессионально?».
Сферы применения веб-парсинга огромны и разнообразны:
Освоив парсинг, вы получаете мощный инструмент, который позволяет превратить хаотичный океан веб-данных в структурированные и ценные наборы информации, готовые для анализа и использования.
Прежде чем написать хотя бы одну строчку кода, абсолютно необходимо усвоить правила игры. Парсинг находится в так называемой «серой зоне»: сам по себе он не является незаконным, но его правомерность сильно зависит от того, что и как вы собираете.10 Несоблюдение правил может привести к серьезным последствиям, от блокировки вашего IP-адреса до судебных исков и крупных штрафов.13 Эта глава — самая важная для вашей безопасности. Практика ответственного парсинга строится на трех основных принципах: уважение к правилам сайта, соблюдение законов об авторском праве и защита персональных данных.
Каждый веб-сайт — это частная собственность в цифровом мире. Как и в реальном мире, у владельца есть право устанавливать правила поведения на своей территории.
Часто возникает ситуация, когда robots.txt разрешает сканирование (например, для поисковых систем), а Условия использования — запрещают. Этот конфликт создается намеренно: технический отдел хочет, чтобы сайт индексировался в Google, а юридический отдел — чтобы иметь основания для судебного преследования в случае нежелательного парсинга.22 В таких случаях юридический приоритет, как правило, имеют Условия использования. Это означает, что нельзя слепо доверять robots.txt, если ToS говорит обратное.
Данные на сайтах часто защищены законами об авторском праве.23
Это самый строгий и опасный аспект современного парсинга. Персональными данными (Personally Identifiable Information, PII) считается любая информация, которая может прямо или косвенно идентифицировать человека: имена, адреса электронной почты, номера телефонов, IP-адреса, геолокационные данные.3
Помимо юридических требований, существует и этическая сторона вопроса. Ответственный скрейпер всегда должен помнить, что он является «гостем» на чужом сервере.
Чтобы помочь начинающим ориентироваться в этих правилах, ниже приведена таблица с контрольным списком действий перед началом любого проекта по парсингу.
Контрольный пункт | Действие | Почему это важно |
Наличие API | Проверить, предлагает ли сайт публичный API? | API — это предпочтительный, законный и наиболее эффективный способ получения данных. |
Анализ robots.txt | Прочитать и понять файл robots.txt? | Демонстрирует уважение к пожеланиям владельца сайта и помогает избежать очевидно запрещенных разделов. |
Условия использования | Прочитать Условия использования на предмет пунктов, запрещающих парсинг? | ToS — это юридическое соглашение; его нарушение может иметь серьезные последствия. |
Тип данных | Собираются факты или контент, защищенный авторским правом? | Факты, как правило, можно собирать; защищенные материалы требуют разрешения или обоснования добросовестного использования. |
Персональные данные | Содержат ли данные какую-либо персональную информацию (PII)? | Сбор PII строго регулируется GDPR/CCPA и сопряжен с чрезвычайно высоким риском. |
Частота запросов | Используется ли разумная задержка между запросами? | Предотвращает перегрузку сервера, что неэтично и может привести к бану IP-адреса. |
Идентификация | Использует ли парсер понятный заголовок User-Agent? | Обеспечивает прозрачность для администраторов сайта. |
Соблюдение этих правил не только защитит вас от юридических рисков, но и сделает вас ответственным членом цифрового сообщества.
Прежде чем мы сможем начать извлекать данные, необходимо подготовить рабочее окружение. Этот процесс включает в себя установку языков программирования и их экосистем. Мы рассмотрим установку для двух платформ: Python и.NET Core, на трех основных операционных системах: Windows, macOS и Linux. Цель этого раздела — обеспечить успешную установку даже для абсолютного новичка.
Сам процесс установки выявляет фундаментальное различие в философии двух экосистем. Установка Python может быть более фрагментированной, с потенциальными конфликтами версий и путаницей между командами (python и python3, pip и pip3). Это отражает гибкость и децентрализованность мира open-source. С другой стороны, установка.NET более унифицирована и контролируется Microsoft, предлагая более структурированный и «все в одном» опыт с единым SDK и командой dotnet. Это не означает, что одна система лучше другой, но понимание этих различий помогает осознанно подходить к процессу.
Python — чрезвычайно популярный язык для анализа данных и веб-парсинга благодаря своей простоте и огромному количеству библиотек.
Установка на Windows:
Установка на macOS:
macOS часто поставляется с предустановленной старой версией Python 2, которую не следует использовать для новых проектов.
Установка на Linux:
Большинство дистрибутивов Linux поставляются с предустановленным Python 3.
Установка pip:
pip — это система управления пакетами, используемая для установки и управления программными пакетами, написанными на Python. Начиная с Python 3.4, pip обычно устанавливается вместе с Python.36
.NET (ранее.NET Core) — это кроссплатформенная среда разработки от Microsoft, мощная и производительная, с отличной поддержкой для создания веб-приложений и сервисов.
Установка.NET SDK:
Основной инструмент, который вам понадобится, — это .NET SDK (Software Development Kit), который включает в себя все необходимое для разработки и запуска приложений.
Bash
sudo apt-get update
sudo apt-get install -y apt-transport-https
sudo apt-get update
sudo apt-get install -y dotnet-sdk-8.0 # Замените на актуальную версию
Теперь, когда базовые инструменты установлены, вы готовы к настройке среды разработки для написания кода.
IDE (Integrated Development Environment) или интегрированная среда разработки — это текстовый редактор, «заряженный» дополнительными инструментами, которые упрощают написание, запуск и отладку кода. Правильно настроенная IDE значительно повышает продуктивность. Мы сосредоточимся на Visual Studio Code как на универсальном и мощном решении для обеих платформ, но также упомянем и другие популярные альтернативы.
Visual Studio Code (VS Code) — это бесплатный, легковесный и чрезвычайно расширяемый редактор кода от Microsoft, который стал стандартом де-факто для многих разработчиков благодаря своей кроссплатформенности и огромной экосистеме расширений.
Установка VS Code:
Перейдите на официальный сайт (https://code.visualstudio.com/) и скачайте установщик для вашей ОС (Windows, macOS, Linux).42 Процесс установки интуитивно понятен.
Настройка для разработки на Python:
Настройка для разработки на.NET Core:
Хотя VS Code является отличным универсальным инструментом, существуют и более специализированные IDE, которые могут предложить еще больше удобств.
Для целей нашего руководства VS Code более чем достаточно. Он обеспечивает единую среду для изучения обоих языков и является отличным выбором для начинающих, который остается актуальным и для профессиональной разработки.
В основе любого взаимодействия в интернете лежит протокол передачи гипертекста — HTTP (Hypertext Transfer Protocol). Прежде чем мы сможем извлечь данные со страницы, нам нужно ее получить. Этот процесс получения страницы — не что иное, как отправка HTTP-запроса на сервер, где расположен сайт, и получение от него HTTP-ответа.
Представьте общение с веб-сервером как диалог. Вы (клиент) задаете вопрос (отправляете запрос), а сервер дает ответ. В контексте парсинга нас в первую очередь интересуют два типа «вопросов» или, как их называют, HTTP-методов:
Каждый раз, когда сервер отвечает, он присылает не только запрошенные данные (например, HTML-код), но и код состояния (status code). Это трехзначное число, которое сообщает, был ли запрос успешным. Для скрейпера важно знать несколько основных кодов 51:
В мире Python для работы с HTTP-запросами существует неофициальный стандарт — библиотека requests. Она славится своей простотой и элегантностью, полностью оправдывая один из принципов Python: «Простое лучше, чем сложное».50
Установка:
Bash
pip install requests
Отправка GET-запроса:
Это невероятно просто. Чтобы получить HTML-код страницы, достаточно одной строки:
import requests
url = 'http://quotes.toscrape.com/'
response = requests.get(url)
# Проверяем успешность запроса
if response.status_code == 200:
# Выводим содержимое страницы
print(response.text)
else:
print(f"Ошибка: {response.status_code}")
Объект response содержит всю информацию об ответе сервера. response.status_code хранит код состояния, а response.text — тело ответа (в данном случае, HTML-код) в виде строки.50 Если вы ожидаете данные в формате JSON (например, от API), можно использовать response.json(), который автоматически преобразует ответ в словарь Python.
Передача параметров и заголовков:
Часто требуется передать параметры в URL (например, для поиска ?q=python) или добавить заголовки (headers), чтобы имитировать запрос из реального браузера. requests делает это очень удобным:
import requests
# Параметры для URL
params = {'page': '2'}
# Заголовки, имитирующие браузер
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'
}
url = 'http://quotes.toscrape.com/'
response = requests.get(url, params=params, headers=headers)
# Итоговый URL будет: http://quotes.toscrape.com/?page=2
print(response.url)
print(response.text)
Библиотека requests автоматически управляет пулом соединений, что позволяет эффективно переиспользовать их для запросов к одному и тому же хосту, повышая производительность.53
В.NET для HTTP-запросов используется встроенный класс HttpClient.54 Однако здесь есть важный нюанс, связанный с лучшими практиками.
Как НЕ надо делать:
Наивный подход — создавать новый экземпляр HttpClient для каждого запроса:
C#
// Неправильный подход!
using (var client = new HttpClient())
{
//... сделать запрос
}
Хотя это кажется логичным из-за using, такой код может привести к серьезной проблеме, называемой «истощением сокетов» (socket exhaustion).54 Каждый новый экземпляр HttpClient резервирует системный сокет, который не освобождается немедленно после использования. При большом количестве запросов это может исчерпать все доступные сокеты и привести к сбоям в приложении.
Как НАДО делать: IHttpClientFactory:
Современный и правильный подход в.NET — использовать IHttpClientFactory. Это фабрика, которая управляет жизненным циклом экземпляров HttpClient централизованно, эффективно переиспользуя их и предотвращая проблему с сокетами.54 Она интегрируется в приложение через механизм внедрения зависимостей (Dependency Injection).
Настройка в консольном приложении.NET:
Сначала установим необходимый пакет:
Bash
dotnet add package Microsoft.Extensions.Http
dotnet add package Microsoft.Extensions.Hosting
Теперь настроим IHttpClientFactory и выполним запрос:
C#using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
// Создаем хост для использования Dependency Injection
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices((context, services) =>
{
// Регистрируем IHttpClientFactory
services.AddHttpClient();
})
.Build();
// Получаем фабрику из сервис-провайдера
var httpClientFactory = host.Services.GetRequiredService<IHttpClientFactory>();
// Создаем (или получаем из пула) клиент
var client = httpClientFactory.CreateClient();
// Устанавливаем заголовки
client.DefaultRequestHeaders.Add("User-Agent", "My.NET Scraper");
string url = "http://quotes.toscrape.com/";
HttpResponseMessage response = await client.GetAsync(url);
// Проверяем успешность запроса
if (response.IsSuccessStatusCode)
{
// Читаем содержимое как строку
string htmlContent = await response.Content.ReadAsStringAsync();
Console.WriteLine(htmlContent);
}
else
{
Console.WriteLine($"Ошибка: {response.StatusCode}");
}
Этот подход не только решает проблему с сокетами, но и позволяет централизованно настраивать клиенты (например, задавать базовый адрес или заголовки по умолчанию), что делает код чище и проще в поддержке.55 Освоив отправку HTTP-запросов, вы научились получать «сырой» HTML-код страниц. Следующий шаг — научиться «читать» этот код и извлекать из него ценную информацию.
После того как мы получили HTML-код страницы с помощью HTTP-запроса, перед нами стоит задача извлечь из этого, на первый взгляд, хаотичного набора тегов нужную информацию. Этот процесс называется парсингом.
HTML-документ имеет четкую иерархическую структуру, которую удобно представлять в виде дерева — Document Object Model (DOM). Представьте HTML-страницу как генеалогическое древо 60:
Такое представление позволяет нам навигировать по документу, перемещаясь от родительских элементов к дочерним и между соседними элементами, чтобы найти то, что нам нужно.62
Чтобы указать парсеру, какой именно элемент на странице нас интересует, мы используем селекторы. Это специальные строки, которые описывают путь к элементу в DOM-дереве. Существует два основных типа селекторов:
Выбор между CSS-селекторами и XPath часто является делом предпочтения, но для большинства задач парсинга CSS-селекторов более чем достаточно, и они, как правило, проще для начинающих.
В Python есть несколько отличных библиотек для парсинга HTML.
BeautifulSoup: Это самая популярная библиотека для начинающих. Она невероятно проста в использовании и очень «терпима» к некорректному или «сломанному» HTML, который часто встречается на реальных сайтах.63
from bs4 import BeautifulSoup
import requests
url = 'http://quotes.toscrape.com/'
response = requests.get(url)
# Создаем объект Soup, указывая HTML и парсер
soup = BeautifulSoup(response.text, 'lxml')
# Находим первый заголовок <h1>
title = soup.find('h1').text
print(f"Заголовок страницы: {title}")
# Используем CSS-селектор для поиска всех цитат
quotes = soup.select('div.quote')
for quote in quotes:
# Внутри каждого блока цитаты ищем текст и автора
text = quote.select_one('span.text').text
author = quote.select_one('small.author').text
print(f"Цитата: {text}\nАвтор: {author}\n")
soup.find() находит первый тег, soup.find_all() — все теги. Еще более мощные методы — soup.select() и soup.select_one(), которые работают с CSS-селекторами.63 Текст извлекается с помощью .text, а атрибуты — как в словаре, например, link['href'].68
lxml: Это высокопроизводительная библиотека, основанная на C, что делает ее одной из самых быстрых для парсинга XML и HTML.69 Она менее «прощающая», чем BeautifulSoup, но идеально подходит для задач, где важна скорость.
from lxml import html
import requests
url = 'http://quotes.toscrape.com/'
response = requests.get(url)
# Создаем дерево из HTML-кода
tree = html.fromstring(response.content)
# Используем XPath для поиска всех текстов цитат
# //div[@class='quote']//span[@class='text']/text()
# означает: найти все теги <span> с классом 'text' внутри <div> с классом 'quote'
# и взять их текстовое содержимое.
texts = tree.xpath("//div[@class='quote']//span[@class='text']/text()")
authors = tree.xpath("//div[@class='quote']//small[@class='author']/text()")
for text, author in zip(texts, authors):
print(f"Цитата: {text}\nАвтор: {author}\n")
В экосистеме.NET также есть два основных игрока на поле парсинга HTML.
HtmlAgilityPack (HAP): Это ветеран в мире.NET, очень надежная и, как и BeautifulSoup, толерантная к некорректному HTML.72
using HtmlAgilityPack;
using System;
using System.Net.Http;
using System.Threading.Tasks;
var url = "http://quotes.toscrape.com/";
var httpClient = new HttpClient();
var html = await httpClient.GetStringAsync(url);
var htmlDocument = new HtmlDocument();
htmlDocument.LoadHtml(html);
// Используем XPath для выбора всех блоков с цитатами
var quoteNodes = htmlDocument.DocumentNode.SelectNodes("//div[@class='quote']");
if (quoteNodes!= null)
{
foreach (var node in quoteNodes)
{
var text = node.SelectSingleNode(".//span[@class='text']").InnerText;
var author = node.SelectSingleNode(".//small[@class='author']").InnerText;
Console.WriteLine($"Цитата: {text}\nАвтор: {author}\n");
}
}
AngleSharp: Это современная, быстрая и строго соответствующая стандартам W3C библиотека. Ее цель — парсить HTML точно так же, как это делает современный браузер.77
using AngleSharp;
using System;
using System.Threading.Tasks;
// Настраиваем AngleSharp
var config = Configuration.Default.WithDefaultLoader();
var context = BrowsingContext.New(config);
var url = "http://quotes.toscrape.com/";
// AngleSharp может сам загрузить и распарсить страницу
var document = await context.OpenAsync(url);
// Используем CSS-селекторы
var quoteElements = document.QuerySelectorAll("div.quote");
foreach (var element in quoteElements)
{
var text = element.QuerySelector("span.text").TextContent;
var author = element.QuerySelector("small.author").TextContent;
Console.WriteLine($"Цитата: {text}\nАвтор: {author}\n");
}
Выбор библиотеки зависит от задачи и личных предпочтений. Для новичков в Python BeautifulSoup является отличной отправной точкой. В.NET AngleSharp предлагает более современный и удобный API, основанный на CSS-селекторах.
Библиотека | Язык | Основной селектор | Скорость | Ключевая особенность |
BeautifulSoup | Python | CSS-селекторы | Средняя | Чрезвычайно дружелюбна к новичкам, отлично справляется с «грязным» HTML. |
lxml | Python | XPath | Очень высокая | Высокая производительность, может использоваться как парсер для BeautifulSoup. |
HtmlAgilityPack | .NET | XPath | Высокая | Очень надежна и толерантна к некорректному HTML, давно существует. |
AngleSharp | .NET | CSS-селекторы | Очень высокая | Современная, строго соответствует стандартам (парсит как браузер). |
Выбор инструмента может зависеть от качества целевых сайтов. Для хорошо сформированных, современных сайтов AngleSharp или lxml будут превосходным выбором. Для старых, «сломанных» сайтов HtmlAgilityPack или BeautifulSoup с более снисходительным парсером могут оказаться более щадящими.
Теория важна, но ничто не закрепляет знания лучше, чем практика. В этой главе мы объединим все, что изучили ранее — HTTP-запросы и HTML-парсинг — чтобы создать наш первый полноценный скрейпер. Мы напишем две версии программы, одну на Python и одну на.NET, которые будут выполнять одну и ту же задачу: собирать все цитаты, имена авторов и теги с главной страницы сайта http://quotes.toscrape.com. Этот сайт специально создан для тренировки навыков парсинга.66
Этот проект продемонстрирует не только синтаксис, но и идиоматические различия в обработке данных между Python и C#. Python естественным образом тяготеет к гибким, динамическим структурам данных, таким как словари, тогда как C# поощряет использование строго типизированных структур, таких как классы или записи. Понимание этого различия поможет вам мыслить в «родном» стиле выбранного языка.
Прежде чем писать код, откройте сайт в браузере и используйте инструменты разработчика (обычно вызываются клавишей F12 или правым кликом -> «Inspect»/»Исследовать элемент»). Наведите курсор на одну из цитат. Вы увидите, что:
Эта структура будет основой для наших селекторов.
1. Настройка проекта:
Создайте папку для проекта, внутри нее — виртуальное окружение и установите необходимые библиотеки:
Bash
mkdir python_scraper
cd python_scraper
python -m venv env
source env/bin/activate # macOS/Linux
#.\env\Scripts\activate # Windows
pip install requests beautifulsoup4 lxml
Создайте файл scraper.py.
2. Написание кода:
import requests
from bs4 import BeautifulSoup
import json # Для красивого вывода
def scrape_quotes():
"""
Основная функция для парсинга цитат.
"""
url = 'http://quotes.toscrape.com/'
try:
response = requests.get(url)
# Генерируем исключение, если запрос был неуспешным
response.raise_for_status()
except requests.exceptions.RequestException as e:
print(f"Не удалось получить доступ к странице: {e}")
return
soup = BeautifulSoup(response.text, 'lxml')
# Список для хранения всех собранных данных
all_quotes_data =
# Находим все контейнеры с цитатами
quote_containers = soup.select('div.quote')
for quote in quote_containers:
# Извлекаем текст цитаты
text = quote.select_one('span.text').text
# Извлекаем автора
author = quote.select_one('small.author').text
# Извлекаем теги
tags_elements = quote.select('a.tag')
tags = [tag.text for tag in tags_elements]
# Сохраняем данные в словарь
quote_data = {
'text': text,
'author': author,
'tags': tags
}
all_quotes_data.append(quote_data)
return all_quotes_data
if __name__ == '__main__':
scraped_data = scrape_quotes()
if scraped_data:
# Выводим данные в формате JSON для наглядности
print(json.dumps(scraped_data, indent=4, ensure_ascii=False))
В этом коде мы создаем список словарей (all_quotes_data). Каждый словарь представляет одну цитату и имеет четкие ключи (text, author, tags). Это очень гибкий и типичный для Python подход.
1. Настройка проекта:
Откройте терминал, создайте новый проект и установите AngleSharp:
Bash
dotnet new console -n CSharpScraper
cd CSharpScraper
dotnet add package AngleSharp
2. Написание кода:
Сначала определим класс для хранения наших данных. Это строго типизированный подход, характерный для C#.
Создайте новый файл Quote.cs:
C#
// Quote.cs
using System.Collections.Generic;
public class Quote
{
public string Text { get; set; }
public string Author { get; set; }
public List<string> Tags { get; set; }
}
Теперь изменим основной файл Program.cs:
C#
// Program.cs
using AngleSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
public class Program
{
public static async Task Main(string args)
{
var scrapedData = await ScrapeQuotesAsync();
if (scrapedData.Any())
{
// Настраиваем параметры сериализации для красивого вывода
var options = new JsonSerializerOptions
{
WriteIndented = true,
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
string jsonString = JsonSerializer.Serialize(scrapedData, options);
Console.WriteLine(jsonString);
}
}
public static async Task<List<Quote>> ScrapeQuotesAsync()
{
var allQuotesData = new List<Quote>();
var url = "http://quotes.toscrape.com/";
try
{
// Настраиваем AngleSharp для загрузки страницы
var config = Configuration.Default.WithDefaultLoader();
var context = BrowsingContext.New(config);
var document = await context.OpenAsync(url);
// Используем CSS-селекторы для поиска контейнеров с цитатами
var quoteElements = document.QuerySelectorAll("div.quote");
foreach (var element in quoteElements)
{
var text = element.QuerySelector("span.text")?.TextContent?? "N/A";
var author = element.QuerySelector("small.author")?.TextContent?? "N/A";
var tags = element.QuerySelectorAll("a.tag")
.Select(e => e.TextContent)
.ToList();
allQuotesData.Add(new Quote
{
Text = text,
Author = author,
Tags = tags
});
}
}
catch (Exception ex)
{
Console.WriteLine($"Произошла ошибка: {ex.Message}");
}
return allQuotesData;
}
}
Здесь мы сначала создаем класс Quote, который четко определяет структуру наших данных. Это требует немного больше кода вначале, но обеспечивает безопасность типов и лучшую читаемость в больших проектах. Запустив любой из этих скриптов, вы получите одинаковый по смыслу результат: структурированный список цитат, авторов и тегов, извлеченный с веб-страницы. Вы только что создали свой первый работающий парсер!
Когда ваш проект по парсингу вырастает за пределы одной-двух страниц, простой скрипт на requests и BeautifulSoup становится громоздким и неэффективным. Нужно обрабатывать постраничную навигацию (пагинацию), управлять потоком данных, сохранять результаты в разные форматы, обрабатывать ошибки. Для таких задач существуют фреймворки — готовые каркасы для приложений, которые берут на себя большую часть рутинной работы.
В мире Python королем фреймворков для парсинга является Scrapy. Это мощный, быстрый и асинхронный фреймворк, созданный специально для написания сложных и производительных краулеров.81
Главное преимущество Scrapy — его асинхронная природа. Обычный скрипт работает последовательно: отправил запрос -> дождался ответа -> обработал -> отправил следующий запрос. Scrapy же работает как кофейня с несколькими бариста: он может отправить новый запрос, не дожидаясь ответа на предыдущий. Пока один ответ загружается, Scrapy уже работает над другими задачами. Это позволяет выполнять сотни запросов «параллельно», значительно ускоряя процесс сбора данных.82 При этом вам не нужно глубоко вникать в сложности асинхронного программирования на Python — Scrapy делает всю магию «под капотом».
Scrapy имеет четкую структуру, которая помогает организовать код 83:
В экосистеме.NET не существует прямого аналога Scrapy, который бы предоставлял такой же комплексный и готовый к использованию фреймворк для парсинга. Разработчики на.NET обычно собирают аналогичную функциональность из отдельных библиотек.
Давайте перепишем наш парсер цитат с использованием Scrapy, добавив переход по страницам.
1. Установка и создание проекта:
Bash
pip install scrapy
scrapy startproject quotes_project
cd quotes_project
Эта команда создаст папку quotes_project с полной структурой файлов Scrapy.80
2. Создание «паука»:
Перейдите в папку проекта и выполните команду для генерации паука:
Bash
scrapy genspider quotes quotes.toscrape.com
Эта команда создаст файл quotes_project/spiders/quotes.py с шаблоном паука.85
3. Написание логики паука:
Откройте quotes_project/spiders/quotes.py и измените его следующим образом:
import scrapy
class QuotesSpider(scrapy.Spider):
# Уникальное имя паука
name = 'quotes'
# Домен, за пределы которого паук не будет выходить
allowed_domains = ['quotes.toscrape.com']
# Стартовый URL
start_urls = ['http://quotes.toscrape.com/']
def parse(self, response):
"""
Этот метод вызывается для каждой загруженной страницы.
Он извлекает данные и находит ссылку на следующую страницу.
"""
# Используем CSS-селекторы Scrapy
for quote in response.css('div.quote'):
# yield возвращает данные в Scrapy
yield {
'text': quote.css('span.text::text').get(),
'author': quote.css('small.author::text').get(),
'tags': quote.css('div.tags a.tag::text').getall(),
}
# Находим ссылку на следующую страницу
next_page = response.css('li.next a::attr(href)').get()
if next_page is not None:
# Если ссылка найдена, создаем новый запрос к ней,
# используя тот же метод parse для обработки.
# response.follow автоматически создает полный URL.
yield response.follow(next_page, callback=self.parse)
Обратите внимание на ключевые отличия от простого скрипта:
4. Запуск паука и сохранение данных:
Вернитесь в корневую папку проекта (quotes_project) и запустите паука. Scrapy позволяет легко сохранять результаты в разные форматы прямо из командной строки.86
Команда -O перезаписывает файл, а -o — дописывает в конец (для JSON это создаст невалидный файл, поэтому для дозаписи лучше использовать формат JSON Lines с расширением .jl).86
Scrapy — это огромный шаг вперед по сравнению с простыми скриптами. Он предоставляет надежную архитектуру для создания сложных, быстрых и легко поддерживаемых краулеров, которые могут обходить целые сайты, а не только отдельные страницы.
До сих пор мы работали со «статическими» сайтами. Это означает, что весь HTML-контент страницы приходит от сервера в первом же ответе на наш GET-запрос. Однако все больше современных веб-сайтов являются «динамическими». Они используют JavaScript для загрузки данных и построения страницы уже после того, как основной HTML-каркас был получен.
Когда вы используете requests в Python или HttpClient в.NET, вы получаете только первоначальный HTML. Если важные данные (например, список товаров или комментарии) подгружаются позже с помощью JavaScript, вы их просто не увидите. Ваш парсер получит пустой «скелет» страницы.
Чтобы решить эту проблему, нам нужен инструмент, который умеет выполнять JavaScript так же, как это делает ваш обычный браузер (Chrome, Firefox). Такие инструменты существуют, и они могут управлять настоящим браузером, но без графического интерфейса. Этот режим работы называется «безголовым» (headless).66 Наш код будет программно отдавать команды браузеру: «открой эту страницу», «прокрути вниз», «нажми на эту кнопку», а затем, когда JavaScript отработает и все данные появятся на странице, мы сможем забрать финальный, полностью отрендеренный HTML и отдать его нашему парсеру (BeautifulSoup или AngleSharp).
Два самых популярных инструмента для автоматизации браузера — это Selenium и Playwright.
Selenium — это мощный фреймворк для автоматизации браузеров, изначально созданный для тестирования веб-приложений, но широко адаптированный для парсинга.87
Настройка:
Для работы с Selenium требуется две вещи: библиотека для вашего языка и специальный драйвер для браузера, которым вы хотите управлять.
Пример использования (Python):
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from bs4 import BeautifulSoup
import time
# Указываем путь к драйверу
# Для более новых версий Selenium (4.6+) можно не указывать путь,
# если драйвер в PATH или используется Selenium Manager
driver = webdriver.Chrome()
try:
# Открываем сайт с динамическим контентом
driver.get("http://quotes.toscrape.com/js/")
# Ждем, пока не загрузится хотя бы один блок с цитатой (до 10 секунд)
# Это критически важный шаг для динамических сайтов!
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.CLASS_NAME, "quote"))
)
# Получаем исходный код страницы ПОСЛЕ выполнения JavaScript
html_content = driver.page_source
# Теперь парсим его с помощью BeautifulSoup
soup = BeautifulSoup(html_content, 'lxml')
for quote in soup.select('div.quote'):
print(quote.select_one('span.text').text)
finally:
# Обязательно закрываем браузер
driver.quit()
Ключевой момент здесь — WebDriverWait. Мы явно говорим Selenium подождать, пока нужный нам элемент не появится на странице, прежде чем забирать HTML.90
Пример использования (.NET):
C#
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
using System;
using AngleSharp;
// Путь к chromedriver.exe
IWebDriver driver = new ChromeDriver();
try
{
driver.Navigate().GoToUrl("http://quotes.toscrape.com/js/");
// Явное ожидание
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until(d => d.FindElement(By.ClassName("quote")));
string htmlContent = driver.PageSource;
// Парсинг с помощью AngleSharp
var context = BrowsingContext.New(Configuration.Default);
var document = await context.OpenAsync(req => req.Content(htmlContent));
var quoteElements = document.QuerySelectorAll("div.quote");
foreach (var element in quoteElements)
{
Console.WriteLine(element.QuerySelector("span.text").TextContent);
}
}
finally
{
driver.Quit();
}
Playwright — это более современная библиотека для автоматизации от Microsoft, которая часто оказывается быстрее, надежнее и проще в использовании, чем Selenium.91 Она имеет более современный API и лучше справляется с «капризами» современных веб-приложений.
Настройка:
Установка Playwright проще, так как он сам управляет браузерами.
pip install playwright
playwright install # Эта команда скачает браузеры (Chromium, Firefox, WebKit)
dotnet add package Microsoft.Playwright
# Запустите этот скрипт из папки проекта в PowerShell
pwsh bin/Debug/net8.0/playwright.ps1 install
Пример использования (Python, асинхронный API):
Playwright изначально асинхронен, что делает его очень эффективным.
import asyncio
from playwright.async_api import async_playwright
async def main():
async with async_playwright() as p:
browser = await p.chromium.launch(headless=True) # можно headless=False для отладки
page = await browser.new_page()
await page.goto("http://quotes.toscrape.com/js/")
# Playwright имеет встроенные механизмы ожидания.
# Метод locator сам подождет появления элемента.
first_quote = page.locator("div.quote").first
await first_quote.wait_for()
# Используем встроенные локаторы Playwright для извлечения данных
all_quotes = await page.locator("div.quote").all()
for quote in all_quotes:
text = await quote.locator("span.text").inner_text()
print(text)
await browser.close()
asyncio.run(main())
Одно из главных преимуществ Playwright — его «автоматические ожидания». Вам не всегда нужно писать явные WebDriverWait, так как многие методы Playwright сами ждут появления элемента перед тем, как с ним взаимодействовать.91
Пример использования (.NET):
C#
using Microsoft.Playwright;
using System;
using System.Threading.Tasks;
class Program
{
public static async Task Main()
{
using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Chromium.LaunchAsync(new() { Headless = true });
var page = await browser.NewPageAsync();
await page.GotoAsync("http://quotes.toscrape.com/js/");
// Локатор сам будет ждать появления элемента
var firstQuote = page.Locator("div.quote").First;
await firstQuote.WaitForAsync();
var allQuotes = await page.Locator("div.quote").AllAsync();
foreach (var quote in allQuotes)
{
var text = await quote.Locator("span.text").InnerTextAsync();
Console.WriteLine(text);
}
await browser.CloseAsync();
}
}
Хотя Selenium является устоявшимся стандартом с огромным количеством документации, для новых проектов стоит рассмотреть Playwright. Он часто предлагает более элегантный и надежный способ решения тех же задач, особенно при работе со сложными одностраничными приложениями (SPA).
Создать парсер, который работает один раз, — это только полдела. Создать скрейпер, который работает надежно, долго и не создает проблем ни вам, ни владельцам сайтов, — вот настоящее мастерство. Веб-сайты активно защищаются от ботов, и это превращается в своего рода «игру в кошки-мышки».75 Чтобы ваш скрейпер не был быстро заблокирован, необходимо следовать ряду лучших практик.
Цель этих практик — не быть агрессивным, а, наоборот, сделать вашего бота как можно более похожим на обычного пользователя. Это стратегия «цифрового камуфляжа»: не пытаться быть невидимым, а слиться с толпой обычного трафика. Использование только одного из этих методов в изоляции часто бывает недостаточно, так как продвинутые системы защиты анализируют совокупность факторов.
import requests
import random
# Список ваших прокси-серверов (платные прокси надежнее)
proxies = [
'http://user:pass@1.2.3.4:8080',
'http://user:pass@5.6.7.8:8080',
]
# Выбираем случайный прокси
proxy = random.choice(proxies)
response = requests.get(
'https://httpbin.org/ip', # Этот сайт вернет IP, с которого пришел запрос
proxies={'http': proxy, 'https': proxy}
)
print(response.json())
using System.Net;
using System.Net.Http;
var proxy = new WebProxy
{
Address = new Uri("http://1.2.3.4:8080"),
BypassProxyOnLocal = false,
UseDefaultCredentials = false,
// Если прокси требует аутентификации
Credentials = new NetworkCredential("user", "pass")
};
var httpClientHandler = new HttpClientHandler
{
Proxy = proxy,
};
var client = new HttpClient(handler: httpClientHandler);
var response = await client.GetAsync("https://httpbin.org/ip");
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(content);
import requests
import random
user_agents =
headers = {'User-Agent': random.choice(user_agents)}
response = requests.get('https://httpbin.org/user-agent', headers=headers)
print(response.json())
//... (код с HttpClient)
var userAgents = new List<string> { /*... список... */ };
var random = new Random();
var userAgent = userAgents[random.Next(userAgents.Count)];
client.DefaultRequestHeaders.UserAgent.ParseAdd(userAgent);
var response = await client.GetAsync("https://httpbin.org/user-agent");
//...
import time
import random
for i in range(10):
#... ваш код для отправки запроса...
print(f"Сделан запрос номер {i+1}")
# Пауза от 1 до 3 секунд
delay = random.uniform(1, 3)
print(f"Пауза на {delay:.2f} секунд")
time.sleep(delay)
Для более сложных сценариев существуют библиотеки, такие как requests_ratelimiter, которые могут управлять частотой запросов более гибко.103
var random = new Random();
for (int i = 0; i < 10; i++)
{
//... ваш код для отправки запроса...
Console.WriteLine($"Сделан запрос номер {i + 1}");
// Пауза от 1000 до 3000 миллисекунд
int delay = random.Next(1000, 3001);
Console.WriteLine($"Пауза на {delay} мс");
await Task.Delay(delay);
}
Применяя эти три техники в комплексе, вы значительно повышаете шансы на успешный и долгосрочный сбор данных, оставаясь при этом «хорошим гражданином» интернета.
Извлечение данных — это только половина дела. Чтобы эти данные принесли пользу, их нужно сохранить в удобном и структурированном формате. Выбор формата хранения зависит от ваших дальнейших целей: хотите ли вы просто просмотреть данные в таблице, использовать их в другом приложении или проводить сложный анализ. Этот выбор лучше делать заранее, чтобы «начинать, имея в виду конечную цель».
Мы рассмотрим три самых распространенных способа хранения: CSV, JSON и база данных SQLite.
CSV — это простой текстовый формат, представляющий собой таблицу, где значения в столбцах разделены запятыми (или другими разделителями, например, точкой с запятой). Файлы CSV легко открываются в любом табличном редакторе, таком как Microsoft Excel или Google Sheets, что делает их идеальными для простого анализа и визуализации.
Подход Python (с библиотекой pandas):
Библиотека pandas — это золотой стандарт для работы с данными в Python. Она предоставляет мощную структуру данных под названием DataFrame, которая очень похожа на таблицу.
import pandas as pd
# Предположим, это ваши собранные данные
scraped_data =},
{'text': 'It is our choices, Harry...', 'author': 'J.K. Rowling', 'tags': ['abilities', 'choices']}
]
# Создаем DataFrame из списка словарей
df = pd.DataFrame(scraped_data)
# Сохраняем в CSV-файл без индекса pandas
# encoding='utf-8-sig' важен для корректного отображения кириллицы в Excel
df.to_csv('quotes.csv', index=False, encoding='utf-8-sig')
print("Данные успешно сохранены в quotes.csv")
Подход.NET (с библиотекой CsvHelper):
CsvHelper — это популярная и мощная библиотека для работы с CSV в.NET.
using CsvHelper;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
// Предположим, у вас есть класс Quote и список с данными
// public class Quote { public string Text { get; set; }... }
var scraped_data = new List<Quote>
{
new Quote { Text = "...", Author = "...", Tags = new List<string>{"tag1"} }
};
// Записываем данные в файл
using (var writer = new StreamWriter("quotes.csv"))
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
// CsvHelper автоматически использует публичные свойства объекта как заголовки
csv.WriteRecords(scraped_data);
}
Console.WriteLine("Данные успешно сохранены в quotes.csv");
JSON — это текстовый формат обмена данными, основанный на парах «ключ-значение». Он очень популярен в веб-приложениях и API, так как легко читается и людьми, и машинами.109 Подход Python (встроенный модуль json):
Python имеет встроенную поддержку JSON.
import json
scraped_data = [
{'text': '...', 'author': '...', 'tags': ['...']}
]# Записываем данные в файл
with open('quotes.json', 'w', encoding='utf-8') as f:
# indent=4 делает файл читаемым для человека
# ensure_ascii=False для корректного отображения не-ASCII символов
json.dump(scraped_data, f, indent=4, ensure_ascii=False)
print("Данные успешно сохранены в quotes.json")
Подход.NET (встроенный System.Text.Json):
В современных версиях.NET для работы с JSON используется высокопроизводительная библиотека System.Text.Json.
C#
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Text.Encodings.Web;
//... список объектов Quote...
var scraped_data = new List<Quote> { /*... */ };
var options = new JsonSerializerOptions
{
WriteIndented = true, // Для красивого форматирования
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping // Для корректной записи символов
};
// Сериализуем список объектов в JSON-строку
string jsonString = JsonSerializer.Serialize(scraped_data, options);
// Записываем строку в файл
File.WriteAllText("quotes.json", jsonString);
Console.WriteLine("Данные успешно сохранены в quotes.json");
Когда данных становится много или вы планируете их регулярно обновлять и делать сложные запросы, хранение в файлах становится неэффективным. Здесь на помощь приходят базы данных. SQLite — это легковесная, файловая СУБД, которая не требует отдельного сервера и идеально подходит для небольших и средних проектов.
Подход Python (встроенный модуль sqlite3):
Python поставляется со встроенной поддержкой SQLite.
import sqlite3
scraped_data = [
{'text': '...', 'author': '...', 'tags': ['change', 'deep-thoughts']}
]
# Подключаемся к файлу БД (он будет создан, если не существует)
conn = sqlite3.connect('quotes.db')
cursor = conn.cursor()
# Создаем таблицу, если ее нет
cursor.execute('''
CREATE TABLE IF NOT EXISTS quotes (
id INTEGER PRIMARY KEY,
text TEXT,
author TEXT,
tags TEXT
)
''')
# Вставляем данные
for item in scraped_data:
cursor.execute(
"INSERT INTO quotes (text, author, tags) VALUES (?,?,?)",
(item['text'], item['author'], ','.join(item['tags'])) # теги сохраняем как строку
)
# Сохраняем изменения и закрываем соединение
conn.commit()
conn.close()
print("Данные успешно сохранены в quotes.db");
Подход.NET (с библиотекой Microsoft.Data.Sqlite):
В.NET для работы с базами данных часто используют ORM (Object-Relational Mapper) вроде Entity Framework Core, но для простоты мы воспользуемся более низкоуровневой библиотекой, аналогичной sqlite3 в Python.
using Microsoft.Data.Sqlite;
using System.Collections.Generic;
//... список объектов Quote...
var scraped_data = new List<Quote> { /*... */ };
using (var connection = new SqliteConnection("Data Source=quotes.db"))
{
connection.Open();
var command = connection.CreateCommand();
command.CommandText = @"
CREATE TABLE IF NOT EXISTS quotes (
id INTEGER PRIMARY KEY,
text TEXT,
author TEXT,
tags TEXT
);
";
command.ExecuteNonQuery();
// Вставляем данные
foreach (var item in scraped_data)
{
var insertCmd = connection.CreateCommand();
insertCmd.CommandText = "INSERT INTO quotes (text, author, tags) VALUES ($text, $author, $tags);";
insertCmd.Parameters.AddWithValue("$text", item.Text);
insertCmd.Parameters.AddWithValue("$author", item.Author);
insertCmd.Parameters.AddWithValue("$tags", string.Join(",", item.Tags));
insertCmd.ExecuteNonQuery();
}
}
Console.WriteLine("Данные успешно сохранены в quotes.db");
Выбор правильного способа хранения данных — это ключ к их эффективному использованию в будущем.
Поздравляем! Вы прошли путь от полного новичка до человека, обладающего одним из самых мощных навыков в современном цифровом мире. Вы научились не просто писать код на Python и.NET, но и понимать фундаментальные принципы работы веба, этические и юридические нормы, а также применять передовые инструменты для извлечения, обработки и хранения данных.
Давайте еще раз закрепим ключевые принципы ответственного парсинга, которые должны стать вашей второй натурой 8:
Освоение парсинга — это не конечная точка, а начало увлекательного путешествия в мир данных. Вот несколько направлений для вашего дальнейшего развития:
Вы получили в свои руки мощный инструмент. Используйте его с умом, ответственностью и любопытством. Веб — это безграничный источник знаний, и теперь у вас есть ключ, чтобы превратить его необработанную информацию в ценные и полезные открытия. Удачи в ваших проектах!
Краткое саммари: опасная иллюзия легких лидов В мире жесткой конкуренции идея быстро пополнить клиентскую базу,…
Краткое резюме: как превратить сеть сайтов в стабильный источник дохода Создание сети информационных сайтов —…
Знаете ли вы, что невидимые технические ошибки могут «съедать» до 90% вашего потенциального трафика из…
Введение: почему мониторинг цен — необходимость, а защита — не преграда Представьте, что вы пытаетесь…
Значительная часть трафика на любом коммерческом сайте — это не люди. Это боты, которые могут…
Систематический мониторинг цен конкурентов — это не просто способ избежать ценовых войн, а доказанный инструмент…