golang爬虫的基本组成部分和编写方法
随着互联网的普及与信息化的加速发展,越来越多的数据被存放在互联网上,因此网络爬虫已经成为许多人不可或缺的工具。其中,golang爬虫由于其简洁、高效和可扩展性,成为了许多程序员首选的爬虫编写语言。
本文将介绍golang爬虫的基本组成部分和编写方法。
一、golang爬虫的基本组成部分
- URL管理器(UrlManager)
URL管理器主要负责管理需要爬取的URL队列,以及去重等相关操作。其主要包含以下功能:
- 添加URL:将需要爬取的URL添加到队列中;
- 获取URL:从URL队列中获取待爬取的URL;
- 存储URL:将已爬过的URL存储下来;
- 去重:防止重复爬取同一个URL。
- 网页下载器(Downloader)
网页下载器主要负责将URL对应的网页下载到本地。它可以根据URL的不同特点,采用不同的下载方式,如HTTP、HTTPS、FTP等。在golang中,可通过使用第三方库,如net/http来进行网页下载。
- 网页解析器(Parser)
网页解析器主要负责对下载下来的网页进行解析,获取需要的数据并保存。golang中,可通过正则表达式、html5解析器、goquery等方法进行网页解析。
- 存储器(Storage)
存储器主要负责将已经解析下来的数据进行存储,一般有数据库存储和本地文件存储两种方式。golang中可以使用第三方库如GORM、orm等进行数据存储。
二、golang爬虫的编写方法
- 创建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) }
- 创建网页下载器
网页下载器主要用来下载指定的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}) } }
- 爬虫控制器
爬虫控制器主要实现爬虫的调度与协调功能。其主要流程为:
- 初始化URL管理器、网页下载器、网页解析器、存储器;
- 将待爬取的URL添加到URL管理器队列中;
- 循环获取待爬取的URL;
- 判断URL是否已被爬取过,若已爬取过,则跳过该URL;
- 下载URL对应的网页;
- 解析网页,并取出数据;
- 将数据存储到数据库中;
- 将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) } }
- 完整代码
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爬虫的基本组成部分和编写方法的详细内容,更多请关注其它相关文章!