---
title: "Установка и первый запуск Chrome"
description: "chromedp установка в Go: go get, пути к Chrome на всех ОС, headless по умолчанию, первый ctx и chromedp navigate — первая программа с построчным разбором."
course: "Chromedp в Go: автоматизация Chrome без Selenium"
url: "https://golangmaster.ru/courses/chromedp/lessons/02-ustanovka-zapusk/"
published: "2026-09-19"
updated: "2026-09-21"
minutes: 18
---

# Установка и первый запуск Chrome

*Курс «Chromedp в Go: автоматизация Chrome без Selenium», урок 2 из 25. ~18 минут. Обновлено 21.09.2026. Источник: https://golangmaster.ru/courses/chromedp/lessons/02-ustanovka-zapusk/*

chromedp установка удивительно короткая: один go get — и пакет в проекте, потому что кроме библиотеки не нужен ни драйвер, ни сервер, ни cgo. Всё остальное окружение — это Chrome на машине и пара строк каркаса: chromedp navigate делает первый переход уже в третьем уроке, а здесь соберём программу целиком и поймём, откуда берётся браузер и что происходит при первом запуске.

Новая задача: мониторить страницу конкурента и снимать скриншот каждый час. Идея понятна, но руки чешутся сразу от одного вопроса из прошлого опыта с Selenium: сколько всего придётся поставить и сшить между собой — драйвер, сервер, версии, зависимости? С chromedp этот вопрос снимается на первом шаге: библиотека сама запускает браузер, а единственное требование к машине — чтобы Chrome или Chromium был установлен. В этом уроке пройдём весь путь первого запуска: добавим пакет в модуль, найдём, где chromedp ищет браузер на Windows, macOS и Linux, соберём каркас программы с таймаутом и заглянем под капот первого Run — что именно происходит между go run и появлением процесса Chrome в системе. После этого урока любая программа курса будет выглядеть знакомо: одинаковое начало, одинаковая уборка за собой.

### chromedp установка: go get и никакого chromedriver

Проект собирается как обычный Go-модуль: папка, go mod init, добавление пакета. Никаких шагов вне Go-экосистемы не существует — это главная разница с браузерными инструментами из других языков, где к проекту прилагается бинарник драйвера, который должен совпадать по версии с браузером. В chromedp chromedriver не нужен в принципе: библиотека общается с Chrome по протоколу отладки, встроенному в сам браузер, а сам chromedp — чистый Go без cgo, поэтому кросс-компиляция не страдает. chromedp установка сводится к трём командам терминала, а версии пакета и его зависимостей фиксируются в go.mod и go.sum штатным механизмом модулей. Требования к окружению минимальные: установленный Chrome или Chromium — тот же, что стоит у вас на рабочей машине, и достаточно свежий Go. Для серверов есть путь аккуратнее: Docker-образ с предустановленным браузером, чтобы версии не гуляли между машинами, — ему посвящён отдельный урок про [Docker и серверное окружение](/courses/chromedp/lessons/23-docker-server/), а пока работаем локально.

### Три команды до первой программы

```go
mkdir chromedp-hello && cd chromedp-hello
go mod init example.com/chromedp-hello
go get github.com/chromedp/chromedp

go run .
```

go mod init создаёт модуль, go get добавляет chromedp в зависимости и фиксирует версию в go.mod и go.sum. Последняя строка запускает программу из текущей папки. Отдельного шага установки драйвера нет: пакет скомпилируется сам, а браузер будет найден при первом запуске.

### Первый ctx: NewContext, ленивый запуск и defer cancel

