Colly 官方文档学习从入门到入土

Colly 官方文档学习从入门到入土

一、介绍

Colly 是用于构建 Web 爬虫的 Golang 框架。使用 Colly ,你可以构建各种复杂的 Web 抓取工具,从简单的抓取工具到处理数百万个网页的复杂的异步网站抓取工具。 Colly 提供了一个 API,用于执行网络请求和处理接收到的内容(例如,与 HTML 文档的 DOM 树进行交互)。

如何安装

Colly 只有一个前置条件,那就是 Golang 编程语言。你可以使用其安装指南进行安装。

安装 Colly

在终端上键入以下命令,然后按 Enter 键安装 Colly 。

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

入门

使用Colly之前,请确保你具有最新版本。有关更多详细信息,请参见安装指南。

让我们从一些简单的例子开始。

首先,你需要将Colly导入你的代码库:

1
import "github.com/gocolly/colly"

Collector

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

1
c := colly.NewCollector()

回调

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

将回调添加到 Collector
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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.OnResponseHeaders(func(r *colly.Response) {
fmt.Println("Visited", r.Request.URL)
})

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.OnResponseHeaders
在收到响应标头后调用

4.OnResponse
收到回复后调用

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

6.OnXML
OnHTML如果接收到的内容是HTML或XML ,则在之后调用

7.OnScraped
OnXML回调后调用

配置

Colly 是一个高度可定制的抓取框架。它具有合理的默认值,并提供了很多选项来更改它们。

Collector 配置

Collector属性的完整列表可以在这里找到。建议使用初始化 Collector 的方法colly.NewCollector(options...)

使用默认设置创建Collector:

1
c1 := colly.NewCollector()

创建另一个 Collector 并更改User-Agenturl重新访问选项:

1
2
3
4
c2 := colly.NewCollector(
colly.UserAgent("xy"),
colly.AllowURLRevisit(),
)

通过覆盖 Collector 的属性,可以在抓取作业的任何时候更改配置。

一个很好的例子是User-Agent切换器,它会在每个请求上更改User-Agent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 初始化的最后一步,因此初始化之后的每个配置更改都会覆盖从环境解析的配置。

环境配置变量
  • COLLY_ALLOWED_DOMAINS (comma separated list of domains)
  • COLLY_CACHE_DIR (string)
  • COLLY_DETECT_CHARSET (y/n)
  • COLLY_DISABLE_COOKIES (y/n)
  • COLLY_DISALLOWED_DOMAINS (comma separated list of domains)
  • COLLY_IGNORE_ROBOTSTXT (y/n)
  • COLLY_FOLLOW_REDIRECTS (y/n)
  • COLLY_MAX_BODY_SIZE (int)
  • COLLY_MAX_DEPTH (int - 0 means infinite)
  • COLLY_PARSE_HTTP_ERROR_RESPONSE (y/n)
  • COLLY_USER_AGENT (string)

HTTP 配置

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

1
2
3
4
5
6
7
8
9
10
11
12
13
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,
}

二、最佳实践

使用多个 Collector

如果任务足够复杂或具有不同类型的子任务,建议使用多个 Collector 来执行一个抓取作业。一个很好的例子是 Coursera 课程抓取工具,其中使用了两个 Collector -一个解析列表视图并处理分页,另一个则收集课程详细信息。

Colly 具有一些内置方法来支持多个 Collector 的使用。

Tips:用于Collector.ID调试以区分不同的 Collector

克隆采集器

Clone()如果 Collector 具有类似的配置,则可以使用 Collector 的方法。Clone()复制具有相同配置但没有附加回调的 Collector 。

1
2
3
4
5
6
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()
在 Collector 之间传递自定义数据

使用 Collector 的Request()功能可以与其他 Collector 共享上下文。

共享上下文示例:

1
2
3
4
c.OnResponse(func(r *colly.Response) {
r.Ctx.Put(r.Headers.Get("Custom-Header"))
c2.Request("GET", "https://foo.com/", nil, r.Ctx, nil)
})

调试

有时将一些log.Println()函数调用放置到你的回调中就足够了,但有时却不是。 Colly 具有用于 Collector 调试的内置功能。提供了调试器接口和其他类型的调试器实现。

将调试器附加到Collector

附加基本的日志调试器需要 Colly 仓库中的debuggithub.com/gocolly/colly/debug)包。

