Содержание урока · 10 разделов
Каталог прочитан, селекторы настроены — и тут задача вырастает: найти товар по названию. Для этого нужно напечатать запрос в строку поиска, нажать кнопку и дождаться результатов. Это уже не парсинг, а управление: программа ведёт себя как пользователь, и сайт отвечает ей как пользователю. Именно на этом шаге у новичков ломается интуиция, потому что у управления страницей есть два соблазнительных, но неправильных пути — отредактировать DOM напрямую или понадеяться, что «клик есть клик». Оба пути работают до первого сайта с валидацией, живыми обработчиками и фреймворком, который хранит состояние отдельно от разметки. В этом уроке — механика честного управления: как chromedp click отправляет настоящие события мыши, как chromedp sendkeys синтезирует клавиатуру, когда SetValue уместнее обоих и что происходит с ожиданиями после нажатия кнопки, если за ней прячется переход.
chromedp click: настоящие события мыши#
chromedp.Click работает не как вызов JavaScript-метода click(), а как рука пользователя: браузеру отправляются события мыши — нажатие и отпускание кнопки — по координатам найденного узла. Разница не академическая. Настоящие события мыши всплывают по дереву, будят обработчики наведения, активируют фокус и запускают всю цепочку реакций, которую сайт повесил на кнопку; программный click() через JS часто срабатывает только на самом узле и минует часть этой цепочки. Чтобы координаты вообще существовали, Click внутри себя дожидается видимости узла — условие NodeVisible добавляется к действию по умолчанию, — а невидимый элемент браузер при необходимости прокручивает в поле зрения: перед кликом выполняется ScrollIntoViewIfNeeded, поэтому элемент, оказавшийся ниже границы экрана, дотянется до видимой области сам. Из механики следуют два практических правила. Первое: кликать можно только видимое — если элемент спрятан под оверлеем загрузки или в неоткрытой вкладке, клик не дойдёт. Второе: клик идёт по первому узлу результата — селектор должен однозначно указывать на кнопку, а не на коллекцию из десяти подходящих элементов. Для двойных кликов и других жестов есть отдельные действия, о них ниже.
Поиск: напечатать, нажать, дождаться
GOerr := chromedp.Run(ctx,
chromedp.SendKeys("#search", "меховые тапочки", chromedp.NodeVisible),
chromedp.Click("button[type=submit]", chromedp.NodeVisible),
chromedp.WaitVisible("#results"),
)SendKeys дожидается видимого поля, фокусирует его и печатает запрос посимвольно; Click ждёт кнопку и шлёт события мыши по её координатам; WaitVisible дожидается блока результатов. Опция NodeVisible здесь показана явно для наглядности, хотя и Click, и SendKeys добавляют её по умолчанию — с невидимым узлом оба отказываются работать. Программа нигде не спит: на каждом шаге она ждёт факт, а не время.
chromedp sendkeys: клавиатура по буквам#
chromedp.SendKeys синтезирует клавиатуру: для каждого символа строки браузеру уходят события нажатия, ввода символа и отпускания клавиши — тот же поток, что идёт от настоящей клавиатуры. Перед вводом chromedp фокусирует поле. Строка передаётся целиком, включая управляющие символы: Enter, стрелки и забой едут тем же посимвольным конвейером, отдельное действие для них не нужно; событие ввода символа синтезируется только для печатных знаков, остальные уходят как чистые нажатия клавиш. Из этой механики следует главное свойство SendKeys, о котором ломаются половины скриптов: действие не очищает поле. Печать идёт в текущее значение — как у человека, который добавляет слова в уже набранный текст. Если в поле что-то было, вы получите склейку. Отсюда же и достоинство: для сайтов, которые валидируют каждый символ, показывают подсказки по мере ввода или включают кнопку только после «настоящего» набора, SendKeys — единственный способ пройти их корректно. Особый случай — загрузка файлов: если селектор указывает на input типа file, chromedp не печатает путь, а заполняет поле загрузки по-настоящему, через отдельный механизм браузера. Про формы целиком — валидации, дропдауны, чекбоксы — будет отдельный урок про формы, здесь фундамент.
SetValue: значение без клавиатуры#
Второй способ положить значение в поле — chromedp.SetValue: он ставит свойство value напрямую через JavaScript и затем возбуждает события input и change, чтобы фреймворки заметили изменение. Это быстрее посимвольного набора и гарантированно чистит поле: значение присваивается, а не допечатывается. Для десятков полей в формах экономия ощутимая. Но у скорости есть граница, и важно понимать, где она проходит: SetValue возбуждает события изменения, но не события клавиатуры — keydown и keyup не происходят, потому что клавиатуры не было. Для подавляющего большинства полей этого хватает: их состояние живёт на событиях input или change, и фреймворк видит значение — React и Vue слушают именно input, поэтому их состояние обновляется, и SetValue остаётся полноценным вводом для современных витрин, а не грязным хаком. Но если сайт повесил логику именно на нажатия клавиш — маски ввода, фильтрация символов, подсчёт по keyup —SetValue обойдёт эту логику стороной, и поле будет заполнено, а сайт о нём не узнает. Правило выбора звучит так: SetValue — когда нужно просто положить значение и сайт слушает изменения; SendKeys — когда важно само поведение набора. Спорные случаи решаются проверкой результата через чтение значения обратно.
Классическая авария: парсер ходит по карточкам в цикле и печатает запрос в одно и то же поле поиска. SendKeys не чистит поле, и на второй итерации в строке лежит «телевизортелевизор» — поиск молча возвращает ноль результатов, программа честно пишет «товаров нет». Сломалась переиспользуемая сессия: значение из прошлого прогона никто не стёр. Лечение — Clear перед SendKeys или SetValue, который заменяет значение целиком. Спорное мнение с условием: SetValue быстрее и надёжнее для циклов, но если сайт валидирует по клавиатуре — остаётся пара Clear плюс SendKeys, и это правильная цена за честное поведение.
Clear перед SendKeys: цикл без склеек
GO// сайт валидирует по клавиатуре: SetValue обойдёт проверку,
// поэтому чистим и печатаем по-настоящему
err := chromedp.Run(ctx,
chromedp.Clear("#search", chromedp.ByID),
chromedp.SendKeys("#search", "меховые тапочки", chromedp.ByID),
chromedp.Click("button[type=submit]"),
)Clear обнуляет значение input или textarea, затем SendKeys печатает запрос в чистое поле, и только после этого идёт клик. Порядок важен: сначала уборка, потом набор. Clear работает через DOM и клавиатурных событий не шлёт, поэтому посимвольную валидацию не задевает — сайт проверяет значение, а не способ его очистки.
Четыре инструмента управления полями#
| Действие | Механика | Когда брать |
|---|---|---|
| chromedp.Click | события мыши по координатам узла, ждёт видимости | кнопки, ссылки, чекбоксы, вкладки — всё, что нажимается |
| chromedp.SendKeys | события клавиатуры посимвольно, поле фокусируется | валидация по набору, подсказки, Enter в поле поиска |
| chromedp.SetValue | присваивание value через JS плюс события input и change | циклы и большие формы: быстро, чистит поле |
| chromedp.Clear | обнуление значения input или textarea | перед SendKeys в переиспользуемых полях |
После клика: навигация и гонки#
Кнопка редко бывает концом сценария: за ней открывается страница результатов, вкладка, модальное окно. И здесь управление встречается с материалом прошлого урока — ожиданиями, потому что после Click начинается самая интересная гонка. События клика ушли, сайт начал реагировать: где-то поехал переход, где-то подтянулись результаты, и всё это занимает неопределённое время. Если следующее действие — чтение результатов — запустить сразу, оно попадёт в старую страницу и упадёт или прочитает мусор. Хорошая новость: chromedp сам закрывает большую часть дыры — Query-действия переспрашивают документ, и WaitVisible на целевом узле дожидается его появления, пусть даже после перехода. Остаток дисциплины — за вами: цель ожидания должна быть узлом новой страницы, а не чем-то, что совпадает и там, и там; заголовок старой страницы виден и до клика, ожидание пройдёт мгновенно и не докажет ничего. Поэтому в цепочках ждут уникальный для результата элемент: блок результатов, кнопку выхода из профиля, номер страницы. Отдельный случай — кнопка, которая отправляет форму синхронно: страница перезагружается целиком, и все ожидания начинаются заново, как после Navigate, — механика та же, поэтому и приём не меняется. Тонкие случаи, когда узлов два и оба «правильные», — территория умных ожиданий.
Двойной клик, Submit и другие жесты#
Одиночный клик — база, но не вся геометрия действий. DoubleClick посылает двойное нажатие той же механикой и нужен для интерфейсов, где одиночный клик выделения, а двойной открывает: файловые списки, таблицы с редактированием. chromedp.Submit отправляет родительскую форму найденного элемента — это замена нажатия Enter в поле: полезно, когда кнопка отправки не имеет устойчивого селектора, а форма одна. Для загрузки файлов существует SetUploadFiles — он принимает список путей и заполняет поле input типа file напрямую; SendKeys на таком поле тоже сработает, но явное действие читается лучше. Остальная геометрия — наведение, зажатие кнопки, перетаскивание — уже территория низкоуровневых событий мыши с координатами, и до неё дотягиваются в редких задачах; в курсе она не понадобится вплоть до итогового проекта. Все жесты, как и одиночный клик, применяются к первому узлу результата и добавляют NodeVisible по умолчанию: правила одной механики действуют без исключений, и селектор по-прежнему половина дела. Если сценарий разросся до десятков жестов, честный вопрос — не «какой ещё вызов добавить», а «не проще ли найти API», о котором говорили в первом уроке.
После SetValue и SendKeys не поленитесь прочитать значение обратно — chromedp.Value кладёт текущее value поля в строку. Это превращает ввод в проверяемую операцию: значение совпало — идём дальше, нет — программа узнаёт об этом сразу, а не после десяти минут пустого обхода. Тот же принцип применяется и к кликам: после нажатия дождитесь узла, который существует только в новом состоянии. Пара чтение-проверка дешевле часа отладки.
Порядок работы с формой#
- дождаться видимости формы или первого поля — WaitVisible по селектору контейнера
- для каждого поля: SetValue с заменой значения либо Clear плюс SendKeys
- кликать по кнопке с однозначным селектором или отправлять форму через Submit
- после отправки дождаться узла нового состояния — результатов, профиля, сообщения об ошибке
- прочитать значение обратно через Value, если важна гарантия, что сайт увидел ввод
- ошибки валидации на странице проверять явно — они тоже узлы DOM
Оговорка про песочницу, последняя в этом блоке уроков: управление страницей — это процессы и сеть, поэтому практиковаться нужно локально, с установленным Go и Chrome; интерактивы сайта проверяют механику и порядок действий, а тематическая программа на чистом Go в песочнице показывает поиск узлов без браузера. Кстати, для статических сайтов без скриптов всё то же самое — формы и POST-запросы — умеет делать Colly напрямую: смежный курс разбирает этот путь без браузера, и когда он сработает, он дешевле. А сессии после логина, куки и заголовки — тема тринадцатого урока.
Проверка: механика клика
Что делает chromedp.Click перед отправкой клика?
Проверка: что увидит сайт
Сайт валидирует поле на событии keydown. Какое действие пройдёт валидацию?
Что окажется в поле
Прочитайте код и выберите, что он выведет:
// <input id="q" value="страница">
// err := chromedp.Run(ctx, chromedp.SendKeys("#q", "поиск", chromedp.ByID))
// var v string
// err = chromedp.Value("#q", &v, chromedp.ByID)Соберите вход по логину
Восстановите действия: печать логина и пароля, нажатие кнопки, ожидание профиля.
Расставьте сценарий поиска
Соберите по порядку программу: контекст, ввод запроса, клик и ожидание результатов.
Строки
Ваша программа
Нажимайте на строки слева, чтобы собрать программу
Карточки: управление страницей
Что дальше: управление и извлечение — две половины браузерного парсера. В шестом уроке — извлечение текста и атрибутов: Text, InnerHTML и атрибуты узлов, из которых складываются данные. Девятый урок про умные ожидания закроет оставшиеся дыры между кликом и результатом. А когда база соберётся, двенадцатый урок доведёт формы до конца — заполнение и отправка со всеми сложными полями, от чекбоксов до загрузки файлов.
Песочница урока
Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.
Запустите код, чтобы увидеть вывод.
Коротко: что запомнить
- Click шлёт настоящие события мыши по координатам узла: ждёт видимости, прокручивает элемент, будит всю цепочку обработчиков.
- SendKeys синтезирует клавиатуру посимвольно и не очищает поле — переиспользуемые поля требуют Clear или SetValue.
- SetValue ставит value через JS и возбуждает input с change: быстро и чисто, но события клавиатуры не происходят.
- Сайты, валидирующие по keydown и keyup, проходят только SendKeys; поля, слушающие input, довольствуются SetValue.
- Value читает значение обратно — ввод превращается в проверяемую операцию, а не в надежду.
- После клика ждут узел нового состояния, уникальный для результата: заголовок старой страницы ожидание не докажет.
- Клик идёт по первому узлу результата и требует видимости: селектор — половина успеха, как и в чтении данных.
- DoubleClick, Submit и SetUploadFiles закрывают редкие жесты той же механикой: первый узел результата и требование видимости.
Домашнее задание
Возьмите селекторы из домашнего задания четвёртого урока и соберите сценарий: ввод запроса в поиск каталога, клик по кнопке, ожидание первого результата и чтение его названия. Затем реализуйте тот же ввод двумя способами — SendKeys и SetValue — и сравните поведение на сайте: где появились подсказки, где сработала валидация, что показало обратное чтение через Value. Повторите сценарий в цикле на три запроса и убедитесь, что поле чистится — например, парой Clear плюс SendKeys.
Личные заметкиПусто
Вопросы и ответы по уроку
Почему Click не работает по кнопке, которая видна в браузере?
Частые причины: над кнопкой стоит оверлей — спиннер или баннер куков перехватывает события; элемент видимый, но селектор указывает на другой узел с тем же классом; кнопка активируется только после заполнения соседних полей. Проверка — дождаться исчезновения оверлея через WaitNotPresent и убедиться, что селектор в консоли находит именно кнопку. Если ничего не помогает, включите видимое окно: глазами сразу видно, что именно перехватывает клик.
SendKeys или SetValue — что выбрать для форм?
По умолчанию SetValue: быстрее, чистит поле, возбуждает input и change, которых хватает большинству фреймворков. SendKeys нужен там, где важен процесс набора: маски, посимвольная валидация, подсказки, кнопка, активирующаяся только от клавиатуры. Спорное поле решается обратным чтением значения и одним прогоном.
Как нажать Enter в поле поиска?
Передайте его символом в строке SendKeys — управляющий символ пойдёт как нажатие клавиши. Альтернатива — chromedp.Submit на элементе формы: он отправляет родительскую форму напрямую. После любого из вариантов дожидайтесь узла результатов, как после обычного клика. Для полей с автодополнением Enter иногда выбирает подсказку вместо отправки — проверьте на своём сайте, что именно отправляет форму.
Термины урока
Ключевые понятия из этого урока — короткие определения в глоссарии. Наведите на термин, чтобы вспомнить, откройте карточку для подробностей и ссылки на практику.
Изучайте дальше
Следующая ступень учебного пути
Testcontainers в Go: интеграционные тесты с Docker
Курс testcontainers-go: Postgres, Redis, Kafka и Mongo в тестах через Docker, wait-стратегии, сети, параллельность, CI и итоговый стенд. 25 уроков.
- Поднимать реальные зависимости в Docker из кода теста
- Использовать модули Postgres, Redis, MongoDB, Kafka и RabbitMQ
- Ждать готовность сервиса wait-стратегиями вместо sleep
- Собирать стенды из нескольких контейнеров и своего образа