Сердце программы — две функции, и важно понимать разницу между ними. chromedp.NewContext(context.Background()) только готовит контекст: он запоминает настройки сессии, но не запускает ни одного процесса. Проверить это легко — поставьте после NewContext вызов os.Exit и убедитесь, что в системе не появилось ни одного процесса браузера. Ленивость здесь осмысленная: пока нет действий, браузер не нужен. Процесс стартует на первом chromedp.Run — это самый дорогой вызов программы, на него уходят и старт браузера, и подключение к нему. Отсюда практическое правило: NewContext и Run разделяют Initialization и работу, а между ними удобно навешивать таймауты. Вторая функция — cancel из пары: отмена первого контекста закрывает весь браузер, поэтому defer cancel() в main — обязательная гигиена каждого примера курса. Забытый cancel не приведёт к ошибке компиляции, но оставит висеть процесс Chrome — на ноутбуке это заметно по вентилятору, а на сервере — по памяти, которая кончилась в три часа ночи. Ещё одна деталь того же семейства: контекст можно готовить заранее, и это дёшево — программа создаёт сессии про запас и запускает их по мере необходимости, потому что цена NewContext — записать настройки, а не запустить процесс. Ленивость тут не экономия ради экономии: старт браузера — самое хрупкое место программы, и группировать его в одном месте — значит видеть все точки, где он может не удастся.

### Откуда chromedp берёт Chrome: путь по умолчанию

При старте библиотека ищет браузер по списку стандартных мест, и список этот зависит от операционной системы. На macOS проверяются приложения Chromium и Google Chrome в папке Applications. На Windows — путь через PATH и папки Program Files, включая профиль пользователя в AppData. На Linux список длиннее всего: chromium, chromium-browser, google-chrome, google-chrome-stable и его бета-варианты, /usr/bin/google-chrome, /usr/local/bin/chrome, а также /snap/bin/chromium — на Ubuntu браузер часто живёт именно в snap. Первый найденный бинарник и станет рабочим. Отсюда частая путаница на Linux: у вас стоит chromium, программа ищет google-chrome — и находит, потому что поиск идёт по всему списку, а не по одному имени. Если браузер лежит в нестандартном месте — кастомная сборка, отдельная версия для тестов, — путь передают явно опцией ExecPath при создании allocator-контекста, о котором ниже. Когда поиск ни к чему не привёл, chromedp не молчит: он возвращает ошибку запуска с именем, которое не нашлось, — и это обычно первый сигнал проверить, установлен ли браузер вообще. Список путей не секретная величина, а задокументированное поведение: на него можно опираться в скриптах развёртывания, зная, где именно следует ожидать бинарник.

### Нестандартный путь и видимое окно: allocator-контекст

```go
options := append(chromedp.DefaultExecAllocatorOptions[:],
        chromedp.Flag("headless", false),        // показать окно браузера
        chromedp.ExecPath("/usr/bin/chromium"),  // свой путь к бинарнику
)

allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), options...)
defer cancel()

ctx, cancel := chromedp.NewContext(allocCtx)
defer cancel()
```

DefaultExecAllocatorOptions — набор флагов по умолчанию, где уже включён headless; мы дописываем своё поверх него. Flag передаёт Chrome любые флаги командной строки, а ExecPath задаёт путь к бинарнику, если он не в стандартном месте. NewExecAllocator создаёт контекст запуска браузера, а NewContext на его основе — рабочую сессию; обе пары cancel нужно закрыть.

> **Типичная ошибка: chromium есть, а chromedp его не видит.** На свежей машине программа падает с ошибкой запуска, хотя браузер «точно стоит». Причины из практики: установлен не Chrome, а только headless-shell; браузер поставлен snap-ом в нестандартный путь после кастомной сборки; PATH в окружении systemd не содержит пользовательские папки. Проверка простая — выполните в терминале google-chrome --version или chromium --version. Ошибка говорит сама: chromedp ищет по фиксированному списку, и путь нужно либо дать через ExecPath, либо установить браузер в стандартное место. Сломалось бы всё то же, что и всегда: CI и серверы без явного пути — с букетом таких находок.

### Headless по умолчанию и что меняет окно

