Heim  >  Artikel  >  Backend-Entwicklung  >  Die Grundkomponenten und Schreibmethoden des Golang-Crawlers

Die Grundkomponenten und Schreibmethoden des Golang-Crawlers

PHPz
PHPzOriginal
2023-04-25 16:19:25656Durchsuche

Mit der Popularisierung des Internets und der beschleunigten Entwicklung der Informatisierung werden immer mehr Daten im Internet gespeichert, sodass Webcrawler für viele Menschen zu einem unverzichtbaren Werkzeug geworden sind. Unter ihnen ist Golang Crawler aufgrund seiner Einfachheit, Effizienz und Skalierbarkeit für viele Programmierer zur bevorzugten Crawler-Schreibsprache geworden.

In diesem Artikel werden die grundlegenden Komponenten und Schreibmethoden des Golang-Crawlers vorgestellt.

1. Die Grundkomponenten des Golang-Crawlers

  1. URL-Manager (UrlManager)

URL-Manager ist hauptsächlich für die Verwaltung der URL-Warteschlange, die gecrawlt werden muss, sowie für damit verbundene Vorgänge wie die Deduplizierung verantwortlich. Es umfasst hauptsächlich die folgenden Funktionen:

  • URL hinzufügen: Die zu crawlende URL zur Warteschlange hinzufügen;
  • URL abrufen: Die zu crawlende URL aus der URL-Warteschlange abrufen;
  • Speicher-URL: Die zu crawlende URL hinzufügen wurde gecrawlt. Speichern Sie es.
  • Deduplizierung: Verhindern Sie das wiederholte Crawlen derselben URL.
  1. Webpage Downloader (Downloader)

Webpage Downloader ist hauptsächlich dafür verantwortlich, die der URL entsprechende Webseite auf die lokale Seite herunterzuladen. Je nach den unterschiedlichen Eigenschaften der URL können unterschiedliche Download-Methoden verwendet werden, z. B. HTTP, HTTPS, FTP usw. In Golang können Webseiten mithilfe von Bibliotheken von Drittanbietern wie net/http heruntergeladen werden.

  1. Webpage Parser (Parser)

Webpage Parser ist hauptsächlich dafür verantwortlich, die heruntergeladene Webseite zu analysieren, die erforderlichen Daten abzurufen und zu speichern. In Golang können Webseiten mithilfe regulärer Ausdrücke, HTML5-Parser, Goquery und anderer Methoden analysiert werden.

  1. Speicher

Speicher ist hauptsächlich für die Speicherung der analysierten Daten verantwortlich. Es gibt im Allgemeinen zwei Methoden der Datenbankspeicherung und der lokalen Dateispeicherung. In Golang können Bibliotheken von Drittanbietern wie GORM, orm usw. zur Datenspeicherung verwendet werden.

2. So schreiben Sie einen Golang-Crawler

  1. Erstellen Sie einen URL-Manager

Der URL-Manager wird hauptsächlich zum Verwalten der zu crawlenden/gecrawlten URLs verwendet und bietet Vorgänge wie das Hinzufügen von URLs, das Abrufen von URLs und das Ermitteln, ob URLs vorhanden sind .

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. Erstellen Sie einen Webseiten-Downloader

Der Webseiten-Downloader wird hauptsächlich verwendet, um den Webseiteninhalt entsprechend der angegebenen URL herunterzuladen und zurückzugeben.

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. Erstellen Sie einen Webseiten-Parser

Der Webseiten-Parser wird hauptsächlich zum Parsen des heruntergeladenen Webseiteninhalts und zum Extrahieren der erforderlichen Daten verwendet. Das Folgende ist ein Beispiel für einen Parser, der Goquery als Beispiel verwendet:

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. Speicher erstellen

Speicher wird hauptsächlich zum lokalen Speichern geparster Daten oder in einer Datenbank verwendet. Hier ist eine MySQL-Datenbank als Beispiel:

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. Crawler-Controller

Der Crawler-Controller implementiert hauptsächlich die Planungs- und Koordinationsfunktionen von Crawlern. Der Hauptprozess ist:

  • URL-Manager, Webseiten-Downloader, Webseiten-Parser und Speicher initialisieren;
  • Schleife, um die zu crawlende URL zu erhalten; ob die URL gecrawlt wurde, überspringen Sie die URL;
  • Laden Sie die Webseite herunter und rufen Sie die Daten ab;
  • Fügen Sie die URL zur Liste der gecrawlten URLs hinzu.
  • 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)
        }
    }
  • Vollständiger Code
  • 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. Zusammenfassung: Der Golang-Crawler zeichnet sich durch Einfachheit, Effizienz und Skalierbarkeit aus und kann aufgrund seiner natürlichen Parallelitätsvorteile die Geschwindigkeit des Crawlens von Daten erheblich erhöhen. Dieser Artikel stellt die grundlegende Komposition und Schreibmethode von Golang Crawler vor und hofft, den Lesern dabei zu helfen, weitere Erfahrungen in der Praxis zu sammeln.

Das obige ist der detaillierte Inhalt vonDie Grundkomponenten und Schreibmethoden des Golang-Crawlers. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn