Веб-скрапинг — это процесс программного извлечения данных с веб-сайтов. Он позволяет вам собирать информацию из Интернета и использовать ее для анализа, архивирования, создания приложений, машинного обучения или любых других целей.
Несмотря на то, что существует множество языков и инструментов, которые можно использовать для парсинга веб-страниц, в этой статье мы сосредоточимся на том, как парсить данные в Go с помощью фантастической платформы Colly. Простой синтаксис Go, строгая типизация и встроенные функции параллелизма делают его отличным выбором для написания парсеров.
Мы рассмотрим практический пример использования Go и Colly для извлечения данных из ветки комментариев Hacker News. К концу этого урока вы будете знать, как:
- Установите и настройте Колли
- Навигация по веб-сайтам и переход по ссылкам
- Извлечение текста, атрибутов и HTML из элементов на странице.
- Разберитесь с пагинацией
- Вывод очищенных данных
Давайте погрузимся!
Настройка среды Go
Во-первых, убедитесь, что у вас установлена последняя версия Go (1.13+). Вы можете проверить текущую версию с помощью:
$ go version
Создайте новый каталог для вашего проекта и инициализируйте в нем модуль Go:
$ mkdir hackernews-scraper
$ cd hackernews-scraper
$ go mod init github.com/yourusername/hackernews-scraper
Затем установите пакет Colly:
$ go get -u github.com/gocolly/colly/...
Теперь создайте файл с именем main.go
– здесь мы напишем код нашего парсера.
Представляем Colly — мощный фреймворк для парсинга Go
Colly предоставляет чистый и выразительный API для написания парсеров на Go. Он обрабатывает множество низкоуровневых деталей, таких как управление одновременными запросами, обработка файлов cookie и сеансов, отслеживание перенаправлений, соблюдение robots.txt и т. д.
Основная концепция Colly — это «Коллекционер», который представляет собой очистку данных. Вы создаете коллектор, настраиваете его с помощью параметров и обратных вызовов, а затем начинаете работу, предоставляя один или несколько URL-адресов для посещения.
Когда Collector сканирует страницы, он выполняет обратные вызовы в ответ на определенные события — например, вы можете зарегистрировать обратные вызовы для запуска, когда Colly:
- Посещает новый URL-адрес
- Находит определенный элемент HTML
- Получает HTTP-ответ
- Обнаруживает ошибку
Эта управляемая событиями архитектура позволяет легко извлекать только те данные, которые вам нужны, не теряясь в логике синтаксического анализа. Давайте посмотрим на это в действии!
Извлечение данных из Hacker News с помощью Go и Colly
В качестве примера давайте напишем парсер, который извлекает комментарии верхнего уровня из публикации Hacker News и выводит их на консоль. Мы очистим этот пост: https://news.ycombinator.com/item?id=12345
Вот полный код нашего парсера:
package main
import (
"encoding/json"
"fmt"
"log"
"strings"
"github.com/gocolly/colly"
)
type comment struct {
Author string `selector:"a.hnuser"`
URL string `selector:".age a[href]" attr:"href"`
Comment string `selector:".comment"`
}
type post struct {
Title string `selector:"a.storylink"`
URL string `selector:"a.storylink" attr:"href"`
Comments []comment
}
func main() {
// Create a new collector
c := colly.NewCollector(
colly.AllowedDomains("news.ycombinator.com"),
)
p := post{}
// Extract post title and URL
c.OnHTML(".fatitem", func(e *colly.HTMLElement) {
e.Unmarshal(&p)
})
// Extract comments
c.OnHTML(".comment-tree tr.athing", func(e *colly.HTMLElement) {
comment := comment{}
e.Unmarshal(&comment)
// Remove extra spacing from comments
comment.Comment = strings.TrimSpace(comment.Comment)
// Build absolute URL from relative URL
comment.URL = e.Request.AbsoluteURL(comment.URL)
p.Comments = append(p.Comments, comment)
})
// Set max depth to 1 so we only visit the post and its comments
c.MaxDepth = 1
c.OnRequest(func(r *colly.Request) {
fmt.Println("Visiting", r.URL)
})
c.OnError(func(r *colly.Response, e error) {
log.Printf("Error scraping %s: %s", r.Request.URL, e)
})
c.OnScraped(func(r *colly.Response) {
// Dump results as JSON
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
enc.Encode(p)
})
c.Visit("https://news.ycombinator.com/item?id=12345")
}
Давайте разберемся:
Мы определяем два типа структур:
post
иcomment
, для хранения очищенных данных. Обратите внимание на теги «selector» и «attr» — они сообщают Colly, как найти эти данные в DOM.In
main
, мы создаем новый Сборщик, указывая, что ему разрешено посещать только страницы наnews.ycombinator.com
.Регистрируем двоих
OnHTML
обратные вызовы:- Первый ищет
.fatitem
элемент, который содержит заголовок и URL-адрес публикации и используетUnmarshal
извлечь их в нашуpost
структура. - Второй обратный вызов ищет
.comment-tree
элементы и извлекает автора, относительный URL-адрес и текст каждого комментария, немного очищая их и добавляя вComments
массив нашихpost
структура.
- Первый ищет
Регистрируем еще несколько обратных вызовов:
OnRequest
регистрирует каждый URL-адрес, который мы посещаемOnError
регистрирует любые ошибки, возникающие во время парсингаOnScraped
запускается после завершения всего задания очистки и выгружает очищенные данные в формате JSON.
Наконец, мы вызываем
c.Visit
с URL-адресом сообщения, которое мы хотим очистить, что запускает фактическую работу по парсингу. Colly будет переходить по ссылкам и вызывать наши обратные вызовы по мере сканирования.
Запустите код с помощью:
$ go run main.go
Вы должны увидеть вывод вроде:
Visiting https://news.ycombinator.com/item?id=12345
{
"Title": "Example HN Post",
"URL": "http://example.com",
"Comments": [
{
"Author": "user1",
"Comment": "First comment!",
"URL": "https://news.ycombinator.com/item?id=12346"
},
...
]
}
Тада! Всего с помощью нескольких десятков строк кода мы извлекли структурированные данные с веб-страницы. Конечно, вы можете сделать гораздо больше – это лишь поверхностное представление API Colly. Обязательно ознакомьтесь с документацией и примерами Colly, чтобы узнать о таких функциях, как:
- Автоматическая обработка файлов cookie и сеансов
- Параллельное парсинг с помощью горутин
- Кэширование ответов
- Настройка заголовков запросов, прокси и таймаутов
- Ограничение скорости и глубины сканирования
За пределами базового парсинга – масштабные проблемы
Хотя Colly и Go упрощают начало работы с веб-скрапингом, все становится сложнее, когда вы пытаетесь собрать большие объемы данных с крупных веб-сайтов. Вы быстро столкнетесь с такими мерами противодействия ботам, как:
- Ограничение скорости и CAPTCHA
- Баны по IP при слишком быстром или слишком большом сканировании с одного IP
- Динамические страницы, отображаемые на JavaScript, которые трудно очистить.
- Частые изменения в структуре сайта, которые нарушают работу парсеров.
Чтобы обойти это, вам придется распределить парсеры по множеству IP-адресов, автоматизировать CAPTCHA и прокси, визуализировать JS, а также постоянно отслеживать и обновлять свой код. Это выполнимо, но для правильной реализации требуется значительное время и инфраструктура.
Если вы предпочитаете сосредоточиться на фактическом использовании данных, которые вы очищаете, а не на борьбе с мерами по борьбе с ботами, попробуйте ScrapingBee. Это API, который решает всю эту головную боль за вас — просто отправьте ему URL-адрес и получите обратно структурированные данные JSON, не беспокоясь о прокси, CAPTCHA или запретах IP. Он даже будет отображать для вас страницы JavaScript. Первые 1000 вызовов API бесплатны, так что попробуйте его в своем следующем проекте по парсингу!
Заключение
Парсинг веб-страниц — невероятно полезный навык, который должен быть в вашем арсенале разработчика, а сочетание Go и Colly делает его доступным и приятным. В этой статье мы рассмотрели полный пример парсинга ветки комментариев Hacker News – надеюсь, этот реальный пример использования дал вам представление о том, что возможно, и вдохновил вас попробовать парсить самостоятельно!
Помните, всегда будьте уважительны при очистке и соблюдайте файл robots.txt веб-сайтов и условия обслуживания. А теперь иди и освободи немного данных!