chromedp стартует Chrome с набором флагов по умолчанию, и главный среди них — headless: окно не рисуется, интерфейс не существует, но движок полностью рабочий. К этому режиму добавлены тишина звука и скрытые полосы прокрутки — детали, которые не мешают автоматизации. Для повседневных задач headless — правильный режим: быстрее старт, меньше памяти, работает на сервере без дисплея. Но при отладке он прячет самое ценное — собственно страницу. Половина вопросов новичков — «почему селектор не находится» — решается за минуту, когда запускаешь программу с видимым окном и смотришь, что реально открылось: редирект на капчу, баннер согласия с куками, мобильная версия. Включается окно одной опцией Flag(«headless», false) из блока выше — базовый набор флагов при этом сохраняется. Отдельный практический нюанс: даже в headless у страницы есть размеры окна, и от них зависит вёрстка, которая отрисуется. Размер задают опцией WindowSize — это заметно на сайтах с адаптивом, где от ширины вьюпорта меняется вся сетка. Последний штрих к режимам: окно — не только отладка, но и редкие задачи со свойствами, привязанными к отображению, вроде печати страницы в PDF с точными полями; но в повседневной работе headless возвращается сразу, как только причина отладки устранена.

### Что происходит при первом Run

Первый Run — самое насыщенное место всей программы, и полезно знать его этапы. Сначала chromedp собирает аргументы командной строки из опций allocator: headless, отключённые первые-запуски, порт отладки со случайным номером — если порт не задан явно, — и готовит временный каталог профиля: по умолчанию свежая папка в системном tmp, чтобы сессии не мешали друг другу и не тащили историю; каталог сразу уезжает в браузер опцией user-data-dir. Затем стартует процесс Chrome с пустой первой страницей, и библиотека читает его вывод, пока там не появится строка с адресом отладки — на неё вешается WebSocket-подключение; если браузер молчит слишком долго, Run завершается ошибкой таймаута чтения адреса. После подключения создаётся вкладка — target, — и только теперь исполняются ваши действия. Всё это занимает доли секунды на быстрой машине, но на слабом CI растягивается заметно, поэтому первый Run и любят оборачивать запасным таймаутом. Здесь же живут две типовые ошибки. Первая — запуск как root в Linux-контейнере без песочницы Chrome: chromedp сам добавит нужный флаг, но понимать механику стоит. Вторая — context canceled посреди работы: соединение с браузером оборвалось, процесс убит или закрыт вручную, и chromedp отменил контекст, чтобы не врать про успех. Правильная реакция на неё — не глушить, а логировать и решать: ретрай с новым контекстом или сигнал о проблеме окружения. Знание этих этапов экономит часы: когда программа зависает, по месту затыка сразу видно, что сломалось — старт процесса, подключение или уже сами действия.

> **Увидеть протокол своими глазами.** Добавьте опцию chromedp.WithDebugf(log.Printf) при создании контекста — и в лог попадёт весь обмен с браузером: команды и события построчно. Это лучший способ понять, на каком шаге зависла программа: видно, что Navigate уже вернулся, а нужный узел так и не появился. Для быстрого чтения лога полезно уметь искать в нём имена методов Page.navigate и DOM.performSearch. На проде отладку выключают — лог шумный и подробный, а в CI его объём растёт вместе с числом страниц, поэтому включайте точечно и на коротких прогонах.

### Настройка запуска браузера

| Задача | Инструмент | Комментарий |
| --- | --- | --- |
| Запуск по умолчанию | chromedp.NewContext(context.Background()) | headless включён, путь к Chrome ищется по стандартным местам |
| Свой путь к бинарнику | chromedp.ExecPath(path) в NewExecAllocator | кастомная сборка, отдельная версия, нестандартный путь |
| Видимое окно при отладке | chromedp.Flag("headless", false) | дописывается к DefaultExecAllocatorOptions |
| Профиль и данные | опция UserDataDir | без неё — свежий временный каталог на каждую сессию |
| Сервер без настройки | готовый образ с браузером | отдельный урок про Docker закрывает версии и зависимости |

### Чек-лист первого запуска

