CoderCastrov logo
CoderCastrov
Парсер

Парсинг сайтов с GO

Парсинг сайтов с GO
просмотров
4 мин чтение
#Парсер

Обзор библиотеки colly

Введение

Colly — это инструмент для парсинга сайтов на языке GO. С ним сайты парсятся довольно быстро, по крайней мере, по моим замерам на одном и том же сайте, он отрабатывал чуть больше, чем в 2 раза быстрее при синхронном парсинге и почти в 10 раз быстрее при асинхронном, чем мой предыдущий парсер (на python + requests + bs4). Важная ремарка: сравнение скорости я проводил только одно, так что как истину воспринимать это не стоит.

В Colly есть особенность — запросы к сайтам и извлечение из них данных происходит неразрывно. Что это значит? Например, при работе в своем прошлом стеке сначала я делал запрос через requests, а результат отдавал в bs4 и извлекал нужные мне данные. Здесь же мне необходимо сначала путем создания колбеков указать, какие данные я буду извлекать, и только потом делать запрос на сайт.


Привет, мир

Пожалуй, сразу покажу, как выглядит работа с colly. Вот пример кода, который будет извлекать заголовок со страницы с документацией к данной библиотеке.

package main

import (
    "github.com/gocolly/colly"
)

const CollyDocsLink = "https://godoc.org/github.com/gocolly/colly"

func main() {
    collector := colly.NewCollector()
    collector.OnHTML("h2#pkg-overview", func(e *colly.HTMLElement) {
        println(e.Text)
    })
    collector.Visit(CollyDocsLink)
}

Результатом данного кода должна стать строка “package colly”, код вы можете копировать и проверять, что все работает, главное, нужно не забыть установить саму библиотеку с помощью команды:

go get -u github.com/gocolly/colly/

Collector

Collector - это сущность, с которой придется работать больше всего. Она предоставляет интерфейс для совершения запросов и извлечения данных. Также через неё можно конфигурировать сам парсер. Почитать более подробно можно здесь: https://godoc.org/github.com/gocolly/colly#Collector

Выгрузка данных

Как упоминалось выше, чтобы работать с полученной страницей, необходимо задать коллбэки. Задаются они так же внутри этой структуры. Коллбэки можно задавать так, что они будут вызываться в разных ситуациях, для этого есть следующие методы:

  • *func (c Collector) OnError(f ErrorCallback) - принимает функцию, которая будет вызвана в случае ошибки во время совершения запроса.
  • *func (c Collector) OnHTML(goquerySelector string, f HTMLCallback) - принимает селектор и функцию, которая будет вызываться всякий раз, когда на загруженной странице будет найден подходящий под селектор элемент.
  • *func (c Collector) OnRequest(f RequestCallback) - принимает функцию, которая будет вызываться после каждого запроса.
  • *func (c Collector) OnResponse(f ResponseCallback) - принимает функцию, которая будет вызвана после каждого ответа.
  • *func (c Collector) OnResponseHeaders(f ResponseHeadersCallback) - принимает функцию, которая будет вызываться, когда статус и хэдер уже получены, но тело ещё не прочитано.
  • *func (c Collector) OnScraped(f ScrapedCallback) - принимает функцию, которая будет вызвана после OnHTML, как часть парсинга.
  • *func (c Collector) OnXML(xpathQuery string, f XMLCallback) - принимает селектор и функцию, которая будет вызвана каждый раз, когда в загруженном xml-документе будет найден подходящий под селектор элемент.

Совершение запросов

Для совершения запросов в Collector есть 3 основных метода:

  • *func (c Collector) Post(URL string, requestData map[string]string) error — делает Post-запрос.
  • *func (c Collector) Visit(URL string) error — делает GET-запрос.
  • **func (c Collector) Request(method, URL string, requestData io.Reader, ctx Context, hdr http.Header) error — делает кастомный http-запрос с возможностью указать дополнительные параметры.

Валидные значения для method:

  • GET
  • HEAD
  • POST
  • PUT
  • PATCH
  • DELETE
  • OPTIONS

Работа с данными

Коллбэк, который передается в OnHTML, принимает в себя параметр *colly.HTMLElement, который, в свою очередь, хранит в себе элемент, подходящий под указанный селектор. Для работы с этим элементом данная структура предоставляет набор методов и свойств. Среди этих свойств, в большинстве случаев будет достаточно только Text. С методами уже немного интереснее, в распоряжении есть следующие:

  • *func (h HTMLElement) Attr(k string) string — возвращает html атрибут из HTMLelement или пустую строку, если атрибут не будет найден.
  • **func (h *HTMLElement) ChildAttr(goquerySelector, attrName string) string **— возвращает содержимое html атрибута, первого найденного элемента.
  • *func (h HTMLElement) ChildAttrs(goquerySelector, attrName string) []string — то же самое, что и ChildAttr, но для всех элементов.
  • **func (h HTMLElement) ForEach(goquerySelector string, callback func(int, HTMLElement)) — перебирает все найденные элементы и вызывает коллбэк, передавая туда перебираемый элемент.

Причем последний метод — самый интересный, так как, если нужно асинхронно распарсить множество сложных страниц, необходим интерфейс для удобной работы с вложенными элементами, как раз, что данный метод и предоставляет.

Режимы работы

По умолчанию Collector работает синхронно. Это значит, что когда происходит запрос управление не возвращается, пока обработка запроса не закончится. В голову может прийти мысль, что, раз уж на GO легко работать с параллельной работой, то было бы неплохо запихнуть совершение всех запросов в горутины, но так делать не надо.

Для удобной асинхронной работы, можно просто переключить флаг Async в True внутри Collector. Также можно сразу задать ему нужное значение при создании коллектора colly.NewCollector(colly.Async(true)). При асинхронной работе Collector будет создавать горутины под капотом и, если нужно подождать, пока все они отработают, то для этого есть метод Collector.Wait(), который дожидается конца работы всех горутин.