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
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:
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.
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.
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
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) }
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 }
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 }
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}) } }
Der Crawler-Controller implementiert hauptsächlich die Planungs- und Koordinationsfunktionen von Crawlern. Der Hauptprozess ist:
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) } }
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() }
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!