ホームページ  >  記事  >  バックエンド開発  >  golang クローラーの基本コンポーネントと書き方

golang クローラーの基本コンポーネントと書き方

PHPz
PHPzオリジナル
2023-04-25 16:19:25612ブラウズ

インターネットの普及と情報化の加速に伴い、インターネット上に保存されるデータがますます多くなっているため、Web クローラーは多くの人にとって不可欠なツールとなっています。中でも、golang クローラーは、そのシンプルさ、効率性、スケーラビリティにより、多くのプログラマーにとって優先されるクローラー記述言語となっています。

この記事では、golang クローラーの基本コンポーネントと書き方を紹介します。

1. golang クローラーの基本コンポーネント

  1. URL マネージャー (UrlManager)

URL マネージャーは主に、次の処理が必要な URL キューの管理を担当します。クロールされるだけでなく、重複排除などの関連操作も実行できます。これには主に次の機能が含まれます:

  • Add URL: クロールする URL をキューに追加します;
  • Get URL: URL キューからクロールする URL を取得します;
  • ストレージ URL: クロールされた URL を保存します;
  • 重複排除: 同じ URL の繰り返しのクロールを防ぎます。
  1. Web ページ ダウンローダー (ダウンローダー)

Web ページ ダウンローダーは主に、URL に対応する Web ページをローカルにダウンロードする役割を果たします。 HTTP、HTTPS、FTP などの URL のさまざまな特性に応じて、さまざまなダウンロード方法を使用できます。 golang では、net/http などのサードパーティ ライブラリを使用して Web ページをダウンロードできます。

  1. Web ページ パーサー (パーサー)

Web ページ パーサーは主に、ダウンロードされた Web ページを解析し、必要なデータを取得して保存する役割を果たします。 golang では、正規表現、html5 パーサー、goquery、その他の方法を使用して Web ページを解析できます。

  1. Storage (ストレージ)

Storage は主に解析されたデータを保存する役割を担っており、一般にデータベース ストレージとローカル ファイル ストレージの 2 つの方法があります。 GORM、orm などのサードパーティ ライブラリを golang でデータ ストレージに使用できます。

2. golang クローラーの書き方

  1. URL マネージャーの作成

URL マネージャーは主に、クロール/クロールされるリソースを管理するために使用されます。 URL は、URL の追加、URL の取得、URL が存在するかどうかの判断などの操作を提供します。

type UrlManager struct {
    Urls map[string]bool
}
// 新建URL管理器
func NewUrlManager() *UrlManager {
    return &UrlManager{Urls: make(map[string]bool)}
}
// 添加URL到管理器队列
func (um *UrlManager) AddUrl(url string) bool {
    if um.Urls[url] {
        // URL已经存在
        return false
    }
    um.Urls[url] = true
    return true
}
// 添加URL列表到管理器队列
func (um *UrlManager) AddUrls(urls []string) bool {
    added := false
    for _, url := range urls {
        if um.AddUrl(url) {
            added = true
        }
    }
    return added
}
// 判断URL是否存在
func (um *UrlManager) HasUrl(url string) bool {
    return um.Urls[url]
}
// 获取待爬取的URL
func (um *UrlManager) GetUrl() string {
    for url := range um.Urls {
        delete(um.Urls, url)
        return url
    }
    return ""
}
// 获取URL数量
func (um *UrlManager) UrlCount() int {
    return len(um.Urls)
}
  1. Web ページ ダウンローダーの作成

Web ページ ダウンローダーは、主に、指定された URL に対応する Web ページ コンテンツをダウンロードして返すために使用されます。

type Downloader struct {
    client *http.Client
}
// 新建网页下载器
func NewDownloader() *Downloader {
    return &Downloader{client: &http.Client{}}
}
// 网页下载
func (d *Downloader) Download(url string) ([]byte, error) {
    req, err := http.NewRequest("GET", url, nil)
    req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36")
    resp, err := d.client.Do(req)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    // 读取响应正文内容
    contents, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return nil, err
    }
    return contents, nil
}
  1. Web ページ パーサーの作成

Web ページ パーサーは主に、ダウンロードされた Web ページ コンテンツを解析し、必要なデータを抽出するために使用されます。以下は、例として goquery を使用したパーサーの例です:

type Parser struct{}
// 新建网页解析器
func NewParser() *Parser {
    return &Parser{}
}
// 网页解析
func (parser *Parser) Parse(content []byte) []string {
    doc, err := goquery.NewDocumentFromReader(bytes.NewReader(content))
    if err != nil {
        log.Fatal(err)
    }
    var urls []string
    doc.Find("a").Each(func(i int, s *goquery.Selection) {
        href, exists := s.Attr("href")
        if exists && !strings.HasPrefix(href, "javascript") && len(href) > 1 {
            // 绝对路径和相对路径都考虑
            u, err := url.Parse(href)
            if err != nil {
                return
            }
            if u.IsAbs() {
                urls = append(urls, href)
                return
            }
            // 补全相对路径,例如:./abc --> http://example.com/abc
            base, _ := url.Parse(contentUrl)
            urls = append(urls, base.ResolveReference(u).String())
        }
    })
    return urls
}
  1. ストレージの作成

ストレージは主に、解析されたデータをローカルまたはデータベースに保存するために使用されます。例として、MySQL データベースを使用します。

type Storage struct {
    db *gorm.DB
}
//新建数据存储器
func NewStorage() *Storage{
    db, _ := gorm.Open("mysql", "root:password@tcp(localhost:3306)/mydb?charset=utf8&parseTime=True&loc=Local")
    return &Storage{db:db}
}
// 保存数据到数据库
func (storage *Storage) SaveData(data []string) {
    for _, item := range data {
        storage.db.Create(&MyModel{Name: item})
    }
}
  1. クローラ コントローラ

クローラ コントローラは、主にクローラのスケジューリングおよび調整機能を実装します。主なプロセスは次のとおりです:

  • URL マネージャー、Web ページ ダウンローダー、Web ページ パーサー、およびストレージを初期化します;
  • クロールする URL を URL マネージャー キューに追加します;
  • クロールする URL を取得するためのループ;
  • URL がクロールされたかどうかを判断し、クロールされた場合は URL をスキップします;
  • 対応する URL Web ページをダウンロードします。
  • Web ページを解析してデータを取得します;
  • データをデータベースに保存します;
  • クロールされた URL のリストに URL を追加します。
func Run() {
    // 初始化URL管理器、网页下载器、网页解析器、存储器
    urlManager := NewUrlManager()
    downLoader := NewDownloader()
    parser := NewParser()
    storage := NewStorage()
    // 添加待爬取的URL
    urlManager.AddUrl("http://example.com")
    // 爬虫运行
    for urlManager.UrlCount() > 0 {
        // 获取待爬取的URL
        url := urlManager.GetUrl()
        // 判断URL是否已爬取过
        if downLoader.IsCrawled(url) {
            continue
        }
        // 下载网页
        contents, err := downLoader.Download(url)
        if err != nil {
            continue
        }
        // 解析网页
        urls := parser.Parse(contents)
        // 存储数据
        storage.SaveData(urls)
        // 将URL添加到已爬取过的URL列表
        downLoader.AddCrawled(url)
        // 将解析出来的URL添加到URL队列中
        urlManager.AddUrls(urls)
    }
}
  1. 完全なコード
