编程

go-colly:快速、优雅的 Go 语言爬虫框架

2773 2023-10-08 15:19:00

Colly 提供了一个干净的接口来编写任何类型的爬虫、采集器、蜘蛛

使用 Colly,可以快速从网站中提取结构化数据,这些数据可用于广泛的应用程序,如数据挖掘、数据处理或归档。

特性

  • 干净的API
  • 快速 (单核 >1k 请/秒)
  • 管理请求延迟和每个域名的最大并发性
  • 自动cookie和会话处理
  • 同步/异步/并行抓取
  • 分布式爬取
  • 缓存
  • 非unicode响应的自动编码
  • Robots.txt 支持
  • Google App Engine 支持

安装

在终端输入如下命令安装 Colly:

go get -u github.com/gocolly/colly/...

开始

首先, 引入 Colly:

import "github.com/gocolly/colly"

Collector

Colly 主要的实体是 Collecotr 对象。Collector 管理网络通信,并负责在 collector 作业运行时执行附加的回调。要使用colly,您必须初始化 Collector

c := colly.NewCollector()

回调

您可以将不同类型的回调函数附加到 Collector,用以控制收集作业或检索信息。查看软件包文档中的相关部分。

将回调添加到 Collector

c.OnRequest(func(r *colly.Request) {
    fmt.Println("Visiting", r.URL)
})

c.OnError(func(_ *colly.Response, err error) {
    log.Println("Something went wrong:", err)
})

c.OnResponse(func(r *colly.Response) {
    fmt.Println("Visited", r.Request.URL)
})

c.OnHTML("a[href]", func(e *colly.HTMLElement) {
    e.Request.Visit(e.Attr("href"))
})

c.OnHTML("tr td:nth-of-type(1)", func(e *colly.HTMLElement) {
    fmt.Println("First column of a table row:", e.Text)
})

c.OnXML("//h1", func(e *colly.XMLElement) {
    fmt.Println(e.Text)
})

c.OnScraped(func(r *colly.Response) {
    fmt.Println("Finished", r.Request.URL)
})

回调的调用顺序

1. OnRequest

请求前调用

2. OnError

在请求区间出错时调用

3. OnResponse

收到响应后调用

4. OnHTML

如果收到的内容是 HTML,则在 OnResponse 后调用

5. OnXML

如果收到的内容是 HTML 或 XML,则在 OnHTML 后调用

6. OnScraped

OnXML 回调后调用

 

配置

Colly 是可高度自定义的爬虫框架。它有合理的默认值,并提供了大量的更改选项。

Collector 配置

Collector 属性的完整列表可以在此处找到。初始化 collector 的推荐方法是使用 colly.NewCollector(options…)

使用默认设置创建 collector:

c1 := colly.NewCollector()

创建另外一个 collector 并修改 User-Agent 及 url revisit 选项:

c2 := colly.NewCollector(
	colly.UserAgent("xy"),
	colly.AllowURLRevisit(),
)

或者

c2 := colly.NewCollector()
c2.UserAgent = "xy"
c2.AllowURLRevisit = true

通过重写 Collector 的属性,可以在爬虫作业的任何点更改配置。

以下一个很好的例子是用 User-Agent 切换器,它在每次请求时都会更改 User-Agent:

const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

func RandomString() string {
	b := make([]byte, rand.Intn(10)+10)
	for i := range b {
		b[i] = letterBytes[rand.Intn(len(letterBytes))]
	}
	return string(b)
}

c := colly.NewCollector()

c.OnRequest(func(r *colly.Request) {
	r.Headers.Set("User-Agent", RandomString())
})

通过环境变量配置

Collector 默认配置可以通过环境变量进行更改。这使我们可以在不重新编译的情况下对 collector 进行微调。环境解析是 collector 初始化的最后一步,因此初始化后的每一次配置更改都会覆盖从环境中解析的配置。

环境配置变量

  • ALLOWED_DOMAINS (域名列表,逗号隔开)
  • CACHE_DIR (string)
  • DETECT_CHARSET (y/n)
  • DISABLE_COOKIES (y/n)
  • DISALLOWED_DOMAINS (域名列表,逗号隔开)
  • IGNORE_ROBOTSTXT (y/n)
  • MAX_BODY_SIZE (int)
  • MAX_DEPTH (int - 0 为无限)
  • PARSE_HTTP_ERROR_RESPONSE (y/n)
  • USER_AGENT (string)

HTTP 配置

Colly 使用 Golang 的默认 http 客户端 作为网络层。HTTP 选项可以通过修改默认的 HTTP roundtripper 微调。 

c := colly.NewCollector()
c.WithTransport(&http.Transport{
	Proxy: http.ProxyFromEnvironment,
	DialContext: (&net.Dialer{
		Timeout:   30 * time.Second,
		KeepAlive: 30 * time.Second,
		DualStack: true,
	}).DialContext,
	MaxIdleConns:          100,
	IdleConnTimeout:       90 * time.Second,
	TLSHandshakeTimeout:   10 * time.Second,
	ExpectContinueTimeout: 1 * time.Second,
}

内容解析

内容抓取到了,如何解析并获取咱们想要的内容呢?
html 为例(colly 也有 xml 等内容解析):

// refentry 内容
c.OnHTML(".refentry", func(element *colly.HTMLElement) {
   // ...
})

OnHtml 第一个参数是 jquery风格的选择器,第二个参数是 callback,callback 会传入 HTMLElement 对象。HTMLElement 结构体:
type HTMLElement struct {
   // 标签的名称
   Name       string
   Text       string
   attributes []html.Attribute
   // 当前的 request
   Request *Request
   // 当前的 response
   Response *Response
   // 当前节点的 DOM 元素
   DOM *goquery.Selection
   // 在该 callback 回调中,此 element 的索引
   Index int
}

其中,能够经过 DOM 字段操做(增删节点)、遍历、获取节点内容。
DOM 字段是 Selection 类型,该类型提供了大量的方法。若是你用过 jQuery,你必定会以为熟悉。

举个例子,咱们想要删除 h1.refname 标签,并返回父元素的 html 内容:

c.OnHTML(".refentry", func(element *colly.HTMLElement) {
   titleDom := element.DOM.Find("h1.refname")
   title := titleDom.Text()
   titleDom.Remove()
   
   content, _ := element.DOM.Html()
   // ...
})