本文将介绍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]}// 获取待爬取的urlfunc (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 mainimport ( 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]}// 获取待爬取的urlfunc (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爬虫的基本组成部分和编写方法的详细内容。
