gocolly简介

0x00

gocolly是使用golang实现的一个爬虫库。之前在爬某些网页的时候做过简单应用,最近在爬某电商网站的时候,发现关于这个还挺有意思,所以趁年前有时间,看了下源码实现。
gocolly内部依赖的是http.Client与goquery。其中goquery的语法和jquery相同,整个流程比较简单。

0x01

gocolly的使用很简单,具体可以看给出的一些例子。
套路一般就是设置相关的回调函数,然后访问某个URL,引擎会根据访问结果来调用相应的回调函数。
回调函数接口包括:

// RequestCallback is a type alias for OnRequest callback functions
type RequestCallback func(*Request)  //发起访问前调用回调

// ResponseCallback is a type alias for OnResponse callback functions
type ResponseCallback func(*Response) //访问结束后调用回调

// HTMLCallback is a type alias for OnHTML callback functions
type HTMLCallback func(*HTMLElement) //访问结束后根据不同的querySelector 调用回调

// XMLCallback is a type alias for OnXML callback functions
type XMLCallback func(*XMLElement) //访问结束后根据不同的querySelector 调用回调

// ErrorCallback is a type alias for OnError callback functions
type ErrorCallback func(*Response, error) //发生错误时调用回调

// ScrapedCallback is a type alias for OnScraped callback functions
type ScrapedCallback func(*Response) //针对当前response的其他回调都调用过以后会调用这个回调

访问的接口可以简单调用Visit/Post。
下面就是一个最简单的例子:

func main() {
	c := colly.NewCollector(
		colly.AllowedDomains("hackerspaces.org", "wiki.hackerspaces.org"),
	)
	c.OnHTML("a[href]", func(e *colly.HTMLElement) {
		link := e.Attr("href")
		fmt.Printf("Link found: %q -> %s\n", e.Text, link)
		c.Visit(e.Request.AbsoluteURL(link))
	})
	c.OnRequest(func(r *colly.Request) {
		fmt.Println("Visiting", r.URL.String())
	})
	c.Visit("https://hackerspaces.org/")
}

0x02

gocolly的核心在于 Collector 结构体:

type Collector struct {
	UserAgent string
	MaxDepth int
	AllowedDomains []string
	DisallowedDomains []string
	DisallowedURLFilters []*regexp.Regexp
	URLFilters []*regexp.Regexp
	AllowURLRevisit bool
	MaxBodySize int
	CacheDir string
	IgnoreRobotsTxt bool
	Async bool
	ParseHTTPErrorResponse bool
	ID uint32
	DetectCharset bool
	RedirectHandler   func(req *http.Request, via []*http.Request) error
	store             storage.Storage
	debugger          debug.Debugger
	robotsMap         map[string]*robotstxt.RobotsData
	htmlCallbacks     []*htmlCallbackContainer
	xmlCallbacks      []*xmlCallbackContainer
	requestCallbacks  []RequestCallback
	responseCallbacks []ResponseCallback
	errorCallbacks    []ErrorCallback
	scrapedCallbacks  []ScrapedCallback
	requestCount      uint32
	responseCount     uint32
	backend           *httpBackend
	wg                *sync.WaitGroup
	lock              *sync.RWMutex
}

关于这个结构体我只介绍其中几个比较关键的字段:

  • UserAgent:ua的设置还是比较关键的,一般网站在反爬虫的时候都会检查ua字段。colly默认的ua简直送上门给人毙的。
    对ua的设置可以看gocolly.extensions 包的 RandomUserAgent 函数,这个函数会随机生成某个版本的FF和chrome的ua。
  • MaxDepth:用于控制某个请求的深度。我们知道在爬虫访问中有可能会在某个Request的基础上继续访问。这里每多访问一层就会将depth+1,如果超过MaxDepth,那么访问终止。默认值为0,即不检查深度。
  • AllowedDomains:爬虫允许访问的domain,这里只能做精确匹配。
  • DisallowedDomains:你懂的。当然我们知道,在大部分ACL实现中,deny的项都位于allow之前,这里也不例外。所以对url的检查会先检查 disallow再检查allow。
  • DisallowedURLFilters:正则表达式,用于检查具体的url,等于空就是没有拒绝项。
  • URLFilters:正则表达式,用于检查具体url,等于空就是全部允许。
  • AllowURLRevisit:是否允许相同的url被二次访问。
  • MaxBodySize:对response包体大小的限制。
  • CacheDir:对特定url访问的cache。比较奇怪的时没有看到过期时间的限制。所以当成功get完某个url(http status < 500)并且开启了cache功能,那么之后对这个url的访问都会直接访问cache。
  • IgnoreRobotsTxt:事实上应该很少有人会遵守robotstxt了吧。
  • Async:异步访问。但是这个标志是针对所有请求的,也就是说当这个标志被设置了,没法知道某个具体的请求是否完成。Collector.Wait() 函数之后在所有请求完成时才会返回。当然对单个request的异步完成事件也是很容易实现的。既可以使用原有的回调函数;也可以使用context来通知。
  • backend:真正的http访问引擎。

0x03

当 Collector 初始化完成之后,colly支持 Get/Post/Post Multi-part发起请求,同时可以自定义请求头部。在colly.Request中,我们可以设置一个Context字段,这个字段可以保存与访问有关的数据结构。
发起请求实际上是通过调用 http.Client 接口实现的。http.Client包中对cookie的实现挺不错,实际上我们已经不用额外增加对cookie的处理。
对3xx的情况,http.Client也做了比较好的处理。不过对内的跳转做了10次的限制。每次跳转都会设置相应的 Referer 值。
另外为了对抗某些简单的防御,colly的httpBackend还支持设置一些 LimitRule。LimitRule可以设置对访问某个域名的间隔(固定delay+随机delay)。另外LimitRule可以限制对某个域名的并发访问数量。LimitRule通过正则表达式来匹配 HOST。

0x04

接下来介绍下colly的一些杂项。

  • colly实现了InMemoryStorage用语记录访问过的url。如果 Collector 的AllowURLRevisit没有被设置,那么无法访问同一url。
    在爬某些网站时,我们还是需要把 AllowURLRevisit 标志置上。否则某些由于访问失败重新回到url池的url就无法再次访问了。
  • colly实现了一个queue,相当于一个url池。
  • colly提供了proxy功能,具体的proxy还是会通过http.Client实现。在调试中可以设置 burpsuite 的代理地址,抓取https包。
  • extension包实现了 Referrer 功能。
func Referrer(c *colly.Collector) {
	c.OnResponse(func(r *colly.Response) {
		r.Ctx.Put("_referrer", r.Request.URL.String())
	})
	c.OnRequest(func(r *colly.Request) {
		if ref := r.Ctx.Get("_referrer"); ref != "" {
			r.Headers.Set("Referrer", ref)
		}
	})
}

可以看出,这里Referrer是通过Request的Ctx字段进行传递的。也就是说我们需要复用一个Request持续访问页面内链接。另外Referrer其实是增加了两个回调函数。因此只需要调用一次,后续都会自动实现referer功能。

  • debug包实现了调试功能。日志可以打印到io.Writer接口或者web中。

0x05

colly在日常使用中,应该满足了大部分的需求。对于对抗这块,也做了一些比较基础的功能。更深入的功能,就需要自己来慢慢研究了。

你可能感兴趣的:(golang)