- модуль создан, chromedp добавлен через go get, версия зафиксирована в go.mod
- браузер отвечает на google-chrome --version или chromium --version в терминале
- в main есть defer cancel() сразу после NewContext
- на прогон навешен таймаут через context.WithTimeout
- ошибка Run обрабатывается, а не глотается — по ней видно, что именно сломалось
- при отладке включено видимое окно или WithDebugf для лога протокола

### Проверка: момент запуска

**Вопрос:** В какой момент chromedp запускает процесс Chrome?

- Сразу при вызове chromedp.NewContext
- ✅ При первом chromedp.Run на этом контексте
- При импорте пакета chromedp
- При вызове defer cancel()

NewContext только готовит контекст — процессов ещё нет. Первое обращение к Run стартует браузер, подключается к нему по протоколу отладки и создаёт вкладку. Отмена контекста закрывает браузер.

### Проверка: браузер не найден

**Вопрос:** Run вернул ошибку запуска — Chrome не нашёлся. Какой способ починки рабочий?

- Переустановить Go и обновить модули
- ✅ Поставить браузер в стандартное место или задать путь через ExecPath
- Добавить в импорты пакет chromedriver
- Отключить headless-режим — без него найдётся

chromedp ищет бинарник по фиксированному списку стандартных путей для своей ОС. Если браузера там нет, путь передают явно через ExecPath в allocator-контексте — либо ставят браузер туда, где его ищут. Chromedriver тут ни при чём.

### Где вылезет ошибка

```go
// Сервер без браузера. Как упадёт программа?
// ctx, cancel := chromedp.NewContext(context.Background())
// defer cancel()
// time.Sleep(time.Second)
// err := chromedp.Run(ctx, chromedp.Navigate("https://example.com"))
```

**Что выведет программа?**

- NewContext вернёт ошибку: браузера нет
- ✅ Через секунду Sleep закончится, и ошибку вернёт Run при старте браузера
- Программа повиснет навсегда
- chromedp молча скачает браузер и продолжит

NewContext ничего не проверяет и ошибок не возвращает — запуск ленивый. Поиск бинарника и старт процесса происходят при первом Run, поэтому ошибка «браузер не найден» выпадет именно там, после завершения Sleep.

### Соберите allocator с видимым окном

Восстановите настройку запуска: флаг Chrome, создание allocator-контекста и рабочей сессии.

```go
options := append(chromedp.DefaultExecAllocatorOptions[:],
        chromedp.___("headless", false),
)

allocCtx, cancel := chromedp.___(context.Background(), options...)
defer cancel()

ctx, cancel := chromedp.___(allocCtx)
defer cancel()
```

Подсказка: Флаги передаёт Flag, контекст запуска создаёт NewExecAllocator, рабочая сессия — NewContext поверх него.

Правильные ответы в пропусках: `Flag`, `NewExecAllocator`, `NewContext`

### Расставьте настройку запуска

Соберите по порядку программу с кастомными опциями: от среза опций до первого действия.

Правильный порядок строк:

1. `options := append(chromedp.DefaultExecAllocatorOptions[:],`
2. `  chromedp.Flag("headless", false),`
3. `)`
4. `allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), options...)`
5. `defer cancel()`
6. `ctx, cancel := chromedp.NewContext(allocCtx)`
7. `defer cancel() // закрывает сессию`
8. `err := chromedp.Run(ctx, chromedp.Navigate("https://example.com"))`

### Карточки: установка и запуск

- **go get github.com/chromedp/chromedp** — Вся установка библиотеки. Драйверов и серверов нет: нужен только установленный Chrome или Chromium и обычный Go-модуль.
- **Ленивый запуск** — NewContext не стартует процессы — браузер запускается при первом Run. Ошибки окружения поэтому вылезают на Run, а не на создании контекста.
- **Откуда берётся Chrome** — chromedp ищет бинарник по списку стандартных путей ОС: google-chrome, chromium, /snap/bin/chromium и другие. Свой путь — через ExecPath.
- **chromedp.Flag** — Передача любого флага командной строки Chrome: headless false показывает окно, window-size задаёт размеры вьюпорта.
- **defer cancel()** — Отмена первого контекста закрывает весь браузер. Без cancel запущенные процессы остаются висеть — классическая утечка на сервере.
- **context canceled** — Ошибка, когда соединение с браузером потерялось: процесс убит или закрыт. chromedp отменяет контекст, чтобы программа не думала, что всё хорошо.

