Поначалу веб-скрейпинг может казаться пугающим, но, как и любой навык, его лучше всего осваивать на практике. В этих первых трёх главах мы разберём основы извлечения данных из статических HTML-страниц.
Глава 1: HTML-скрейпер Chad
Наше путешествие начинается с самого простого сценария — извлечения текста из базовой HTML-страницы. В этой главе вы познакомитесь с ключевыми понятиями:
- Выполнение HTTP-запросов для получения веб-страниц
- Загрузка HTML-содержимого в парсер
- Базовый выбор элементов DOM с помощью CSS-селекторов
Хотя пример может показаться тривиальным (в конце концов, это всего лишь “hello world”!), он закладывает основу для всего, что будет дальше.
<!-- Example HTML structure -->
<div class="content">
<p>Some text we want to extract</p>
</div>
Вот небольшое представление о том, с чем мы будем работать:
// Basic concepts (not the solution!)
import * as cheerio from 'cheerio';
// Loading HTML content
const $ = cheerio.load(htmlContent);
// Using CSS selectors
const text = $('p').text(); // Selects all <p> tags
const specific = $('.content p').text(); // More specific selection
Глава 2: Структурированные данные
Дальше становится интереснее: мы переходим к учебной странице товара интернет-магазина. Здесь нам предстоит работать с несколькими элементами схожей структуры:
<!-- Example product structure (simplified) -->
<div class="product">
<h2>Product Name</h2>
<span class="price">$99.99</span>
<div class="specs">
<ul>
<li>Size: M</li>
<li>Color: Blue</li>
</ul>
</div>
</div>
Когда вы имеете дело со структурированными данными такого рода, стоит подумать о следующем:
// Conceptual approach (not the solution!)
$('.product').each((index, element) => {
// For each product, we might want to:
// 1. Extract the basic info
const name = $(element).find('h2').text();
// 2. Parse nested data
const specs = $(element).find('.specs li');
// 3. Structure the output
const data = {
name,
specs: specs.map(/* ... */),
};
});
Совет от профи: прежде чем писать код, уделите время анализу структуры HTML. Ищите закономерности в том, как организованы данные — есть ли повторяющиеся имена классов? Как связаны родительские и дочерние элементы?
Глава 3: Скрейпинг с помощью AI
А вот теперь действительно становится интересно! Если в предыдущих заданиях мы изучали традиционные техники скрейпинга, то в главе 3 появляется современный подход: веб-скрейпинг с помощью AI. Перед нами кошмарный сценарий — непоследовательные структуры HTML, обфусцированные имена классов и вперемешку несколько шаблонов разных фреймворков.
Давайте посмотрим, что делает это задание особенным:
<!-- Traditional product structure -->
<div data-testid="product-container-1" class="_3xj_item">
<h2 data-qa="name">Red Sneakers</h2>
<span data-price-current="5999">$59.99</span>
</div>
<!-- React-style component -->
<div class="ProductCard-root-1a2b3c">
<div class="ProductCard-title-4d5e6f">Pink Walking Shoes</div>
<div class="ProductCard-pricing-7g8h9i">$84.99</div>
</div>
<!-- Vue-style template -->
<div data-v-abcdef class="product">
<h2 data-v-abcdef>Navy Boat Shoes</h2>
<span data-v-abcdef>$79.99</span>
</div>
Традиционные подходы к скрейпингу здесь дают сбой, потому что:
- Имена классов рандомизированы или привязаны к конкретному фреймворку
- Структуры данных различаются от товара к товару
- Разные фреймворки используют разные шаблоны
- Семантический смысл теряется в разметке
И вот здесь на помощь приходит AI. Вместо того чтобы писать хрупкие селекторы, мы можем описать на естественном языке, что именно нам нужно, а AI возьмёт на себя сопоставление паттернов. Ключевые идеи этой главы включают:
- Prompt engineering для веб-скрейпинга
- Использование AI для понимания семантического смысла
- Обработку непоследовательных структур данных
- Работу с разметкой, специфичной для разных фреймворков
- Поддержание согласованности данных между разными шаблонами
AI — не магия, но он отлично справляется с задачами, где нужны распознавание паттернов и адаптация. Именно поэтому он особенно полезен для скрейпинга современных веб-приложений, где нельзя рассчитывать на стабильные паттерны разметки.
Примечание об использовании AI
Помните: помощь AI не означает полностью автоматизированные решения. Наилучшие результаты даёт сочетание:
- Чёткой постановки задачи
- Хорошо структурированных промптов
- Валидации данных
- Человеческого контроля
Ваша задача — составить такие промпты, которые помогут AI понять и структуру, и смысл того, что вы пытаетесь извлечь.
Готовы объединить знания о традиционном веб-скрейпинге с современными возможностями AI? Давайте разберёмся, как AI может помочь справиться даже с самым хаотичным HTML!
Подсказки
- Поэкспериментируйте с разными CSS-селекторами:
// Different ways to select elements
$('.class') // By class
$('#id') // By ID
$('div > p') // Direct children
$('div p') // All descendants
$('[data-type="x"]') // By attribute
- Попробуйте изменить формат вывода
- Подумайте об обработке ошибок и пограничных случаях
- Подумайте, как ваше решение будет масштабироваться на более крупных наборах данных
Весь код, необходимый для старта, уже есть в репозитории проекта. Клонируйте его, настройте окружение и начинайте скрейпить!
git clone https://github.com/jonaylor89/housefly.git
cd housefly
npm install
Запустите серверы глав локально — каждая глава работает на своём порту (3001–3011):
turbo dev
Затем отредактируйте свой скрейпер в exercises/chapter-NN/starter/src/index.ts и проверьте его:
npm run validate -- <chapter>
Нужны подсказки? Запустите npm run hint -- <chapter> для пошаговых наводок. Исходный HTML для каждого задания доступен в директории apps/chapter{n}/, а эталонные решения находятся в exercises/chapter-NN/solution/src/. Изучайте структуру, продумывайте подход и помните: каждый эксперт когда-то был новичком.
Не забывайте ответственно обращаться со своими запросами:
// Basic error handling example
async function fetchPage(url: string) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.text();
} catch (error) {
console.error('Failed to fetch page:', error);
throw error;
}
}
Удачного скрейпинга!