Введение

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

В этом руководстве мы рассмотрим, как создать простой параллельный парсер в Go, используя горутины и каналы. Go с его упрощенной моделью параллелизма — отличный выбор для создания высокопроизводительных парсеров, способных эффективно обрабатывать несколько задач параллельно. К концу этого руководства у вас будет основа для создания более продвинутых веб-скребков и поисковых роботов в Go, использующих его мощные функции параллелизма.

Пример на ходу

В этом фрагменте кода мы создадим простой параллельный парсер на Golang, используя горутины и каналы. Парсер будет одновременно извлекать содержимое нескольких URL-адресов и возвращать коды состояния ответа.

package main

import (
 "fmt"
 "log"
 "net/http"
 "sync"
 "time"
)

// URLStatus is a struct that holds the URL and the status code
type URLStatus struct {
 URL    string
 Status int
}

// fetchURL fetches the URL and sends the status code to the channel
func fetchURL(url string, ch chan<- URLStatus, wg *sync.WaitGroup) {
 // decrement the wait group counter when the goroutine exits
 defer wg.Done()

 resp, err := http.Get(url)
 if err != nil {
  fmt.Printf("Error fetching %s: %v\n", url, err)
  ch <- URLStatus{URL: url, Status: -1}
  return
 }
 defer resp.Body.Close()

 ch <- URLStatus{URL: url, Status: resp.StatusCode}
}

func main() {
 urls := []string{
  "https://www.google.com",
  "https://www.example.com",
  "https://www.medium.com",
  "https://www.github.com",
 }

 ch := make(chan URLStatus)
 var wg sync.WaitGroup

 start := time.Now()

 for _, url := range urls {
  log.Println("Starting goroutine for", url)
  wg.Add(1)
  // start a goroutine to fetch the URL
  go fetchURL(url, ch, &wg)
 }

 // close the channel when all goroutines are done
 go func() {
  wg.Wait()
  close(ch)
 }()

 for status := range ch {
  fmt.Printf("URL: %s, Status: %d\n", status.URL, status.Status)
 }

 elapsed := time.Since(start)
 fmt.Printf("Elapsed time: %s\n", elapsed)
}

В этом примере мы определяем структуру URLStatus, которая содержит URL-адрес и соответствующий код состояния. Функция fetchURL принимает строку URL, канал для отправки объекта URLStatus и sync.WaitGroup для управления горутинами.

Внутри функции fetchURL мы отправляем HTTP-запрос GET на предоставленный URL-адрес и получаем код состояния ответа. Затем мы отправляем по каналу объект URLStatus, содержащий URL-адрес и его код состояния. Если есть ошибка, мы отправляем код состояния -1, чтобы указать на проблему.

В функции main мы создаем фрагмент URL-адресов для получения, канал для отправки URLStatus объектов и sync.WaitGroup для управления горутинами. Мы перебираем URL-адреса, вызывая fetchURL в горутине для каждого URL-адреса. Мы также добавляем Горутин, чтобы закрыть канал после завершения всех Горутин fetchURL.

Наконец, мы проходим по каналу и печатаем URL-адрес и его код состояния. Время, прошедшее до получения всех URL-адресов, печатается в конце, демонстрируя параллельный характер парсера.

Этот фрагмент кода демонстрирует, как использовать горутины, каналы и sync.WaitGroup для создания параллельного парсера в Golang. Эта основа может быть расширена для создания более совершенных парсеров и поисковых роботов.