您的位置:首页 > 技术中心 > 其他 >

golang爬虫的基本组成部分和编写方法

时间:2023-04-25 18:38

随着互联网的普及与信息化的加速发展,越来越多的数据被存放在互联网上,因此网络爬虫已经成为许多人不可或缺的工具。其中,golang爬虫由于其简洁、高效和可扩展性,成为了许多程序员首选的爬虫编写语言。

本文将介绍golang爬虫的基本组成部分和编写方法。

一、golang爬虫的基本组成部分

  1. URL管理器(UrlManager)

URL管理器主要负责管理需要爬取的URL队列,以及去重等相关操作。其主要包含以下功能:

  • 添加URL:将需要爬取的URL添加到队列中;
  • 获取URL:从URL队列中获取待爬取的URL;
  • 存储URL:将已爬过的URL存储下来;
  • 去重:防止重复爬取同一个URL。
  1. 网页下载器(Downloader)

网页下载器主要负责将URL对应的网页下载到本地。它可以根据URL的不同特点,采用不同的下载方式,如HTTP、HTTPS、FTP等。在golang中,可通过使用第三方库,如net/http来进行网页下载。

  1. 网页解析器(Parser)

网页解析器主要负责对下载下来的网页进行解析,获取需要的数据并保存。golang中,可通过正则表达式、html5解析器、goquery等方法进行网页解析。

  1. 存储器(Storage)

存储器主要负责将已经解析下来的数据进行存储,一般有数据库存储和本地文件存储两种方式。golang中可以使用第三方库如GORM、orm等进行数据存储。

二、golang爬虫的编写方法

  1. 创建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)}
  1. 创建网页下载器

网页下载器主要用来下载指定的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}
  1. 创建网页解析器

网页解析器主要用来解析下载下来的网页内容,并提取需要的数据。下面是以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}
  1. 创建存储器

存储器主要用来将解析后的数据存储到本地或数据库中,此处以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})    }}
  1. 爬虫控制器

爬虫控制器主要实现爬虫的调度与协调功能。其主要流程为:

  • 初始化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)    }}
  1. 完整代码
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爬虫的基本组成部分和编写方法的详细内容,更多请关注Gxl网其它相关文章!

热门排行

今日推荐

热门手游