简介(Introduction)
-
如何安装?
如何安装
- Colly 只依赖于Go 语言,你可以通过 安装指南 安装它
- 安装Colly,在终端输入如下命令然后回车安装Colly
go get -u github.com/gocolly/colly/...
-
入门
入门
- 开始使用Colly之前确保你已经安装最新版本,更详细内容见安装指南
- 让我们从一些简单的例子开始
- 首先你需要导入Colly到你的代码中
import "github.com/gocolly/colly"
收集器
Colly 的主要实例是收集器对象,当Colly 收集器任务运行时,收集器负责网络通讯和执行附加的回调任务。你必须初始化收集器
c := colly.NewCollector()
回调
你可以把不同类型的回调函数附加到收集器上来控制收集任务,然后取回信息,你可以在包文档中查看相关章节
添加回调到收集器中
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)
})
回调函数执行顺序
- OnRequest
在请求之前调用 - OnError
在请求中出现错误时调用 - OnResponse
响应接收到之后调用 - OnHTML
OnResponse 正确执行后,如果接收到的文本是HTML时执行 - OnXML
OnResponse 正确执行后,如果接收到的文本是XML时执行 - OnScraped
OnXML 回调后调用
-
配置
配置
Colly 是一个高度自定义的爬虫框架,它提供合理的默认配置,而且提供了大量的配置去改变它
收集器配置
全面的收集器属性列表可以在这儿查看,推荐使用colly.NewCollector(options...)这种方法来初始化收集器
使用默认配置创建一个收集器
c1 := colly.NewCollector()
创建另外一个收集器,改变User-Agent 和 Url Revisit
c2 := colly.NewCollector(
colly.UserAgent("xy"),
colly.AllowURLRevisit(),
)
或者
c2 := colly.NewCollector()
c2.UserAgent = "xy"
c2.AllowURLRevisit = true
通过重新设置收集器的属性可以在收集任务运行任何节点改变配置。
一个很好的例子是 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())
})
通过环境变量配置
收集器的默认配置可以通过环境变量来改变,它允许我们在不通过编译的情况下对配置进行微调
环境变量解析是在收集器初始化的最后一步执行,所以每个变量在初始化后的改变都可能被环境变量配置覆盖。
环境变量配置
- 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 client 作为网络层,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,
}
最佳实践(Best Practices)
-
调试
调试
有时候将log.Println()
函数放到回调函数中就足够了,但有时候却不行。Colly有收集器调试的内置功能,调试器接口可以使用不同的调试器实现。
将调试器添加到收集器
从Colly的repo中添加基本的日志调试器debug
import (
"github.com/gocolly/colly"
"github.com/gocolly/colly/debug"
)
func main() {
c := colly.NewCollector(colly.Debugger(&debug.LogDebugger{}))
// [..]
}
实现一个自定义的调试器
你通过实现debug.Debugger类可以创建任何种类的调试器。一个好的例子是 LogDebugger
-
分布式抓取
分布式爬虫可以被不同方式实现,取决于你的抓取任务是什么。大多数情况下,它足以扩展网络通信层,这可以使用代理和Colly的代理切换器轻松实现。
代理选择器
使用代理切换器仍然保持集中,而HTTP请求分布在多个代理之间。
Colly通过其SetProxyFunc()
函数来切换代理。任意自定义的函数可以通过SetProxyFunc()
传参,只要这个函数签名为func(*http.Request) (*url.URL, error)
注意
通过使用 -D 可以将ssh 服务用到 sockets5代理上
Colly 有一个内置的代理切换器,可以在每个请求上轮流切换代理列表
用法
package main
import (
"github.com/gocolly/colly"
"github.com/gocolly/colly/proxy"
)
func main() {
c := colly.NewCollector()
if p, err := proxy.RoundRobinProxySwitcher(
"socks5://127.0.0.1:1337",
"socks5://127.0.0.1:1338",
"http://127.0.0.1:8080",
); err == nil {
c.SetProxyFunc(p)
}
// ...
}
实现自定义的代理切换器:
var proxies []*url.URL = []*url.URL{
&url.URL{Host: "127.0.0.1:8080"},
&url.URL{Host: "127.0.0.1:8081"},
}
func randomProxySwitcher(_ *http.Request) (*url.URL, error) {
return proxies[random.Intn(len(proxies))], nil
}
// ...
c.SetProxyFunc(randomProxySwitcher)
分布式爬虫
管理独立的和分布式的爬虫最好的办法是你可以将爬虫包装在服务器中。服务器可以是各种服务的比如HTTP,TCP服务器和Google APP Engine。使用自定义的存储实现集中和持久化的cookie来处理访问的url
注意
Colly内置支持Google APP Engine。如果你需要从标准的APP Engine中使用Colly,别忘了调用Collector.Appengine(*http.Request)
。
一个实现的例子可以从这儿查看
分布式存储
默认情况下,访问的url和cookie数据存储在内存中,这对短生命周期抓取任务很简单。但它对处理大规模和长时间爬取任务有很多限制。
通过实现colly/storage.Storage
接口,Colly可以使用很多后端存储来代替默认的内存存储。查看existing storages.
-
后端存储
后端存储
Colly可以通过内存存储cookie和访问的url,但它可以被任何实现了colly/storage.Storage的后端存储覆盖
现有的后端存储
In-Memory Backend
Colly 默认的后端存储,可以使用collector.SetStorage() 来覆盖
Redis backend
详细信息查阅redis example
SQLite3 backend
-
使用多个收集器
使用多个收集器
如果一个爬虫任务足够复杂或者有各种各样的子任务,推荐使用多个收集器。一个很好地例子coursera course scraper
使用了2个收集器,一个处理视图页面列表,另外一个处理课程详情。
注意
在调试中使用collector.ID
来区分不同的收集器
克隆收集器
如果收集器中有非常相似的配置,你可以使用Clone()
方法来克隆收集器。Clone()
克隆收集器配置的副本,但不会添加回调函数。
c := colly.NewCollector(
colly.UserAgent("myUserAgent"),
colly.AllowedDomains("foo.com", "bar.com"),
)
// Custom User-Agent and allowed domains are cloned to c2
c2 := c.Clone()
在收集器之间传递自定义参数
使用收集器的Request()
方法可以在其他收集器中共享上下文
一个共享上下文的例子:
c.OnResponse(func(r *colly.Response) {
r.Ctx.Put(r.Headers.Get("Custom-Header"))
c2.Request("GET", "https://foo.com/", nil, r.Ctx, nil)
}
-
爬虫配置
爬虫配置
Colly的默认配置已经经过优化,可以在一个任务重抓取数量较小的站点。如果你想抓取上百万个站点,这样的启动不是最好的。这里有一些调整:
使用持久化的后端存储
默认情况下,Colly将cookie和访问过的URL存储在内存中,你可以使用任意自定义的后端存储来替换内置的内存存储。在这里查看更多详情。
对于递归调用的长任务使用异步存储
默认情况下,Colly在请求未完成时会阻塞。所以,在回调函数中递归调用Collector.Visit
会产生不断增长的堆栈,使用Collector.Async = true
可以避免(不要忘记了与异步一起使用c.Wait()
)
禁用或限制连接保持活动状态
Colly使用HTTP keep-alive提高爬取速度,它需要打开文件描述符,所以max-fd
限制可以轻松达到长时间运行任务
使用如下代码可以禁用HTTP keep-alive
c := colly.NewCollector()
c.WithTransport(&http.Transport{
DisableKeepAlives: true,
})
-
扩展
扩展
扩展是Colly附带的小型辅助工具,插件列表可以在此处获得
用法
以下示例启用随机User-Agent切换器和Referrer setter扩展,并访问httpbin.org两次
import (
"log"
"github.com/gocolly/colly"
"github.com/gocolly/colly/extensions"
)
func main() {
c := colly.NewCollector()
visited := false
extensions.RandomUserAgent(c)
extensions.Referrer(c)
c.OnResponse(func(r *colly.Response) {
log.Println(string(r.Body))
if !visited {
visited = true
r.Request.Visit("/get?q=2")
}
})
c.Visit("http://httpbin.org/get")
}