Web Scraping с Golang и goQuery

Разработка /
Разработка: Web Scraping с Golang и goQuery

Web scraping это обработка HTML кода веб-страницы и выборка необходимых элементов из неё. На этом принципе построены поисковые системы, такие как Google. Они заходят на все страницы, которые найдут, и копируют их себе.

Проверьте, что у вас установлен go и настроена переменная $GOPATH.

Разбор страницы с goQuery


goQuery это почти то же самое, что jQuery, только для go. Она предоставляет удобный доступ к HTML-структуре страницы и позволяет оперировать её элементами и содержимым. Если сравнивать их функции, то здесь также, как в jQuery есть .Text() для строкового содержимого и .Attr() или .AttrOr() для значений атрибутов.

Чтобы начать работать с goQuery, наберите в консоли:

go get github.com/PuerkitoBio/goquery


Сбор ссылок на странице с golang и goQuery


Создадим тестовый проект:

# проверим, что $GOPATH настроена
echo $GOPATH
/home/jonathan/projects/go
# перейдём в папку `src`
cd $GOPATH/src
# создадим папку
mkdir tutorial-web-scraping
# перейдём в неё
cd tutorial-web-scraping

Теперь мы можем создать несколько примеров. Вообще не принято создавать несколько функций main() в одной директории, но мы сделаем для этого примера исключение — мы же новички, верно? :)

Список постов на странице блога

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

// файл: list_posts.go
package main

import (
    // импортируем стандартные библиотеки
    "fmt"
    "log"

    // импортируем сторонние библиотеки
    "github.com/PuerkitoBio/goquery"
)

func postScrape() {
    doc, err := goquery.NewDocument("http://jonathanmh.com")
    if err != nil {
        log.Fatal(err)
    }

    // используем CSS селекторы, найденные инспектором в браузере
    // для каждого используем индекс и элемент
    doc.Find("#main article .entry-title").Each(func(index int, item *goquery.Selection) {
        title := item.Text()
        linkTag := item.Find("a")
        link, _ := linkTag.Attr("href")
        fmt.Printf("Post #%d: %s - %s\n", index, title, link)
    })
}

func main() {
    postScrape()
}

Вывод этой программы будет примерно таким:

$ go run list_posts.go
Post #0: How to use SSH keys for Authentication (for beginners) - http://jonathanmh.com/how-to-use-ssh-keys-for-authentication-for-beginners/
Post #1: Using Sourcegraph Checkup with local file system storage - http://jonathanmh.com/using-sourcegraph-checkup-local-file-system-storage/
Post #2: Copenhagen Pride 2016 Photos - http://jonathanmh.com/copenhagen-pride-2016-photos/
Post #3: Searching the Google Books API with PHP [Quickstart] - http://jonathanmh.com/searching-google-books-api-php-quickstart/
Post #4: How to get a high score on Pagespeed Insights (and make your site fast) - http://jonathanmh.com/get-high-score-pagespeed-insights-make-site-fast/
Post #5: NGINX / Apache: Block Requests to PHP file (xmlrpc.php) - http://jonathanmh.com/nginx-apache-block-requests-php-file-xmlrpc-php/
Post #6: Distortion Copenhagen 2016 – Nørrebro / Wednesday - http://jonathanmh.com/distortion-copenhagen-2016-norrebro-wednesday/
Post #7: I need feminism because: Metal T-shirts - http://jonathanmh.com/need-feminism-metal-t-shirts/
Post #8: On Being Powerless - http://jonathanmh.com/on-being-powerless/
Post #9: How to get a Job in Tech - http://jonathanmh.com/get-job-tech/


Соберём все ссылки со страницы

Собрать все ссылки со страницы не намного сложнее, просто нужно использовать более широкий селектор, body a и обойти все найденные ссылки. Получить содержимое тега <а> можно с помощью linkText := linkTag.Text().

// файл: get_all_links.go
package main

import (
    "fmt"
    "log"

    "github.com/PuerkitoBio/goquery"
)

func linkScrape() {
    doc, err := goquery.NewDocument("http://jonathanmh.com")
    if err != nil {
        log.Fatal(err)
    }

    doc.Find("body a").Each(func(index int, item *goquery.Selection) {
        linkTag := item
        link, _ := linkTag.Attr("href")
        linkText := linkTag.Text()
        fmt.Printf("Link #%d: '%s' - '%s'\n", index, linkText, link)
    })
}

func main() {
    linkScrape()
}

Вывод будет примерно таким:

$ go run get_all_links.go
Link #0: 'Skip to content' - '#content'
Link #1: 'JonathanMH' - 'http://jonathanmh.com/'
Link #2: 'twitter' - 'https://twitter.com/JonathanMH_com'
Link #3: 'rss feed' - 'http://jonathanmh.com/feed/'
... (many more)
Link #172: 'Proudly powered by WordPress' - 'https://wordpress.org/'

Теперь мы знаем, как получить все ссылки со страницы, включая даже их текст!

Получаем название страницы и метаданные

// файл: metadata.go
package main

import (
    "fmt"
    "log"

    "github.com/PuerkitoBio/goquery"
)

func metaScrape() {
    doc, err := goquery.NewDocument("http://jonathanmh.com")
    if err != nil {
        log.Fatal(err)
    }

    var metaDescription string
    var pageTitle string

    pageTitle = doc.Find("title").Contents().Text()

    doc.Find("meta").Each(func(index int, item *goquery.Selection) {
        if( item.AttrOr("name","") == "description") {
            metaDescription = item.AttrOr("content", "")
        }
    })
    fmt.Printf("Page Title: '%s'\n", pageTitle)
    fmt.Printf("Meta Description: '%s'\n", metaDescription)
}

func main() {
    metaScrape()
}

И получим:

Page Title: 'JonathanMH - Just a guy, usually in Denmark, blogging about things he couldn't get to work right away and then made a blog post in case others get stuck too.'
Meta Description: 'JonathanMH Coder, Blogger, Videographer, Webguy, Just a guy, usually in Denmark, blogging about things he couldn't get to work right away and then made a blog post in case others get stuck too.'


В этом примере мы используем AtrrOr( value, fallback_value) для того, чтобы точно знать, что получим данные. Это намного проще написания дополнительных проверок содержимого.

Вы используете web scraping? Если у вас есть вопросы / дополнения по теме, напишите о них в комментариях!

По материалам: Web Scraping with Golang and goQuery
0 комментариев
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.