gocolly-OnResponse的使用(3)

文章目录

介绍

  • 本章节使用OnResponse进行返回网页数据
  • 使用xpath定位数据;
  • 推荐htmlquery

demo

package main

import (
	"fmt"
	"github.com/antchfx/htmlquery"
	"github.com/gocolly/colly"
	"github.com/gocolly/colly/extensions"
	"gopkg.in/xmlpath.v2"
	"log"
	"os"
	"strings"
	"time"
)

/*
请求执行之前调用
	- OnRequest
响应返回之后调用
	- OnResponse
监听执行 selector
	- OnHTML
监听执行 selector
	- OnXML
错误回调
	- OnError
完成抓取后执行,完成所有工作后执行
	- OnScraped
取消监听,参数为 selector 字符串
	- OnHTMLDetach
取消监听,参数为 selector 字符串
	- OnXMLDetach
*/

func init() {
	// 日志配置
	// 指定输出日志的前缀
	log.SetPrefix("【UserCenter】")
	// Ldate:时间
	// Lshortfile:文件名+源代码
	/*
		const (
		    Ldate         = 1 << iota     //日期示例: 2009/01/23
		    Ltime                         //时间示例: 01:23:23
		    Lmicroseconds                 //毫秒示例: 01:23:23.123123.
		    Llongfile                     //绝对路径和行号: /a/b/c/d.go:23
		    Lshortfile                    //文件和行号: d.go:23.
		    LUTC                          //日期时间转为0时区的
		    LstdFlags     = Ldate | Ltime //Go提供的标准抬头信息
		)
	*/
	log.SetFlags(log.LstdFlags | log.Lshortfile)

}

// 声明结构体
type Session struct {
	session *colly.Collector
	file    *os.File
}

// 初始化
func (c *Session) Init() *colly.Collector {
	// 实例化默认收集器
	c.session = colly.NewCollector()

	// 仅访问域
	c.session.AllowedDomains = []string{"quotes.toscrape.com"}

	// 允许重复访问
	c.session.AllowURLRevisit = true

	// 表示抓取时异步的
	// c.session.Async = true
	// 模拟浏览器
	c.session.UserAgent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36"

	// 随机UserAgent
	extensions.RandomUserAgent(c.session)

	// 限制采集规则
	/*
		在Colly里面非常方便控制并发度,只抓取符合某个(些)规则的URLS
		colly.LimitRule{DomainGlob: "*.douban.*", Parallelism: 5},表示限制只抓取域名是douban(域名后缀和二级域名不限制)的地址,当然还支持正则匹配某些符合的 URLS

		Limit方法中也限制了并发是5。为什么要控制并发度呢?因为抓取的瓶颈往往来自对方网站的抓取频率的限制,如果在一段时间内达到某个抓取频率很容易被封,所以我们要控制抓取的频率。
		另外为了不给对方网站带来额外的压力和资源消耗,也应该控制你的抓取机制。
	*/
	err := c.session.Limit(&colly.LimitRule{
		// Filter domains affected by this rule
		// 筛选受此规则影响的域
		DomainGlob: "quotes.toscrape.com/*",
		// Set a delay between requests to these domains
		// 设置对这些域的请求之间的延迟
		Delay: 1 * time.Second,
		// Add an additional random delay
		// 添加额外的随机延迟
		RandomDelay: 1 * time.Second,
		// 设置并发
		Parallelism: 5,
	})
	if err != nil {
		fmt.Println(err)
	}

	return c.session
}

// 开始爬取
func (c *Session) Visit(url string) error {
	// 开始爬取 url
	err := c.session.Visit(url)
	if err != nil {
		fmt.Println(err)
		return err
	}
	return nil
}

// 访问下一页
// OnHTML selector
func (c *Session) getNext2(page *string) error {
	// 调用回调函数,获取标签的属性
	c.session.OnHTML(".pager .next a", func(e *colly.HTMLElement) {
		// 获取属性值
		link := e.Attr("href")
		*page = link
	})

	// 错误回调
	var err1 error = nil
	c.session.OnError(func(_ *colly.Response, err error) {
		fmt.Println("Something went wrong:", err)
		err1 = err
	})
	if err1 != nil {
		fmt.Println(err1)
		return err1
	}

	// 完成抓取后执行,完成所有工作后执行
	// c.session.OnScraped(func(r *colly.Response) {
	// 	fmt.Println("Finished", r.Request.URL)
	// })
	return nil
}

// htmlquery xpath 推荐
func (c *Session) getNext(page *string) error {
	// 完成抓取后执行,完成所有工作后执行
	// c.session.OnScraped(func(r *colly.Response) {
	// 	fmt.Println("Finished", r.Request.URL)
	// })

	// 收到响应后
	c.session.OnResponse(func(r *colly.Response) {
		doc, err := htmlquery.Parse(strings.NewReader(string(r.Body)))
		if err != nil {
			log.Fatal(err)
		}
		link := htmlquery.FindOne(doc, `//ul[@class="pager"]/li[@class="next"]/a/@href`)
		*page = htmlquery.InnerText(link)
		// fmt.Println(*page)
	})

	// 错误回调
	var err1 error = nil
	c.session.OnError(func(_ *colly.Response, err error) {
		fmt.Println("Something went wrong:", err)
		err1 = err
	})
	if err1 != nil {
		fmt.Println(err1)
		return err1
	}

	return nil
}