1
2
3
4
5
6
7
8
9
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)

Tips:带有-D标志的 SSH 服务器可以用作 socks5 代理。

Colly 具有内置的代理切换器,可根据每个请求轮流代理列表。

用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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)
}
// ...
}

实现自定义代理切换器:

1
2
3
4
5
6
7
8
9
10
11
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 处理。

Tips: Colly 具有内置的 Google App Engine 支持。如果你从 App Engine 标准环境中使用 Colly ,请不要忘记调用Collector.Appengine(*http.Request)

可以在此处找到示例实现。

分布式存储

默认情况下,访问的 URL 和 cookie 数据存储在内存中。这对于短寿命的刮板作业很方便,但是在处理大规模或长期运行的爬行作业时可能是一个严重的限制。

Colly 能够用实现了Colly / storage.Storage接口的任何存储后端替换默认的内存存储。检查现有存储。

储存后端

Colly 有一个内存中的存储后端,用于存储 cookie 和访问的 URL ,但是任何实现 Colly / storage.Storage 的自定义存储后端都可以覆盖它。

现有的存储后端

  • 内存后端: Colly 的默认后端。使用Collector.SetStorage()覆盖。
  • Redis 后端:有关详细信息,请参见redis示例。
  • boltdb 后端
  • SQLite3 后端
  • MongoDB 后端
  • PostgreSQL 后端

爬虫程序配置

Colly 的默认配置经过优化,可以在一项作业中抓取较少数量的站点。如果你想抓取数百万个网站,则此设置不是最佳选择。以下是一些调整:

使用永久性存储后端

默认情况下, Colly 将 cookie 和访问的 URL 存储在内存中。你可以用任何自定义后端替换内置的内存中存储后端。在这里查看更多详细信息。

将异步用于具有递归调用的长时间运行的作业

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

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

Colly 使用 HTTP 保持活动来提高抓取速度。它需要打开文件描述符,因此长时间运行的作业很容易达到max-fd限制。

可以使用以下代码禁用 HTTP Keep-alive:

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

扩展

扩展是 Colly 随附的小型帮助程序实用程序。插件列表可在此处获得。

用法

以下示例启用了随机 User-Agent 切换器和 Referrer setter 扩展,并访问了 httpbin.org 两次。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import (
"log"

"github.com/gocolly/colly"
"github.com/gocolly/colly/extensions"
)

func main() {
c := colly.NewCollector()
visited := false

extensions.RandomUserAgent(c)
extensions.Referer(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")
}

三、例子

基本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package main

import (
"fmt"

"github.com/gocolly/colly"
)

func main() {
// Instantiate default collector
c := colly.NewCollector(
// Visit only domains: hackerspaces.org, wiki.hackerspaces.org
colly.AllowedDomains("hackerspaces.org", "wiki.hackerspaces.org"),
)

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

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

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

错误处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main

import (
"fmt"

"github.com/gocolly/colly"
)

func main() {
// Create a collector
c := colly.NewCollector()

// Set HTML callback
// Won't be called if error occurs
c.OnHTML("*", func(e *colly.HTMLElement) {
fmt.Println(e)
})

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

// Start scraping
c.Visit("https://definitely-not-a.website/")
}

登陆

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main

import (
"log"

"github.com/gocolly/colly"
)

func main() {
// create a new collector
c := colly.NewCollector()

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

// attach callbacks after login
c.OnResponse(func(r *colly.Response) {
log.Println("response received", r.StatusCode)
})

// start scraping
c.Visit("https://example.com/")
}

最大深度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main

import (
"fmt"

"github.com/gocolly/colly"
)

func main() {
// Instantiate default collector
c := colly.NewCollector(
// MaxDepth is 1, so only the links on the scraped page
// is visited, and no further links are followed
colly.MaxDepth(1),
)

// 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.Println(link)
// Visit link found on page
e.Request.Visit(link)
})

// Start scraping on https://en.wikipedia.org
c.Visit("https://en.wikipedia.org/")
}

文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package main

import (
"fmt"
"io/ioutil"
"net/http"
"os"
"time"

"github.com/gocolly/colly"
)

func generateFormData() map[string][]byte {
f, _ := os.Open("gocolly.jpg")
defer f.Close()

imgData, _ := ioutil.ReadAll(f)

return map[string][]byte{
"firstname": []byte("one"),
"lastname": []byte("two"),
"email": []byte("onetwo@example.com"),
"file": imgData,
}
}