package main
import (
    "bytes"
    "github.com/PuerkitoBio/goquery"
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"
    "io/ioutil"
    "log"
    "net/http"
    "net/url"
    "strings"
)
type UrlManager struct {
    Urls map[string]bool
}
// 新建URL管理器
func NewUrlManager() *UrlManager {
    return &UrlManager{Urls: make(map[string]bool)}
}
// 添加URL到管理器队列
// 添加URL到管理器队列
func (um *UrlManager) AddUrl(url string) bool {
    if um.Urls[url] {
        // URL已经存在
        return false
    }
    um.Urls[url] = true
    return true
}
// 添加URL列表到管理器队列
func (um *UrlManager) AddUrls(urls []string) bool {
    added := false
    for _, url := range urls {
        if um.AddUrl(url) {
            added = true
        }
    }
    return added
}
// 判断URL是否存在
func (um *UrlManager) HasUrl(url string) bool {
    return um.Urls[url]
}
// 获取待爬取的URL
func (um *UrlManager) GetUrl() string {
    for url := range um.Urls {
        delete(um.Urls, url)
        return url
    }
    return ""
}
// 获取URL数量
func (um *UrlManager) UrlCount() int {
    return len(um.Urls)
}
type Downloader struct {
    client *http.Client
    crawledUrls map[string]bool
}
// 新建网页下载器
func NewDownloader() *Downloader {
    return &Downloader{client: &http.Client{}, crawledUrls: make(map[string]bool)}
}
// 网页下载
func (d *Downloader) Download(url string) ([]byte, error) {
    req, err := http.NewRequest("GET", url, nil)
    req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36")
    resp, err := d.client.Do(req)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    // 读取响应正文内容
    contents, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return nil, err
    }
    return contents, nil
}
// 判断URL是否已爬取
func (d *Downloader) IsCrawled(url string) bool {
    return d.crawledUrls[url]
}
// 将URL添加到已爬取列表中
func (d *Downloader) AddCrawled(url string) {
    d.crawledUrls[url] = true
}
type Parser struct{}
// 新建网页解析器
func NewParser() *Parser {
    return &Parser{}
}
// 网页解析
func (parser *Parser) Parse(content []byte,contentUrl string) []string {
    doc, err := goquery.NewDocumentFromReader(bytes.NewReader(content))
    if err != nil {
        log.Fatal(err)
    }
    var urls []string
    doc.Find("a").Each(func(i int, s *goquery.Selection) {
        href, exists := s.Attr("href")
        if exists && !strings.HasPrefix(href, "javascript") && len(href) > 1 {
            // 绝对路径和相对路径都考虑
            u, err := url.Parse(href)
            if err != nil {
                return
            }
            if u.IsAbs() {
                urls = append(urls, href)
                return
            }
            // 补全相对路径
            base, _ := url.Parse(contentUrl)
            urls = append(urls, base.ResolveReference(u).String())
        }
    })
    return urls
}

type MyModel struct {
    gorm.Model
    Name string
}
type Storage struct {
    db *gorm.DB
}

//新建数据存储器
func NewStorage() *Storage{
    db, _ := gorm.Open("mysql", "root:password@tcp(localhost:3306)/mydb?charset=utf8&parseTime=True&loc=Local")
    db.AutoMigrate(&MyModel{})
    return &Storage{db:db}
}

// 保存数据到数据库
func (storage *Storage) SaveData(data []string) {
    for _, item := range data {
        storage.db.Create(&MyModel{Name: item})
    }
}
func Run() {
    // 初始化URL管理器、网页下载器、网页解析器、存储器
    urlManager := NewUrlManager()
    downLoader := NewDownloader()
    parser := NewParser()
    storage := NewStorage()
    // 添加待爬取的URL
    urlManager.AddUrl("http://example.com")
    // 爬虫运行
    for urlManager.UrlCount() > 0 {
        // 获取待爬取的URL
        url := urlManager.GetUrl()
        // 判断URL是否已爬取过
        if downLoader.IsCrawled(url) {
            continue
        }
        // 下载网页
        contents, err := downLoader.Download(url)
        if err != nil {
            continue
        }
        // 解析网页
        urls := parser.Parse(contents,url)
        // 存储数据
        storage.SaveData(urls)
        // 将URL添加到已爬取过的URL列表
        downLoader.AddCrawled(url)
        // 将解析出来的URL添加到URL队列中
        urlManager.AddUrls(urls)
    }
}

func main(){
    Run()
}

3. 概要

Golang クローラーには、シンプルさ、効率性、スケーラビリティという特徴があります。自然な同時実行の利点により、データのクロール速度が大幅に向上します。この記事では、golang クローラーの基本的な構成と書き方を紹介するとともに、読者の皆様も実践経験を積んでいただければ幸いです。

以上がgolang クローラーの基本コンポーネントと書き方の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。