// gopkg.in/xmlpath.v2 xpath
func (c *Session) getNext3(page *string) error {
	// 完成抓取后执行,完成所有工作后执行
	// c.session.OnScraped(func(r *colly.Response) {
	// 	fmt.Println("Finished", r.Request.URL)
	// })

	// 收到响应后
	c.session.OnResponse(func(r *colly.Response) {
		doc, err := xmlpath.ParseHTML(strings.NewReader(string(r.Body)))
		if err != nil {
			log.Fatal(err)
		}
		path := xmlpath.MustCompile(`//ul[@class="pager"]/li[@class="next"]/a/@href`)
		if link, ok := path.String(doc); ok {
			*page = link
		}
	})

	// 错误回调
	var err1 error = nil
	c.session.OnError(func(_ *colly.Response, err error) {
		fmt.Println("Something went wrong:", err)
		err1 = err
	})
	if err1 != nil {
		fmt.Println(err1)
		return err1
	}

	return nil
}

// 解析页面 OnHTML
func (c *Session) getParse2() {
	// 在每个 a 标签 href 属性 调用回调函数
	c.session.OnHTML(".row .col-md-8 .quote", func(e *colly.HTMLElement) {
		// text
		text := e.ChildText("span.text")
		// fmt.Println("text: ", text)

		// author
		author := e.ChildText("span .author")
		// fmt.Println("author: ", author)

		// tags
		var tags []string
		e.ForEach(".tags a", func(i int, e *colly.HTMLElement) {
			text := e.Text
			tags = append(tags, text)
		})
		// fmt.Println("tags: ", tags)

		// 保存
		c.save(text, author, tags)
	})

	// 收到响应后
	c.session.OnResponse(func(r *colly.Response) {
		if r.StatusCode != 200 {
			log.Println(r.StatusCode)
			return
		}
	})
}

// 解析页面 htmlquery xpath
func (c *Session) getParse() {
	// 收到响应后
	c.session.OnResponse(func(r *colly.Response) {
		if r.StatusCode != 200 {
			log.Println(r.StatusCode)
			return
		}

		doc, err := htmlquery.Parse(strings.NewReader(string(r.Body)))
		if err != nil {
			log.Fatal(err)
		}
		quoteNodes := htmlquery.Find(doc, `//div[@class="row"]/div[@class="col-md-8"]/div[@class="quote"]`)
		for _, node := range quoteNodes {
			textNode := htmlquery.FindOne(node, `./span[@class="text"]/text()`)
			text := htmlquery.InnerText(textNode)
			authorNode := htmlquery.FindOne(node, `./span/small[@class="author"]/text()`)
			author := htmlquery.InnerText(authorNode)
			var tags []string
			aNodes := htmlquery.Find(node, `./div[@class="tags"]/a`)
			for _, a := range aNodes {
				tagNode := htmlquery.FindOne(a, `./text()`)
				tag := htmlquery.InnerText(tagNode)
				tags = append(tags, tag)
			}
			fmt.Println(text)
			fmt.Println(author)
			fmt.Println(tags)
			// 保存
			c.save(text, author, tags)
		}

	})
}

// 翻页
func (c *Session) NextPage() {
	// 访问地址
	url := "http://quotes.toscrape.com"
	baseUrl := "http://quotes.toscrape.com"
	page := ""
	for i := 0; i <= 2; i++ {
		fmt.Println("start url: ", url)

		// 解析页面
		c.getParse()

		// 获取下一页地址
		err1 := c.getNext(&page)
		if err1 != nil {
			break
		}
		err2 := c.Visit(url)
		if err2 != nil {
			break
		}
		url = baseUrl + page
	}

}

// 保存
func (c *Session) save(text string, author string, tags []string) {
	newTags := strings.Join(tags, " ")
	// fmt.Println(newTags)
	_, _ = c.file.Write([]byte(text + "\n"))
	_, _ = c.file.Write([]byte(author + "\n"))
	_, _ = c.file.Write([]byte(newTags + "\n"))
	_, _ = c.file.Write([]byte(strings.Repeat("*", 20)))
	_, _ = c.file.Write([]byte("\n\n"))
}

func main() {
	s := &Session{}
	// 初始化
	s.Init()

	// 读写模式打开,写入追加
	s.file, _ = os.OpenFile("test.txt", os.O_RDWR|os.O_APPEND|os.O_CREATE, 0777)
	defer func() {
		if err := s.file.Close(); err != nil {
			fmt.Println(err)
		}
	}()

	// 翻页
	s.NextPage()

	// 采集等待结束
	s.session.Wait()

	fmt.Println("程序运行结束!")
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值