func setupServer() {
var handler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
fmt.Println("received request")
err := r.ParseMultipartForm(10000000)
if err != nil {
fmt.Println("server: Error")
w.WriteHeader(500)
w.Write([]byte("<html><body>Internal Server Error</body></html>"))
return
}
w.WriteHeader(200)
fmt.Println("server: OK")
w.Write([]byte("<html><body>Success</body></html>"))
}

go http.ListenAndServe(":8080", handler)
}

func main() {
// Start a single route http server to post an image to.
setupServer()

c := colly.NewCollector(colly.AllowURLRevisit(), colly.MaxDepth(5))

// On every a element which has href attribute call callback
c.OnHTML("html", func(e *colly.HTMLElement) {
fmt.Println(e.Text)
time.Sleep(1 * time.Second)
e.Request.PostMultipart("http://localhost:8080/", generateFormData())
})

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

// Start scraping
c.PostMultipart("http://localhost:8080/", generateFormData())
c.Wait()
}

并行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package main

import (
"fmt"

"github.com/gocolly/colly"
)

func main() {
// Instantiate default collector
c := colly.NewCollector(
// MaxDepth is 2, so only the links on the scraped page
// and links on those pages are visited
colly.MaxDepth(2),
colly.Async(true),
)

// Limit the maximum parallelism to 2
// This is necessary if the goroutines are dynamically
// created to control the limit of simultaneous requests.
//
// Parallelism can be controlled also by spawning fixed
// number of go routines.
c.Limit(&colly.LimitRule{DomainGlob: "*", Parallelism: 2})

// 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.Println(link)
// Visit link found on page on a new thread
e.Request.Visit(link)
})

// Start scraping on https://en.wikipedia.org
c.Visit("https://en.wikipedia.org/")
// Wait until threads are finished
c.Wait()
}

代理切换器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package main

import (
"bytes"
"log"

"github.com/gocolly/colly"
"github.com/gocolly/colly/proxy"
)

func main() {
// Instantiate default collector
c := colly.NewCollector(colly.AllowURLRevisit())

// Rotate two socks5 proxies
rp, err := proxy.RoundRobinProxySwitcher("socks5://127.0.0.1:1337", "socks5://127.0.0.1:1338")
if err != nil {
log.Fatal(err)
}
c.SetProxyFunc(rp)

// Print the response
c.OnResponse(func(r *colly.Response) {
log.Printf("%s\n", bytes.Replace(r.Body, []byte("\n"), nil, -1))
})

// Fetch httpbin.org/ip five times
for i := 0; i < 5; i++ {
c.Visit("https://httpbin.org/ip")
}
}

队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package main

import (
"fmt"

"github.com/gocolly/colly"
"github.com/gocolly/colly/queue"
)

func main() {
url := "https://httpbin.org/delay/1"

// Instantiate default collector
c := colly.NewCollector()

// create a request queue with 2 consumer threads
q, _ := queue.New(
2, // Number of consumer threads
&queue.InMemoryQueueStorage{MaxSize: 10000}, // Use default queue storage
)

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

for i := 0; i < 5; i++ {
// Add URLs to the queue
q.AddURL(fmt.Sprintf("%s?n=%d", url, i))
}
// Consume URLs
q.Run(c)

}

随机延迟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package main

