Содержание урока · 11 разделов
- Три простые формы: тег, класс, идентификатор
- Потомок и прямая вложенность: пробел против угловой скобки
- Атрибутные селекторы: фильтр по значению
- Псевдоклассы: nth-of-type и first-of-type
- Селектор — что выбирает — когда использовать
- Что именно поддерживает colly: базовое подмножество CSS3
- Правила устойчивого селектора
- Что дальше
- Песочница урока
- Коротко: что запомнить
- Вопросы и ответы
Ситуация, знакомая каждому первому вечеру со скрапером: во вкладке Elements вы глазами нашли нужный элемент, написали селектор по памяти — и получили либо пустоту, либо полстраницы лишнего. Дело не в colly и не в сайте, а в том, что селектор — это точный запрос к дереву, а не описание «где примерно лежит текст». Хорошая новость: язык маленький. Пара десятков форм покрывает все задачи скрапинга, и в этом уроке мы разберём их на двух песочницах — цитатах и книжном каталоге books.toscrape.com. Каждый селектор ниже можно проверить руками: поиском по документу в devtools, а потом в настоящей программе. К концу урока у вас будет внутренняя таблица соответствий: вот это — для повторяющихся карточек, вот это — для единственного блока, вот это — для ссылок, отфильтрованных по адресу. И второе умение, не менее важное: понимать, когда селектор слишком умный и сломается при следующем релизе сайта. Примеры урока, как и везде в курсе, запускаются локально: скраперу нужна сеть, которой у браузерной песочницы нет, — а вот проверять селекторы в devtools можно прямо сейчас.
Три простые формы: тег, класс, идентификатор#
Базовые формы вы уже видели в прошлых уроках. Селектор-тег — span, a, h1 — выбирает все узлы с таким тегом: span.text на странице цитат поймает и тексты, но вместе с ними и служебные span, поэтому тегом пользуются как частью более длинных запросов. Селектор-класс начинается с точки: .price_color собирает все элементы с классом price_color — на books.toscrape.com это цены всех двадцати книг страницы. Селектор-идентификатор начинается с решётки: #content — единственный узел с таким id, главный контейнер страницы. Colly передаёт строку селектора в goquery, тот компилирует её и ищет совпадения в DOM-дереве ответа — вы пишете обычный CSS, никаких особых диалектов. На практике выбор формы простой: класс — рабочая лошадка для повторяющихся данных, идентификатор — редкая роскошь привязки к конкретному блоку, тег в одиночку — почти всегда слишком широкая сеть. Комбинация из двух-трёх форм — article.product_pod, div.quote, li.next a — закрывает большинство селекторных задач каталогов.
Три правила на книжном каталоге
GOc := colly.NewCollector()
c.OnHTML(".price_color", func(e *colly.HTMLElement) {
fmt.Println("цена:", e.Text)
})
c.OnHTML("article.product_pod h3 a", func(e *colly.HTMLElement) {
fmt.Println("название:", e.Attr("title"))
})
err := c.Visit("http://books.toscrape.com/")Первое правило берёт цены по классу: .price_color стоит ровно у тех узлов, где живёт цена. Второе поднимается от карточки article.product_pod к заголовку и ссылке внутри — а значение берёт из атрибута title, потому что в тексте ссылки название обрезано. Два правила работают независимо и печатают по двадцать значений каждый.
Потомок и прямая вложенность: пробел против угловой скобки#
Комбинатор — то, что стоит между двумя простыми селекторами, — и он же источник половины ошибок. Пробел означает любого потомка на любой глубине: article.product_pod a находит все ссылки внутри карточки, и те, что лежат сразу в ней, и те, что спрятаны в h3 или image_container. Угловая скобка — прямого ребёнка: article.product_pod > a найдёт только ссылки, лежащие непосредственно внутри карточки, без одного уровня вложенности — и на books.toscrape.com их не окажется, потому что ссылки спрятаны внутри h3 и div. Выбор между ними — вопрос глубины, в которой вы уверены. Пробел устойчив к мелким перестройкам разметки: вставьте сайт ещё один обёрточный div, и селектор с пробелом продолжит работать, а со скобкой — отвалится. Зато скобка защищает от ложных совпадений, когда целевой тег встречается и в карточке, и глубже внутри вложенного блока. Практическое правило: пока не нарвались на ложные совпадения, пишите пробел; скобку добавляйте, когда без неё селектор ловит лишнее.
Атрибутные селекторы: фильтр по значению#
Когда классов не хватает, включаются атрибуты. Форма [attr] выбирает узлы, у которых атрибут есть вовсе: [data-id] — все элементы с data-id. Форма [attr=value] требует точное значение: input[type="text"] — только текстовые поля. Для скрапинга самой полезной оказалась частичная проверка: [attr^=value] — значение начинается с префикса. На books.toscrape.com ссылки каталога ведут на адреса вида catalogue/page-2.html и catalogue/mediocre-xxx/index.html, и селектор a[href^="/catalogue/"] ловит только внутренние ссылки каталога, отсекая навигацию и внешние адреса — готовый фильтр для обхода, которому не нужен никакой код. Обратный префикс — [attr$=value]: img[src$=".jpg"] выбирает картинки по расширению. Атрибуты также спасают там, где классы сгенерированы сборкой: href, src, title, data-* обычно стабильнее имён классов, потому что отвечают за поведение, а не за оформление. Единственная цена — чуть более многословная строка, зато селектор читается как условие на естественном языке.
Псевдоклассы: nth-of-type и first-of-type#
Псевдоклассы описывают положение узла среди соседей. li:nth-of-type(2) выбирает второй элемент li среди однотипных соседей внутри родителя — счёт ведётся с единицы, а не с нуля, и это привычная ловушка для тех, кто пришёл из индексов Go. :first-of-type берёт первый однотипный узел, :last-of-type — последний. В скрапинге они нужны нечасто, но когда нужны — спасают полностью: колонка «второй столбец каждой строки таблицы», первый абзац карточки, второй div в блоке без классов. Две оговорки. Первая: of-type считает узлы одного тега, а не одного класса — .price:nth-of-type(2) означает «второй элемент своего тега, если у него есть класс price», и это не то же самое, что «второй price». Вторая: псевдоклассы жестоко завязаны на порядок соседей, а порядок — любимая вещь дизайнеров для перестановок. Если задачу можно решить без нумерации — контейнером, классом, атрибутом — решайте без неё; nth-of-type оставьте для случаев, когда у узла буквально нет других примет.
Селектор — что выбирает — когда использовать#
| Селектор | Что выбирает | Когда использовать |
|---|---|---|
| span | все span на странице | внутри более длинных комбинаций, редко один |
| .price_color | все узлы с этим классом | повторяющиеся данные: карточки, цены, строки |
| #content | единственный узел с этим id | привязка к главному блоку страницы |
| .quote span | любые span внутри .quote на любой глубине | дефолт для вложенности |
| .quote > span | только span — прямые дети .quote | когда пробел ловит лишнее из глубины |
| a[href^="/catalogue/"] | ссылки, ведущие внутрь каталога | фильтрация ссылок для обхода без кода |
| img[src$=".jpg"] | картинки с таким расширением | выкачивание файлов по типу |
| li:nth-of-type(2) | второй li среди соседей своего тега | положение без классов; счёт с единицы |
Что именно поддерживает colly: базовое подмножество CSS3#
Селекторы в OnHTML — это CSS3 в исполнении goquery, библиотеки разбора HTML, на которой colly собирает DOM и компилирует запросы. Говоря точнее: colly умеет базовое подмножество CSS3 — теги, классы, идентификаторы, комбинаторы вложенности, атрибутные условия и распространённые псевдоклассы. Экзотика современных браузеров и редкие псевдоклассы в этой реализации не гарантируются, и перечислять «что сломано» по памяти опасно: поведение зависит от версии библиотеки, а гугл-выдержки легко устаревают. Рабочая тактика практичнее: селектор сначала проверяется на живой странице поиском по документу в devtools, потом — в программе. Совпало в браузере, но не совпало в colly — значит, форма не из поддерживаемого подмножества, и её переписывают через базовые формы: иногда достаточно разбить один хитрый селектор на два правила OnHTML и связать их вложенным обходом, о котором пойдёт речь в следующем уроке. Так вы никогда не застрянете: базовое подмножество закрывает девять задач из десяти.
Скопированный из devtools селектор вида .catalog > div.row > div.col-md-3 > div.thumbnail > div.caption > h4 > a работает сегодня и умирает молча завтра: дизайнеры вставят один анонимный div — и на выходе ноль элементов без единой ошибки в логе. Точка баланса между точностью и устойчивостью выглядит так: поднимитесь от цели к ближайшему осмысленному контейнеру и напишите самую короткую пару, которая однозначно ведёт к данным — div.caption h4 a вместо семи звеньев. И всегда обрабатывайте случай «ноль совпадений»: молчаливая пустота дороже любой ошибки компиляции.
Правила устойчивого селектора#
- самая короткая комбинация, которая однозначно определяет данные, — лучше всего контейнер плюс цель
- семантические теги и осмысленные классы вместо позиционных номеров детей
- атрибуты href, src, data-* вместо хэшированных имён классов
- nth-of-type — только когда у узла нет других примет
- селектор проверен на второй и последней странице каталога, а не только на первой
- в коде есть реакция на ноль найденных элементов
Проверка: пробел или скобка
Чем селектор .quote > span отличается от .quote span?
Проверка: атрибутный префикс
Что выберет селектор a[href^="/catalogue/"] на books.toscrape.com?
Сколько узлов соберёт селектор
Прочитайте код и выберите, что он выведет:
<div class="quote">
<span class="text">"Quote text here."</span>
<span>by <small class="author">Jane Austen</small></span>
<div class="tags">
<a class="tag" href="/tag/truth/">truth</a>
</div>
</div>
// Селектор: .quote span
// Вопрос: сколько вызовов колбэка даст OnHTML(".quote span")?Восстановите селекторы книжного скрапера
Допишите правила: класс цены, путь от карточки к ссылке заголовка и фильтр ссылок пагинации по началу адреса.
Соберите скрапер с двумя правилами
Расставьте строки в порядке исполнения программы: пакеты, коллектор, правила, запуск.
Строки
Ваша программа
Нажимайте на строки слева, чтобы собрать программу
Карточки: язык селекторов
Что дальше#
Селекторы отвечают на вопрос «что искать», но не рассказывают, как colly их выполняет. Следующий урок — механика OnHTML: сколько раз вызовется колбэк, в каком порядке срабатывают правила и как связывать вложенные выборки через ForEach. Потом — способы достать значение: e.Text, e.Attr и семейство Child-методов, которые добирают данные из глубины выбранного узла. А разобранная здесь разметка цитат пригодится в каждом из них.
Песочница урока
Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.
Запустите код, чтобы увидеть вывод.
Коротко: что запомнить
- Селектор — точный запрос к DOM-дереву; colly передаёт строку в goquery, который компилирует её и находит узлы.
- Три простые формы: тег для длинных комбинаций, .class для повторяющихся данных, #id для единственного блока.
- Пробел — любой потомок, угловая скобка — прямой ребёнок; по умолчанию пишут пробел, скобку добавляют против ложных совпадений.
- Атрибутные формы [attr], [attr=value], [attr^=value], [attr$=value] фильтруют ссылки и картинки по адресам без кода.
- Псевдоклассы nth-of-type и first-of-type описывают положение среди соседей; счёт с единицы и крайнее средство по устойчивости.
- colly поддерживает базовое подмножество CSS3 через goquery; экзотику проверяют на живой странице и переписывают базовыми формами.
- Длинные селекторы-паровозы умирают молча при чужом релизе: самая короткая однозначная комбинация плюс реакция на ноль совпадений.
Домашнее задание
На books.toscrape.com соберите три правила OnHTML: цены всех книг страницы, полные названия из атрибута title ссылок в h3 и ссылки пагинации из li.next. Потом сломайте свой селектор специально: замените .price_color на div.row и посмотрите, что окажется в выводе. После этого возьмите Copy selector на карточке товара и сократите его минимум вдвое без потери набора совпадений — проверяйте поиском по документу.
Личные заметкиПусто
Вопросы и ответы по уроку
Какие CSS-селекторы поддерживает colly?
Базовое подмножество CSS3 через goquery: теги, классы, идентификаторы, комбинаторы пробела и скобки, атрибутные условия и распространённые псевдоклассы вроде nth-of-type. Экзотические формы проверяют на живой странице; если не сработало — переписывают базовыми формами.
Почему селектор из devtools в colly не находит элементы?
Copy selector выдаёт абсолютный путь с номерами детей (nth-child), который считает положение блока на конкретной странице. Он ломается при любом изменении разметки. Сократите путь до повторяющейся части — контейнер плюс цель.
Скобка или пробел — что лучше?
По умолчанию пробел: он выживает при появлении новых обёрточных div. Угловую скобку берут, когда пробел ловит лишнее из глубины карточки — например, одноимённые элементы в подвале блока. Выбор всегда проверяют на второй странице каталога.
Термины курса
Понятия, на которых строится этот курс, — короткие определения в глоссарии. Наведите на термин, чтобы вспомнить, откройте карточку для подробностей и ссылок на практику.
Изучайте дальше
Следующая ступень учебного пути
Testify: тестирование Go с assert и require
Интерактивный курс Testify для Go: assert и require, табличные тесты, suite, mock-объекты, Eventually, HTTP-тесты. 25 уроков с примерами и практикой в браузере.
- Писать читаемые проверки через assert и require, а не лестницы if
- Проверять ошибки, паники и краевые случаи без ручного кода
- Сравнивать структуры, слайсы, карты и числа с плавающей точкой
- Строить табличные тесты и сьюты с хуками подготовки