Содержание урока · 11 разделов
- DOM: дерево страницы, по которому ходит скрапер
- Теги и атрибуты: на что смотрит скрапер
- Класс против id: сколько элементов выбирает селектор
- Инлайн и внешний контент: что видит скрапер
- Data-атрибуты и семантические теги: надёжные зацепки
- Elements в devtools: от клика до селектора
- Что где лежит: карта атрибутов скрапера
- Что дальше
- Песочница урока
- Коротко: что запомнить
- Вопросы и ответы
Вы открываете вкладку Elements в браузере, и на вас вываливается четыре тысячи строк разметки: вложенные div, классы-абракадабра, скрипты, мета-теги. Хочется закрыть и попросить кого-то другого. Хорошая новость: скраперу вёрстка нужна ровно в объёме, которого хватает, чтобы найти данные и не потерять их завтра. Вы не собираетесь править CSS и ловить баги отступов — вы ищете контейнеры, тексты и атрибуты, и на это уходит процентов двадцать языка. Уверенно читать дерево документа, понимать, чем класс отличается от идентификатора и зачем нужны data-атрибуты, — этого достаточно, чтобы писать селекторы для девяти задач из десяти. Остальная глубина — для верстальщиков, и она нам не пригодится. Всё, что ниже, разбирается на живой разметке песочницы quotes.toscrape.com: откройте её в соседней вкладке и проверяйте каждое утверждение глазами. Программных примеров в этом уроке почти нет — зато весь материал сразу ложится в скрапер из второго урока, когда вы запустите его локально с новым селектором.
DOM: дерево страницы, по которому ходит скрапер#
Браузер, получив HTML, строит из него DOM — объектную модель документа, и dom дерево страницы — это иерархия узлов: элемент внутри элемента внутри элемента. У каждой ветки есть родитель, у элементов — дети, у текста внутри тегов — текстовые узлы. Корень дерева — html, под ним head и body, и дальше разметка разветвляется по вложенности. Скраперу это дерево важно по одной причине: селекторы в colly ищут не строки в тексте, а узлы в дереве, и результат поиска зависит от того, где именно лежит узел и кто его предки. Один и тот же span на верхнем уровне и span внутри карточки товара — разные цели для селектора. Вложенность читается по отступам в исходнике: всё, что открыто между открывающим и закрывающим тегом контейнера, — его потомки. Ошибка новичка — судить о структуре по визуальному расположению на экране: браузер растягивает и переносит элементы так, что визуально рядом могут лежать узлы из разных веток дерева. Смотрите на вложенность, а не на картинку.
Разметка одной цитаты из quotes.toscrape
GO<div class="quote" itemscope itemtype="http://schema.org/CreativeWork">
<span class="text" itemprop="text">
"The world as we have created it is a process of our thinking."
</span>
<span>by <small class="author" itemprop="author">Albert Einstein</small></span>
<div class="tags">
Tags:
<meta class="keywords" itemprop="keywords" content="change,deep-thoughts">
<a class="tag" href="/tag/change/page/1/">change</a>
<a class="tag" href="/tag/deep-thoughts/page/1/">deep-thoughts</a>
</div>
</div>Типовой контейнер: div.quote обнимает всё, что относится к одной цитате; span.text несёт текст, small.author — автора, ссылки a.tag — теги. itemscope и itemprop — микроразметка schema.org: сайт помогает поисковикам, а заодно и нам. На странице таких контейнеров десять, по одному на цитату.
Теги и атрибуты: на что смотрит скрапер#
Тег задаёт тип узла — div, span, a, img, small, — а атрибуты приписаны тегу парами имя-значение. Для парсера значимы четыре вида: class и id, на которых держатся селекторы; href у ссылок и src у картинок — адреса, по которым ходит и качает скрапер; title и alt — человеческие подписи, в которых сайты любят прятать полные названия товаров; и data-атрибуты вида data-url, data-id — куда фронтенд складывает машиночитаемые значения. Текст узла — то, что лежит между открывающим и закрывающим тегом, — достаётся через e.Text, а атрибуты — через e.Attr; это два разных выхода из одного узла, и выбирать надо по тому, где данные надёжнее. Классический пример: у карточки товара в каталоге books.toscrape.com название обрезано в тексте ссылки до пары слов, а полный вариант лежит в атрибуте title той же ссылки — берут именно атрибут. Разметка обычно щедрее, чем кажется: у большинства узлов найдётся второй путь к данным.
Класс против id: сколько элементов выбирает селектор#
Класс описывает тип или роль элемента, и один класс может носить сколько угодно узлов на странице — десять карточек каталога делят один класс product_pod. Идентификатор id по стандарту должен быть уникален в документе: один узел — один id, чаще всего это главный контейнер страницы вроде id="content" или навигация. Отсюда правило выбора для скрапера: повторяющиеся данные — цитаты, товары, строки таблицы — ищут по классам, потому что их много; привязку к конкретному месту страницы делают по id, если он есть и стабильный. Жизнь вносит поправки: современные фронтенд-фреймворки генерируют id автоматически, и они меняются от сборки к сборке, а на некоторых сайтах один id тихо повторяется пять раз, потому что верстальщику было удобно. Поэтому и класс, и id стоит проверять взглядом: откройте Elements, посчитайте совпадения, пройдитесь на вторую страницу каталога и убедитесь, что селектор ловит то же самое. Проверка занимает минуту и экономит вечер отладки.
Инлайн и внешний контент: что видит скрапер#
Здесь спрятана ловушка, о которую спотыкаются все, кто привык к вкладке Elements. Скрапер получает исходный HTML-ответ сервера — то, что отдал сайт по сети, до всяких скриптов. Вкладка Elements показывает текущий DOM: после того как JavaScript успел дорисовать, переставить и удалить узлы. Для серверных страниц, как наши песочницы, разница почти нулевая. Но на сайтах с рендерингом в браузере Elements показывает данные, которых в исходном ответе нет: скрапер по селектору из Elements получит пустоту. Способ проверки без кода: правый клик по странице — «Просмотреть исходный код страницы» и поиск по тексту, который вы ищете. Нашли фрагмент в исходнике — colly до него доберётся; не нашли — данные приходят скриптом, и это уже разговор про браузерные движки из урока про пределы colly. Вторая половина той же мысли: текст, который вы видите на экране, может собираться из нескольких узлов и псевдоэлементов CSS, а e.Text берёт только то, что реально лежит в дереве.
Data-атрибуты и семантические теги: надёжные зацепки#
Хорошие сайты оставляют для машин явные зацепки, и ими стоит пользоваться в первую очередь. Data-атрибуты — всё, что начинается с data-, — придуманы для того, чтобы JavaScript носил данные прямо в разметке: data-id="42", data-url="/product/42", data-price="1490". Они почти не участвуют в оформлении, поэтому живут дольше классов; для скрапера это часто лучший источник значений. Семантические теги — article, main, nav, table, header — описывают смысл блока, а не его вид: карточка товара почти всегда article, список статей — внутри main. Селектор article.product_pod переживает редизайн чаще, чем div.row > div:nth-child(3), потому что смысл карточки не меняется вместе с сеткой раскладки. Микроразметка itemprop, как в разметке цитаты выше, — ещё один слой стабильности: сайты несут её для поисковиков и редко ломают. Приоритет простой: data-атрибуты и семантика, потом осмысленные классы, и только потом геометрия страницы.
На продакшен-сайтах классы бывают хэшированными: css-1x2abc, sc-jEABwJ, ProductCard__price--3kF9q. Их генерирует сборщик CSS-модулей, и при следующем деплое суффиксы переломаются — селектор .css-1x2abc отдаст ноль элементов, скрапер замолчит, а виноватых не найдут. Лечение: не вцепляться в хэш, а подниматься к устойчивому — семантическому тегу, data-атрибуту или стабильной части имени вроде ProductCard__price. Правило: селектор, в котором есть непроизносимая абракадабра, — селектор с датой смерти.
Elements в devtools: от клика до селектора#
Рабочий цикл скрапера начинается в браузере. Правый клик по нужному элементу на странице — «Просмотреть код» — и вкладка Elements разворачивается на том самом узле. Дальше три вопроса: что это за тег, какие у него классы и атрибуты, и что вокруг — в каком контейнере он лежит. Контейнер важнее всего: селектор, построенный от контейнера цитаты, точнее, чем селектор, построенный от самого текста. В контекстном меню узла есть Copy selector — и им стоит пользоваться с оговоркой: devtools часто выдаёт абсолютный путь вида body > div.container > div.row > div.col-md-8 > div.quote:nth-child(4) > span.text — с номерами детей, которые переломаются, как только на странице станет на одну цитату больше или меньше. Такой вариант берут как сырьё: сокращают до повторяющейся части div.quote span.text и проверяют поиском по документу, сколько узлов ловится. Самая эффективная привычка — искать по классу в поиске devtools: Ctrl+F внутри Elements показывает все совпадения и сразу даёт понять, уникален ли селектор.
Что где лежит: карта атрибутов скрапера#
| Атрибут | Что в нём | Как использовать |
|---|---|---|
| class | роль элемента, обычно не уникальна | основной материал селекторов для повторяющихся данных |
| id | уникальный идентификатор узла | привязка к конкретному блоку страницы, если id стабильный |
| href, src | адреса ссылок и картинок | источник для обхода и для выкачивания файлов |
| title, alt | полные подписи элементов | полные названия, обрезанные в тексте, и описания картинок |
| data-имя | машиночитаемые значения фронтенда | часто самый стабильный путь к id, url и ценам |
| itemprop, content | микроразметка schema.org | структурированные данные, которые сайты держат для поисковиков |
Проверка: класс и id
На странице десять карточек товара. Какой селектор их соберёт?
Проверка: Elements против исходника
Во вкладке Elements карточка товара видна, но скрапер по селектору получает ноль элементов. Первое действие?
Что выберет селектор в этой ветке
Прочитайте код и выберите, что он выведет:
<div class="quote">
<span class="text">"Be yourself; everyone else is already taken."</span>
<span>by <small class="author">Oscar Wilde</small></span>
</div>
// Селектор: .quote .author
// Вопрос: какой текст достанет e.Text на этом элементе?Соберите разметку цитаты
Допишите фрагмент разметки: класс контейнера цитаты, класс текста и класс автора — ровно как в quotes.toscrape.
Соберите вложенность разметки
Расставьте строки так, чтобы получилась корректная ветка дерева: контейнер, его дети, закрытия тегов в обратном порядке.
Строки
Ваша программа
Нажимайте на строки слева, чтобы собрать программу
Карточки: HTML глазами скрапера
Что дальше#
Разметка прочитана, зацепки найдены — пора превращать их в запросы. Следующий урок — CSS-селекторы в colly: от простых тегов и классов до комбинаций, атрибутных условий и псевдоклассов. А после него — пятый, про механику колбэков OnHTML: что именно colly делает с найденными узлами и как не намолотить дубликатов. Если захочется освежить, зачем всё это нужно и где границы темы, — вернитесь к знакомству с Colly.
Песочница урока
Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.
Запустите код, чтобы увидеть вывод.
Коротко: что запомнить
- Скраперу хватает минимума HTML: дерево документа, теги, атрибуты, текст узлов — вёрстка на этом объёме заканчивается.
- DOM — дерево вложенных узлов; селектор ловит узлы по месту в дереве, а не по внешнему виду на экране.
- Классы носят много элементов — ими собирают повторяющиеся данные; id уникален и подходит для привязки к блоку, если он стабильный.
- Важные атрибуты: href и src — адреса, title и alt — полные подписи, data-* — машиночитаемые значения, itemprop — микроразметка.
- Elements показывает DOM после скриптов, скрапер видит исходный HTML-ответ; расхождение проверяют через просмотр исходного кода.
- Хэшированные классы вроде css-1x2abc переламываются при деплое — зацепки выбирают по убыванию стабильности: data-атрибуты, семантика, осмысленные классы.
- Copy selector из devtools — сырьё, а не готовое решение: абсолютные пути с nth-child сокращают и проверяют на второй странице.
Домашнее задание
Откройте quotes.toscrape.com и books.toscrape.com в двух вкладках devtools. Для каждой песочницы выпишите: класс контейнера повторяющегося блока, селектор текста, селектор атрибута со ссылкой и один data-атрибут или itemprop, если найдёте. Потом возьмите Copy selector на карточке товара в books и сократите его до самого короткого варианта, который ловит все карточки на странице — проверьте поиском по документу.
Личные заметкиПусто
Вопросы и ответы по уроку
Насколько глубоко нужно знать HTML для парсинга?
Хватит дерева документа, тегов, атрибутов и классов: контейнер, текст, ссылка, картинка. Флексбоксы, сетки и обработчики событий — территория верстальщика; скраперу важнее уметь читать вложенность и выбирать стабильные зацепки.
Почему Copy selector из devtools не работает в скрапере?
Работает, но недолго: devtools выдаёт абсолютный путь с номерами детей (nth-child), который переламывается при любом изменении количества блоков. Сократите путь до повторяющейся части — контейнер плюс цель — и проверьте на нескольких страницах.
Скрапер видит страницу так же, как браузер?
Нет: он получает исходный HTML-ответ, до исполнения JavaScript. Вкладка Elements показывает уже обработанный DOM. Если данные есть только в Elements, их рисуют скрипты, и colly по ним пройдёт с пустыми руками.
Термины урока
Ключевые понятия из этого урока — короткие определения в глоссарии. Наведите на термин, чтобы вспомнить, откройте карточку для подробностей и ссылки на практику.
Изучайте дальше
Следующая ступень учебного пути
Testify: тестирование Go с assert и require
Интерактивный курс Testify для Go: assert и require, табличные тесты, suite, mock-объекты, Eventually, HTTP-тесты. 25 уроков с примерами и практикой в браузере.
- Писать читаемые проверки через assert и require, а не лестницы if
- Проверять ошибки, паники и краевые случаи без ручного кода
- Сравнивать структуры, слайсы, карты и числа с плавающей точкой
- Строить табличные тесты и сьюты с хуками подготовки