import (
"fmt"
"time"

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

func main() {
url := "https://httpbin.org/delay/2"

// Instantiate default collector
c := colly.NewCollector(
// Attach a debugger to the collector
colly.Debugger(&debug.LogDebugger{}),
colly.Async(true),
)

// Limit the number of threads started by colly to two
// when visiting links which domains' matches "*httpbin.*" glob
c.Limit(&colly.LimitRule{
DomainGlob: "*httpbin.*",
Parallelism: 2,
RandomDelay: 5 * time.Second,
})

// Start scraping in four threads on https://httpbin.org/delay/2
for i := 0; i < 4; i++ {
c.Visit(fmt.Sprintf("%s?n=%d", url, i))
}
// Start scraping on https://httpbin.org/delay/2
c.Visit(url)
// Wait until threads are finished
c.Wait()
}

速率限制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package main

import (
"fmt"

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

func main() {
url := "https://httpbin.org/delay/2"

// Instantiate default collector
c := colly.NewCollector(
// Turn on asynchronous requests
colly.Async(true),
// Attach a debugger to the collector
colly.Debugger(&debug.LogDebugger{}),
)

// Limit the number of threads started by colly to two
// when visiting links which domains' matches "*httpbin.*" glob
c.Limit(&colly.LimitRule{
DomainGlob: "*httpbin.*",
Parallelism: 2,
//Delay: 5 * time.Second,
})

// Start scraping in five threads on https://httpbin.org/delay/2
for i := 0; i < 5; i++ {
c.Visit(fmt.Sprintf("%s?n=%d", url, i))
}
// Wait until threads are finished
c.Wait()
}

Reids 后端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package main

import (
"log"

"github.com/gocolly/colly"
"github.com/gocolly/colly/queue"
"github.com/gocolly/redisstorage"
)

func main() {
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()

// create the redis storage
storage := &redisstorage.Storage{
Address: "127.0.0.1:6379",
Password: "",
DB: 0,
Prefix: "httpbin_test",
}

// add storage to the collector
err := c.SetStorage(storage)
if err != nil {
panic(err)
}

// delete previous data from storage
if err := storage.Clear(); err != nil {
log.Fatal(err)
}

// close redis client
defer storage.Client.Close()

// create a new request queue with redis storage backend
q, _ := queue.New(2, storage)

c.OnResponse(func(r *colly.Response) {
log.Println("Cookies:", c.Cookies(r.Request.URL.String()))
})

// add URLs to the queue
for _, u := range urls {
q.AddURL(u)
}
// consume requests
q.Run(c)
}

请求上下文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main

import (
"fmt"

"github.com/gocolly/colly"
)

func main() {
// Instantiate default collector
c := colly.NewCollector()

// Before making a request put the URL with
// the key of "url" into the context of the request
c.OnRequest(func(r *colly.Request) {
r.Ctx.Put("url", r.URL.String())
})

// After making a request get "url" from
// the context of the request
c.OnResponse(func(r *colly.Response) {
fmt.Println(r.Ctx.Get("url"))
})

// Start scraping on https://en.wikipedia.org
c.Visit("https://en.wikipedia.org/")
}

爬虫服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package main

import (
"encoding/json"
"log"
"net/http"

"github.com/gocolly/colly"
)

type pageInfo struct {
StatusCode int
Links map[string]int
}

func handler(w http.ResponseWriter, r *http.Request) {
URL := r.URL.Query().Get("url")
if URL == "" {
log.Println("missing URL argument")
return
}
log.Println("visiting", URL)

c := colly.NewCollector()

p := &pageInfo{Links: make(map[string]int)}

// count links
c.OnHTML("a[href]", func(e *colly.HTMLElement) {
link := e.Request.AbsoluteURL(e.Attr("href"))
if link != "" {
p.Links[link]++
}
})

// extract status code
c.OnResponse(func(r *colly.Response) {
log.Println("response received", r.StatusCode)
p.StatusCode = r.StatusCode
})
c.OnError(func(r *colly.Response, err error) {
log.Println("error:", r.StatusCode, err)
p.StatusCode = r.StatusCode
})

c.Visit(URL)

// dump results
b, err := json.Marshal(p)
if err != nil {
log.Println("failed to serialize response:", err)
return
}
w.Header().Add("Content-Type", "application/json")
w.Write(b)
}

func main() {
// example usage: curl -s 'http://127.0.0.1:7171/?url=http://go-colly.org/'
addr := ":7171"

http.HandleFunc("/", handler)

log.Println("listening on", addr)
log.Fatal(http.ListenAndServe(addr, nil))
}

网址过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package main

import (
"fmt"
"regexp"

"github.com/gocolly/colly"
)

func main() {
// Instantiate default collector
c := colly.NewCollector(
// Visit only root url and urls which start with "e" or "h" on httpbin.org
colly.URLFilters(
regexp.MustCompile("http://httpbin\\.org/(|e.+)$"),
regexp.MustCompile("http://httpbin\\.org/h.+"),
),
)

// 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 matched by any of the URLFilter regexps
c.Visit(e.Request.AbsoluteURL(link))
})

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

// Start scraping on http://httpbin.org
c.Visit("http://httpbin.org/")
}
# ,

评论

`
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×