go爬虫框架go-colly官方从入门到实践之官方文档翻译

go-colly如何安装
  1. Colly 只依赖于Go 语言,Go语言安装可以通过 安装指南进行
  2. 安装Colly,windows在cmd终端/linux在shell 中输入如下命令然后回车安装Colly
go get -u github.com/gocolly/colly/...
  • 入门
入门
  1. 开始使用Colly之前确保你已经安装最新版本,更详细内容见安装指南
  2. 让我们从一些简单的例子开始
  3. 首先你需要导入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)
})

回调函数执行顺序

  1. OnRequest
    在请求之前调用
  2. OnError
    在请求中出现错误时调用
  3. OnResponse
    响应接收到之后调用
  4. OnHTML
    OnResponse 正确执行后,如果接收到的文本是HTML时执行
  5. OnXML
    OnResponse 正确执行后,如果接收到的文本是XML时执行
  6. 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")
}

实际例子:

package main
import (
“encoding/json”
“flag”
“log”
“os”
“strings”
“github.com/gocolly/colly/v2”
)
// Mail is the container of a single e-mail
type Mail struct {
Title string
Link string
Author string
Date string
Message string
}
func main() {
var groupName string
flag.StringVar(&groupName, “group”, “hspbp”, “Google Groups group name”)
flag.Parse()
threads := make(map[string][]Mail)
threadCollector := colly.NewCollector()
mailCollector := colly.NewCollector()
// Collect threads
threadCollector.OnHTML(“tr”, func(e *colly.HTMLElement) {
ch := e.DOM.Children()
author := ch.Eq(1).Text()
// deleted topic
if author == “” {
return
}
title := ch.Eq(0).Text()
link, _ := ch.Eq(0).Children().Eq(0).Attr(“href”)
// fix link to point to the pure HTML version of the thread
link = strings.Replace(link, “.com/d/topic”, “.com/forum/?_escaped_fragment_=topic”, 1)
date := ch.Eq(2).Text()
log.Printf(“Thread found: %s %q %s %s\n, link, title, author, date)
mailCollector.Visit(link)
})
// Visit next page
threadCollector.OnHTML(“body > a[href]”, func(e *colly.HTMLElement) {
log.Println(“Next page link found:”, e.Attr(“href”))
e.Request.Visit(e.Attr(“href”))
})
// Extract mails
mailCollector.OnHTML(“body”, func(e *colly.HTMLElement) {
// Find subject
threadSubject := e.ChildText(“h2”)
if _, ok := threads[threadSubject]; !ok {
threads[threadSubject] = make([]Mail, 0, 8)
}
// Extract mails
e.ForEach(“table tr”, func(_ int, el *colly.HTMLElement) {
mail := Mail{
Title: el.ChildText(“td:nth-of-type(1)”),
Link: el.ChildAttr(“td:nth-of-type(1)”, “href”),
Author: el.ChildText(“td:nth-of-type(2)”),
Date: el.ChildText(“td:nth-of-type(3)”),
Message: el.ChildText(“td:nth-of-type(4)”),
}
threads[threadSubject] = append(threads[threadSubject], mail)
})
// Follow next page link
if link, found := e.DOM.Find(“> a[href]”).Attr(“href”); found {
e.Request.Visit(link)
} else {
log.Printf(“Thread %q done\n, threadSubject)
}
})
threadCollector.Visit(“https://groups.google.com/forum/?_escaped_fragment_=forum/” + groupName)
enc := json.NewEncoder(os.Stdout)
enc.SetIndent(“”, ” “)
// Dump json to the standard output
enc.Encode(threads)
}

相关推荐

JavaScript高级程序设计学习笔记(一)

GLSurfaceView 使用

在这里插入图片描述

Godot Engine:用call_deferred线程安全地调用一个方法

搜索结果

基于Nodejs的express框架使用mogoose来操作mongoDB数据库实现分词搜索的精确查找的后端接口文档