Содержание урока · 13 разделов
- colly user agent: опция NewCollector
- Что сервер видит в скрапере
- Заголовки скрапера: что сообщает и когда менять
- Честное имя бота: когда свой UA лучше
- Referer и Accept-Language: два частых каприза
- Порядок заголовков: мелочь, которую видят
- Accept-Encoding: не трогайте руками
- TLS-отпечаток: чего colly не маскирует
- Граница: совместимость — да, обход запретов — нет
- Что дальше по курсу
- Песочница урока
- Коротко: что запомнить
- Вопросы и ответы
Сайт, который открывается в браузере без вопросов, вдруг отвечает скраперу 403 — или открывает, но вместо карточек показывает заглушку «Доступ ограничен». Чаще всего это не про IP и не про скорость: сервер посмотрел на сам запрос и не узнал в нём посетителя. HTTP-запрос — это не только адрес: с ним едут заголовки, и по ним сервер сортирует клиентов на браузеры, роботов и всё прочее. Скрапер из прошлых уроков шлёт честную подпись colly — и часть сайтов режет её без разговоров. Урок про то, как выглядят запросы изнутри: какой User-Agent шлёт colly по умолчанию, как подставить свой, зачем скраперу Accept-Language и Referer — и где проходит граница между настройкой совместимости и маскировкой.
colly user agent: опция NewCollector#
По умолчанию каждый запрос colly подписывает собственным User-Agent — строкой со ссылкой на репозиторий библиотеки. Честно, но с последствиями: парсинг User-Agent — древний приём фильтрации, и «коллекторские» подписи режут охотно и массово, от CDN до самодельных middleware. Меняется подпись одной опцией: colly.UserAgent("строка") в NewCollector задаёт заголовок всем запросам коллектора. Строку берут из реального браузера — откройте devtools, вкладку Network, скопируйте значение из заголовков любого запроса; у многих разработчиков рядом с проектом давно лежит проверенная константа. Второй путь — заголовки на каждый запрос: в OnRequest у r.Headers вызывается обычный Set из http.Header, и там же ставится всё остальное, что не хочется вшивать в конструктор. Два механизма не спорят, а дополняют: опция задаёт личность, OnRequest — манеру каждого захода.
User-Agent и заголовки на каждый запрос
GOpackage main
import (
"fmt"
"github.com/gocolly/colly/v2"
)
func main() {
c := colly.NewCollector(
colly.UserAgent("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"),
)
c.OnRequest(func(r *colly.Request) {
r.Headers.Set("Accept-Language", "ru-RU,ru;q=0.9,en;q=0.8")
r.Headers.Set("Referer", "http://books.toscrape.com/")
fmt.Println("отправляю", r.URL.String())
})
c.OnResponse(func(r *colly.Response) {
fmt.Println("статус", r.StatusCode)
})
c.Visit("http://books.toscrape.com/")
}colly.UserAgent в конструкторе закрывает подпись разом для всех запросов. В OnRequest r.Headers — стандартный http.Header: Set добавляет Accept-Language с приличными весами и Referer, имитирующий переход с главной каталога. OnRequest срабатывает на каждый запрос — включая пагинацию и повторные Visit, поэтому глобальные заголовки ставят именно здесь, а не перед каждым Visit вручную.
Что сервер видит в скрапере#
Прежде чем менять заголовки, посмотрите на них глазами сервера: httpbin.org/headers возвращает JSON со всем, что дошло до него. Сравните вывод двух прогонов — от colly по умолчанию и от настроенного скрапера — и разница станет наглядной: подпись библиотеки, отсутствие Accept-Language, пустой Referer. Сервер судит по совокупности: браузерный User-Agent при полном отсутствии других заголовков — уже подозрение; идеальный набор заголовков при странном поведении — второе; и так до полного профиля клиента. Для подавляющего большинства задач до глубокого анализа дело не доходит: достаточно правдоподобного набора заголовков и вежливого темпа, чтобы сервер отличил вас от дешёвых ботов и отдал обычную страницу.
Заголовки скрапера: что сообщает и когда менять#
| Заголовок | Что сообщает серверу | Когда менять |
|---|---|---|
| User-Agent | кто стучится: браузер, версия, платформа | почти всегда: браузерная строка или честное имя своего бота |
| Accept-Language | на каком языке ожидать контент | когда у сайта языковые версии и нужен конкретный язык |
| Referer | с какой страницы «пришли» | когда контент отдаётся только с внутренней навигации |
| Accept / Accept-Encoding | какие форматы клиент умеет читать | редко: colly и net/http ставят разумные значения |
| Cookie | чьи данные показывать | это урок про формы: сессия после логина |
Честное имя бота: когда свой UA лучше#
Контринтуитивный совет напоследок: браузерная строка — не всегда лучший выбор. Если вы скрапите источники с публичной политикой — открытые данные, каталоги для партнёров, сайты, где роботы приветствуются, — честное имя с контактом работает лучше. Во-первых, белый список: админы видят GoMasterBot/1.0 в логах, понимают, что это не дешёвый скрипт, и в случае проблем знают, кому написать, прежде чем бить блокировкой. Во-вторых, диагностика от обратной стороны: по внятному UA вам могут точнее настроить лимиты, а по анонимному браузерному — нет. Практика выглядит как строка вида имя-версии-контакт в одном заголовке. Но у подхода есть условие применимости, и оно жёсткое: он живёт только там, где владелец сайта в курсе и не против. На остальных ресурсах честность подписи выливается в мгновенный 403 — и тогда остаётся совместимость, о которой шла речь выше.
Проверка: что реально увидел сервер
GOc := colly.NewCollector(
colly.UserAgent("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36"),
)
c.OnResponse(func(r *colly.Response) {
fmt.Println(string(r.Body))
})
c.Visit("https://httpbin.org/headers")httpbin.org/headers — зеркало: сервер возвращает JSON с заголовками пришедшего запроса. Прогоните без UserAgent-опции и с ней — в поле User-Agent увидите сначала подпись colly, потом браузерную строку. Так же удобно проверять Referer и Accept-Language: сервер показывает то, что реально уехало, а не то, что вы думали поставить.
Referer и Accept-Language: два частых каприза#
Referer — заголовок с историей перехода. Некоторые сайты отдают контент только «изнутри»: прямая ссылка на карточку без Referer получает редирект на каталог или заглушку. Лечится в OnRequest: r.Headers.Set("Referer", адрес каталога) — и запрос выглядит пришедшим с внутренней страницы. Accept-Language решает другой вопрос: у мультиязычных сайтов версия контента выбирается по этому заголовку, и скрапер без него получает язык по умолчанию, который не всегда тот, что нужен. Оба заголовка честные: реальный браузер шлёт и то, и другое — вы не врёте, вы приводите запрос к общепринятому виду. Разница с cookie-сессией из урока про формы простая: cookies — личность, заголовки — манера речи; и то и другое нужно, чтобы сайт узнавал клиента.
Порядок заголовков: мелочь, которую видят#
Кроме значений, сервер видит и порядок заголовков — и это тоже почерк. Браузеры шлют набор в устоявшемся порядке, HTTP-клиенты — в своём; расхождение не фатально для большинства сайтов, но входит в профиль клиента у продвинутых фильтров. Colly порядок штатно не перетасовывает, и гнаться за этим не нужно: если источник фильтрует по порядку заголовков, вы это узнаете после того, как остальные признаки уже выправлены. Практический порядок работы с капризным сайтом такой: снять запрос браузера, воспроизвести значения в OnRequest, проверить зеркалом httpbin — и только если не помогло, идти дальше по списку: cookies из урока про формы, затем прокси из следующего урока. Чаще всего до конца списка не доходит: одна подпись и один Referer решают девять историй из десяти.
Accept-Encoding: не трогайте руками#
Особый случай — Accept-Encoding, и у него правило противоположное: не менять. Браузер шлёт список форматов сжатия — gzip, deflate, br, zstd. Транспорт Go при отсутствии этого заголовка сам добавляет gzip и сам его расжимает: в OnResponse приходит уже читаемый HTML, и селекторы работают. Если же поставить Set("Accept-Encoding", "br") руками, сервер добросовестно отдаст brotli, который стандартный транспорт не расжимает, — и в r.Body окажутся байты, на которых парсинг молча даст ноль результатов. Такие поломки коварны: код ответа 200, тело есть, ошибок нет — а данных нет. Если подозреваете сжатие, смотрите на тело в OnResponse глазами: начало внятного HTML или россыпь нечитаемых байтов. Заголовок, который вы не понимаете до конца, лучше не отправлять вовсе — это правило распространяется на весь набор, а не только на сжатие.
Проверка: точка установки заголовков
Где удобнее ставить заголовок, который должен уйти с каждым запросом коллектора?
Проверка: подпись по умолчанию
Какой User-Agent шлёт colly без настройки?
Что увидит httpbin в поле User-Agent?
Прочитайте код и выберите, что он выведет:
c := colly.NewCollector(
colly.UserAgent("Mozilla/5.0 (compatible; GoMasterBot/1.0)"),
)
c.OnResponse(func(r *colly.Response) {
fmt.Println(string(r.Body))
})
c.Visit("https://httpbin.org/headers")TLS-отпечаток: чего colly не маскирует#
Честное слово про нижний этаж. Под заголовками лежит TLS-рукопожатие, и у каждого стека оно своё: Go из коробки даёт узнаваемый отпечаток, по которому продвинутые антибот-системы отличают клиента на Go от настоящего Chrome — даже когда заголовки идеальны. Colly это не маскирует: под капотом net/http, и подменить рукопожатие опцией нельзя. Практический вывод двоякий. Если сайт отдаёт контент по заголовкам — вы уже всё решили, и TLS никого не волнует. Если же сервер классифицирует клиентов по отпечатку, colly упрётся в потолок: дальше либо честный отказ от скрапинга этого источника, либо браузерный движок — это территория урока про антибот-защиту, где про chromedp и JavaScript-рендеринг отдельный разговор.
Граница: совместимость — да, обход запретов — нет#
Спорное место урока: менять User-Agent — нормально или нет? Моё мнение — менять можно и нужно, когда речь о совместимости. Браузерные строки — де-факто стандарт, который понимают серверы, а «коллекторская» подпись отсекается даже там, где скрапинг разрешён; вы не обходите запрет, вы делаете запрос понятным. Другое дело — подставлять браузерный UA, чтобы пролезть туда, где роботам прямо сказано «нельзя»: в robots.txt, в письме от владельца, в условиях использования. На этом месте заканчивается техническая задача и начинается решение о правилах — правовых и этических, и оно принимается не в терминале. Урок про защиту от скрапинга разложит эту границу по полочкам: robots.txt, капчи, что colly не умеет и что делают вместо неё. Мой рабочий ориентир: настраивай заголовки так, как настроил бы их для публичного API, о котором можно сказать владельцу вслух.
Прежде чем угадывать заголовки, откройте Network в devtools и скопируйте запрос, который отправляет браузер: User-Agent, Accept-Language, Referer. Цель — не копия один в один, а правдоподобный минимум. И проверяйте себя зеркалом httpbin.org/headers: сервер видит то, что реально уехало.
Восстановите OnRequest
Поле запроса с заголовками и язык контента.
Соберите скрапер с настроенной подписью
От опции User-Agent до печати статуса.
Строки
Ваша программа
Нажимайте на строки слева, чтобы собрать программу
Карточки: заголовки и подписи
Что дальше по курсу#
Заголовки — половина внешности скрапера; вторая половина — адрес, с которого сервер видит запросы: урок про прокси добавит пул адресов и ротацию. А если заголовки настроены, а сайт всё равно неприветлив — это уже не настройка, а защита: честные пределы colly и что делают вместо неё.
Песочница урока
Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.
Запустите код, чтобы увидеть вывод.
Коротко: что запомнить
- colly user agent задаётся опцией colly.UserAgent в NewCollector; по умолчанию уезжает подпись библиотеки.
- Заголовки на каждый запрос ставятся в OnRequest через r.Headers.Set — стандартный http.Header.
- Referer и Accept-Language — два частых каприза сайтов: переход «изнутри» и языковая версия контента.
- Проверка настроек — зеркало httpbin.org/headers: сервер возвращает то, что реально дошло.
- TLS-отпечаток colly не маскирует: продвинутые антиботы узнают Go-клиента по рукопожатию.
- Браерный User-Agent — настройка совместимости; маскировка ради обхода явных запретов — уже не техническая задача.
Домашнее задание
Прогоните скрапер с httpbin.org/headers дважды: без настройки и с colly.UserAgent плюс Accept-Language и Referer в OnRequest — сравните JSON между прогонами. Затем возьмите любой сайт, который вы скрапите, откройте в браузере devtools и составьте свой набор заголовков по образцу настоящего запроса. Отдельный шаг: поставьте себе честный ботовский UA вида GoMasterBot/1.0 и запишите, для каких источников такой вариант был бы правильнее браузерного.
Личные заметкиПусто
Вопросы и ответы по уроку
Достаточно ли сменить User-Agent, чтобы скрапер выглядел как браузер?
Для большинства сайтов — да: браузерный UA плюс разумные Accept-Language и Referer снимают простые фильтры. Но продвинутые антиботы смотрят шире: порядок заголовков, поведение запросов и TLS-отпечаток, который colly не маскирует.
Почему colly не подменяет TLS-отпечаток?
Colly построена на net/http: рукопожатие выполняет стандартный TLS-стек Go, и опциями библиотеки это не меняется. Если источник классифицирует клиентов по отпечатку, нужны браузерные движки вроде chromedp — об этом отдельный урок курса.
Можно ли слать браузерный User-Agent всегда?
Можно, и чаще всего это разумная настройка совместимости: многие сайты режут небраузерные UA независимо от правил. Но там, где роботам прямо запрещено, подмена подписи уже не техническая настройка, а обход запрета — такие решения принимаются вне кода.
Термины урока
Ключевые понятия из этого урока — короткие определения в глоссарии. Наведите на термин, чтобы вспомнить, откройте карточку для подробностей и ссылки на практику.
Изучайте дальше
- Прокси в Colly: ротация и пул адресов
- Ошибки и повторы: OnError и таймауты
- Кэш и офлайн-режим: не дёргаем сайт дважды
- URL-фильтры: AllowedDomains, URLFilters, Depth
- Формы и вход: POST, cookies, сессии
- Пагинация: обходим все страницы каталога
- Параллельный скрапинг: Async и WaitGroup
- LimitRule: задержки и параллелизм
- Все курсы по Go
Следующая ступень учебного пути
Testify: тестирование Go с assert и require
Интерактивный курс Testify для Go: assert и require, табличные тесты, suite, mock-объекты, Eventually, HTTP-тесты. 25 уроков с примерами и практикой в браузере.
- Писать читаемые проверки через assert и require, а не лестницы if
- Проверять ошибки, паники и краевые случаи без ручного кода
- Сравнивать структуры, слайсы, карты и числа с плавающей точкой
- Строить табличные тесты и сьюты с хуками подготовки