当前位置: 首页 > 工具软件 > Colly > 使用案例 >

gocolly-手册

舒嘉德
2023-12-01

一、安装

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

二、入门

import "github.com/gocolly/colly"

2.1 收集器

Colly 的主要实例是收集器对象,当Colly 收集器任务运行时,收集器负责网络通讯和执行附加的回调任务。你必须初始化收集器

c := colly.NewCollector()

2.1.1 收集器配置

全面的收集器属性列表可以在这儿查看,推荐使用colly.NewCollector(options…)这种方法来初始化收集器

c2 := colly.NewCollector(
    colly.UserAgent("xy"),
    colly.AllowURLRevisit(),
  	colly.AllowedDomains("hackerspaces.org", "wiki.hackerspaces.org"),
    colly.CacheDir("./coursera_cache"),
)
//或
c2 := colly.NewCollector()
c2.UserAgent = "xy"
c2.AllowURLRevisit = true
//或
c := colly.NewCollector(func(c *colly.Collector) {
		extensions.RandomUserAgent(c) // 设置随机头
		c.Async=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,
}

2.2.2 对于递归调用的长任务使用异步存储

默认情况下,Colly在请求未完成时会阻塞。所以,在回调函数中递归调用Collector.Visit会产生不断增长的堆栈,使用Collector.Async = true 可以避免(不要忘记了与异步一起使用c.Wait()

2.2.3 禁用或限制连接保持活动状态

Colly使用HTTP keep-alive提高爬取速度,它需要打开文件描述符,所以max-fd限制可以轻松达到长时间运行任务

使用如下代码可以禁用HTTP keep-alive

c := colly.NewCollector()
c.WithTransport(&http.Transport{
    DisableKeepAlives: true,
})

2.2 回调

你可以把不同类型的回调函数附加到收集器上来控制收集任务,然后取回信息,你可以在包文档中查看相关章节

2.2.1 添加回调到收集器中

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)
})

2.2.2 回调函数执行顺序

OnRequest:在请求之前调用

OnError:在请求中出现错误时调用

OnResponse:响应接收到之后调用

OnHTML:OnResponse 正确执行后,如果接收到的文本是HTML时执行

OnXML:OnResponse 正确执行后,如果接收到的文本是XML时执行

OnScraped:OnXML 回调后调用

2.3 OnRequest

// Before making a request print "Visiting ..."
c.OnRequest(func(r *colly.Request) {
   fmt.Println("Visiting", r.URL.String())
})

2.4 OnError

c.OnError(func(r *colly.Response, err error) {
   fmt.Println("Request URL:", r.Request.URL, "failed with response:", r, "\nError:", err)
})

2.5 OnResponse

2.6 OnHTML

// On every a element which has href attribute call callback
c.OnHTML("a[href]", func(e *colly.HTMLElement) {
   link := e.Attr("href")
   // Print link
   fmt.Printf("Link found: %q -> %s\n", e.Text, link)
   // Visit link found on page
   // Only those links are visited which are in AllowedDomains
   c.Visit(e.Request.AbsoluteURL(link))
  
  
   e.Request.Visit(link)

  
   e.ForEach("table.basic-info-table tr", func(_ int, el *colly.HTMLElement) {
			switch el.ChildText("td:first-child") {
			case "Language":
				course.Language = el.ChildText("td:nth-child(2)")
			case "Level":
				course.Level = el.ChildText("td:nth-child(2)")
			case "Commitment":
				course.Commitment = el.ChildText("td:nth-child(2)")
			case "How To Pass":
				course.HowToPass = el.ChildText("td:nth-child(2)")
			case "User Ratings":
				course.Rating = el.ChildText("td:nth-child(2) div:nth-of-type(2)")
			}
		})
})

c.OnHTML("#currencies-all tbody tr", func(e *colly.HTMLElement) {
		writer.Write([]string{
			e.ChildText(".currency-name-container"),
			e.ChildText(".col-symbol"),
			e.ChildAttr("a.price", "data-usd"),
			e.ChildAttr("a.volume", "data-usd"),
			e.ChildAttr(".market-cap", "data-usd"),
			e.ChildAttr(".percent-change[data-timespan=\"1h\"]", "data-percentusd"),
			e.ChildAttr(".percent-change[data-timespan=\"24h\"]", "data-percentusd"),
			e.ChildAttr(".percent-change[data-timespan=\"7d\"]", "data-percentusd"),
		})
	})

2.7 OnXML

2.8 OnScraped

2.9 Visit

// Start scraping on https://hackerspaces.org
c.Visit("https://hackerspaces.org/")

三、插件

3.1 调试

有时候将log.Println()函数放到回调函数中就足够了,但有时候却不行。Colly有收集器调试的内置功能,调试器接口可以使用不同的调试器实现。

3.1.1 将调试器添加到收集器

从Colly的repo中添加基本的日志调试器debug

import (
    "github.com/gocolly/colly"
    "github.com/gocolly/colly/debug"
)

func main() {
    c := colly.NewCollector(colly.Debugger(&debug.LogDebugger{}))
    // [..]
}

3.1.2 实现一个自定义的调试器

你通过实现debug.Debugger类可以创建任何种类的调试器。一个好的例子是 LogDebugger

3.2 代理选择器

使用代理切换器仍然保持集中,而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)

3.3 队列

3.4 存储

Colly可以通过内存存储cookie和访问的url,但它可以被任何实现了colly/storage.Storage的后端存储覆盖

现有的后端存储

In-Memory Backend

Colly 默认的后端存储,可以使用collector.SetStorage() 来覆盖

Redis backend

详细信息查阅redis example

SQLite3 backend

3.5 扩展

扩展是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")
}
 类似资料: