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