Что дальше: программа умеет запускаться, но пока просто открывает страницу. Первый настоящий chromedp navigate случится уже в третьем уроке — [навигация и ожидание загрузки](/courses/chromedp/lessons/03-navigaciya-zagruzka/) покажут, что Navigate возвращает управление раньше, чем на странице появляются данные, и научат ждать честно. Фундамент каркаса — контексты и таймауты — это чистая стандартная библиотека, и если хочется освежить её до того, как примеры станут сложнее, загляните в [основы Go](/courses/go-basics/). А карта всех двадцати пяти тем живёт на [странице курса](/courses/chromedp/). Напоминание про песочницу: этот урок — про терминал и процессы, поэтому запускать его нужно локально; интерактивы выше проверяют понимание механики, а не исполняют chromedp.

## Коротко: что запомнить

- Установка chromedp — один go get: чистый Go без cgo, драйверов и серверов; нужна только библиотека и установленный Chrome.
- NewContext готовит контекст, но не запускает процессов — браузер стартует на первом Run, и ошибки окружения вылезают именно там.
- Путь к Chrome ищется по стандартным местам ОС; нестандартный бинарник передают через ExecPath в allocator-контексте.
- Headless включён по умолчанию; Flag("headless", false) показывает окно — главный приём отладки «почему селектор не находит».
- Первый Run стартует процесс, создаёт временный профиль, подключается по WebSocket и открывает вкладку — самый дорогой вызов программы.
- defer cancel() обязателен: отмена первого контекста закрывает браузер, забытый cancel оставляет процессы висеть.
- context canceled после обрыва с браузером — штатный сигнал, что сессия умерла, а не повод глушить ошибку.
- WithDebugf выводит обмен протокола в лог — быстрый способ увидеть, на каком этапе зависла программа.

## Домашнее задание

Соберите проект из урока: модуль, go get, main с каркасом из первого запуска и переходом на example.com. Затем умышленно сломайте окружение — задайте ExecPath на несуществующий путь — и убедитесь, что ошибку вернёт Run, а не NewContext. После этого верните путь и включите видимое окно через Flag("headless", false): посмотрите на страницу глазами браузера и запишите, чем вкладка Elements отличается от исходного кода.

## Вопросы и ответы по уроку

### Нужен ли именно Chrome или подойдёт Chromium?

Подойдёт любой браузер на движке Chromium, который умеет протокол отладки: Google Chrome, Chromium, headless-shell. chromedp ищет их по стандартным путям; Edge и Brave тоже работают, но путь к ним задают явно через ExecPath.

### Нужно ли ставить Chrome сам по себе?

Да — это единственное требование chromedp к системе, и библиотека ничего не скачивает сама. На сервере браузер ставят вместе с приложением или берут готовый Docker-образ; отдельный урок курса разбирает образ с предустановленным Chrome и шрифтами. Версии библиотеки и браузера не сшиты жёстко: протокол отладки развивается совместимо, поэтому свежий Chrome со «старым» chromedp обычно работает. Но крайние комбинации иногда ломают отдельные команды — ещё один аргумент фиксировать образ с браузером на сервере, а не обновлять его вслепую.

### Почему ошибка запуска браузера вылезает не сразу?

Потому что NewContext ленивый: он только готовит контекст и ничего не проверяет. Поиск бинарника, старт процесса и подключение происходят при первом Run — там и появится ошибка. Это же делает запуск дешёвым: контекстов можно готовить сколько нужно.
