分享
  1. 首页
  2. 文章

写爬虫还在用 python?快来试试 go 语言的爬虫框架吧

Y_xx · · 5048 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

今天为大家介绍的是一款 go 语言爬虫框架 -- colly

开始

首先,你可以使用一下命令安装 colly

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

其次,构建 Collector,添加事件,然后访问:

package main
import (
 "fmt"
 "github.com/gocolly/colly"
)
func main() {
 // 初始化 colly
 c := colly.NewCollector(
 // 只采集规定的域名下的内容
 colly.AllowedDomains("hackerspaces.org", "wiki.hackerspaces.org"),
 )
 // 任何具有 href 属性的标签都会触发回调函数
 // 第一个参数其实就是 jquery 风格的选择器
 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))
 })
 // 在请求发起之前输出 url
 c.OnRequest(func(r *colly.Request) {
 fmt.Println("Visiting", r.URL.String())
 })
 // 从以下地址开始抓起
 c.Visit("https://hackerspaces.org/")
}

运行以上代码,会从最开始的地址抓起,一直把规定的两个域名下的页面递归采集完。看,是不是很简单很方便!

登录鉴权

某些网站的某些页面可能需要登录状态才能访问。Colly 提供 Post 方法用于登录请求(colly 本身会维护 cookie)。

// authenticate
err := c.Post("http://example.com/login", map[string]string{"username": "admin", "password": "admin"})
if err != nil {
 log.Fatal(err)
}

很多网站可能会有验证码、csrf_token 之类的仿网络攻击策略。对于 csrf_token,一般都会在页面的某个位置,比如表单,或者 mate 标签里,这些都是很容易获取到的。对于验证码,可以尝试在控制台输入结果或者采用图片识别的方式。

速率控制

很多内容网站会有防采集策略,所以过快的请求速率很可能导致被封 ip。这里可以使用 LimitRule 限制采集速度。

// 对于任何域名,同时只有两个并发请求在请求该域名
c.Limit(&colly.LimitRule{DomainGlob: "*", Parallelism: 2})

上面是一个简单的例子。除了可以限制域名并发量外,还可以限制间隔时间等。我们看一下 LimitRule 的结构:

type LimitRule struct {
 // 匹配域名的正则表达式
 DomainRegexp string
 // glob 匹配模式
 DomainGlob string
 // 在发起一个新请求时的等待时间
 Delay time.Duration
 // 在发起一个新请求时的随机等待时间
 RandomDelay time.Duration
 // 匹配到的域名的并发请求数
 Parallelism int
 waitChan chan bool
 compiledRegexp *regexp.Regexp
 compiledGlob glob.Glob
}

队列与redis存储支持

某些情况下,我们的爬虫可能会主动或被动地挂掉,所以一个合理的进度保存有助于我们排除已经爬过的内容。这时候我们就需要用到队列以及存储支持。

Colly 本身有文件存储模式,默认是 未开启状态。推荐使用 redis 进行存储。

urls := []string{
 "http://httpbin.org/",
 "http://httpbin.org/ip",
 "http://httpbin.org/cookies/set?a=b&c=d",
 "http://httpbin.org/cookies",
}
c := colly.NewCollector()
// 创建 redis storage
storage := &redisstorage.Storage{
 Address: "127.0.0.1:6379",
 Password: "",
 DB: 0,
 Prefix: "httpbin_test",
}
// 把 storage 设置到 collector 上
err := c.SetStorage(storage)
if err != nil {
 panic(err)
}
// 删除之前的数据(如果需要)
if err := storage.Clear(); err != nil {
 log.Fatal(err)
}
// 结束后关闭 redis 连接
defer storage.Client.Close()
// 使用 redis 作为存储后端,创建请求队列
// 消费者数量设定为 2
q, _ := queue.New(2, storage)
c.OnResponse(func(r *colly.Response) {
 log.Println("Cookies:", c.Cookies(r.Request.URL.String()))
})
// 把 url 加入到队列
for _, u := range urls {
 q.AddURL(u)
}
// 开始采集
q.Run(c)

使用队列时,在解析到页面的链接后,可以继续把链接的 url 添加到队列中。

内容解析

内容抓取到了,如何解析并获取我们想要的内容呢?
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()
 // ...
})

其他

除此之外,Colly 还具有其他强大的功能,比如最大递归深度、url 过滤、url revisit(默认一个 url 只访问一次)以及编码检测等。这些都可以在官网文档或者 colly 代码里找到影子。

另附上 colly 文档地址:http://go-colly.org/docs/intr...


bVbjLVY?w=800&h=300


有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
5048 次点击
被以下专栏收入,发现更多相似内容
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