隨著網路的普及與資訊化的加速發展,越來越多的資料被存放在網路上,因此網路爬蟲已經成為許多人不可或缺的工具。其中,golang爬蟲由於其簡潔、高效和可擴展性,成為了許多程式設計師首選的爬蟲編寫語言。
本文將介紹golang爬蟲的基本組成部分和編寫方法。
一、golang爬蟲的基本組成部分
URL管理器主要負責管理需要爬取的URL佇列,以及去重等相關操作。其主要包含以下功能:
網頁下載器主要負責將URL對應的網頁下載到本機。它可以根據URL的不同特點,採用不同的下載方式,如HTTP、HTTPS、FTP等。在golang中,可透過使用第三方函式庫,如net/http來進行網頁下載。
網頁解析器主要負責對下載下來的網頁進行解析,以取得所需的資料並儲存。 golang中,可透過正規表示式、html5解析器、goquery等方法進行網頁解析。
記憶體主要負責將已經解析下來的資料進行存儲,一般有資料庫儲存和本機檔案儲存兩種方式。 golang中可以使用第三方函式庫如GORM、orm等進行資料儲存。
二、golang爬蟲的編寫方法
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) }
網頁下載器主要用來下載指定的URL對應的網頁內容,並將其傳回。
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 }
網頁解析器主要用來解析下載下來的網頁內容,並擷取所需的資料。以下是以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 }
記憶體主要用來將解析後的資料儲存到本機或資料庫中,此處以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}) } }
爬蟲控制器主要實作爬蟲的排程與協調功能。其主要流程為:
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() }
三、總結
golang爬蟲具有簡潔、高效和可擴展性的特點,並且由於其天然的並發優勢,可以大幅提升爬取數據速度。本文透過介紹golang爬蟲的基本組成和編寫方法,希望能對讀者有所幫助,也歡迎讀者在實踐中累積更多的經驗。
以上是golang爬蟲的基本組成部分和編寫方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!