术业有专攻,不要用原生 http 库自己写爬虫!

术业有专攻,不要用原生 http 库自己写爬虫!

网管叨bi叨 2024-06-05 09:27 北京 3人听过
 

以下文章来源于凉凉的知识库 ,作者凉凉的知识库

凉凉的知识库.

凉凉的知识库,带你学点后端必备知识

有些人可能认为爬虫框架和http client库的功能一样,用http client库也可以写爬虫。当然,无论用第三方的http client库还是官方的http库,都可以写爬虫。但术业有专攻,爬虫框架专门为批量爬取设计,往往拥有并发控制、队列、缓存、HTML解析等一系列开箱即用的API,能大幅简化在爬虫实现过程中的负担

Python中非常知名的爬虫框架有Scrapy,Go中也有一些star数较高的爬虫框架。colly就是其中的佼佼者,它API简洁,性能优良,开箱即用。今天就来快速学习一下吧!

基本使用

首先引入依赖

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

之后就可以使用colly,通过Visit函数来告知colly采集器要访问的URL

package main

import (
 "fmt"

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

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

 c.Visit("http://go-colly.org/")
}

这样就行了么?运行下试试,没有任何输出。

$ go run main.go                                                  

原因在于代码要求colly采集器访问http://go-colly.org/,但没有设定访问URL成功或者失败后要执行的动作。colly提供了一系列的回调函数,用于URL访问和响应过程中各种情况的处理

例如,可以设定访问URL前、响应成功、响应失败时不同逻辑的处理

package main

import (
 "fmt"

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

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

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

 c.OnResponse(func(r *colly.Response) {
  fmt.Println("Visited", r.Request.URL)
 })

 c.OnError(func(_ *colly.Response, err error) {
  fmt.Println("Something went wrong:", err)
 })
 
 c.Visit("http://go-colly.org/")
}

colly提供的回调和回调的顺序如下图,每个回调可以设置多次,会依次执行

图片回调的时机

常规配置

配置分两部分,一部分是colly采集器的配置,一部分是HTTP的配置

🌲 colly采集器的配置

✨ 新建colly采集器时指定配置,例如

c := colly.NewCollector(
 colly.UserAgent("example.com"),
 colly.DisallowedDomains("baidu.com", "bing.com"),
)
 
//...
c.Visit("http://baidu.com/")
c.Visit("http://go-colly.org/")

✨ 使用环境变量可以不用重新编译代码。看名字大家应该也能猜到每个环境变量都有什么作用

注意环境变量有固定前缀COLLY_,官方文档里并没有说明(坑!!)

  • COLLY_ALLOWED_DOMAINS (逗号分隔)
  • COLLY_CACHE_DIR (string)
  • COLLY_DETECT_CHARSET (y/n)
  • COLLY_DISABLE_COOKIES (y/n)
  • COLLY_DISALLOWED_DOMAINS (逗号分隔)
  • COLLY_IGNORE_ROBOTSTXT (y/n)
  • COLLY_MAX_BODY_SIZE (int)
  • COLLY_MAX_DEPTH (0 代表不限深度)
  • COLLY_PARSE_HTTP_ERROR_RESPONSE (y/n)
  • COLLY_USER_AGENT (string)
$ COLLY_DISALLOWED_DOMAINS=baidu.com,bing.com go run main.go 
Visiting http://go-colly.org/
Visited http://go-colly.org/
Finished http://go-colly.org/

✨ 通过colly采集器的属性进行配置

c := colly.NewCollector()
c.UserAgent = "example.com"

//...
c.Visit("http://go-colly.org/")
c.DisallowedDomains = []string{"baidu.com", "bing.com"}
c.Visit("http://baidu.com/")

colly采集器的配置优先级就是上面介绍的顺序:新建 < 环境变量 < 实例属性

🌲 HTTP的配置

colly默认使用的是Go标准库中的http client,我们可以进行替换

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

常见功能

作为一个爬虫,很少会仅抓取一个链接,通常会抓取大量的链接,甚至会不断分析当前页面中的链接,继续进行深度的爬取。代码通常会类似下面

✨ 为了避免无限制的爬取,可以限制爬取的域名范围,和访问深度;

✨ colly会记录已经爬取过的链接,不会再重复爬取

func main() {
 c := colly.NewCollector(
  colly.AllowedDomains("httpbin.org"),
  colly.MaxDepth(2),
 )

 c.OnHTML("a[href]", func(e *colly.HTMLElement) {
  link := e.Attr("href")
  fmt.Printf("Link found: %q -> %s\n", e.Text, link)
  c.Visit(link)
 })

 c.OnError(func(_ *colly.Response, err error) {
  fmt.Println("Something went wrong:", err)
 })

 c.Visit("http://httpbin.org/links/20/3")
}

并行抓取

colly默认是串行逐个链接进行爬取,想要提高爬取能力最快速简单的方式就是开启并行。

✨ 除了需要设置colly.Async(true)之外,还需要在最后c.Wait()等待所有并发的请求执行完成

✨ c.Limit可以针对某一个域名设置并发度和发起每一个请求的延迟时间

func main() {
 c := colly.NewCollector(
  colly.AllowedDomains("httpbin.org"),
  colly.MaxDepth(2),
  colly.Async(true),
 )

 c.OnHTML("a[href]", func(e *colly.HTMLElement) {
  link := e.Attr("href")
  fmt.Printf("Link found: %q -> %s\n", e.Text, link)
  c.Visit(link)
 })
  
  c.Limit(&colly.LimitRule{
  DomainGlob:  "*httpbin.*",
  Parallelism: 2,
  Delay:      5 * time.Second,
 })

 c.Visit("http://httpbin.org/links/20/3")
 c.Wait()
}

持久化的外部存储

默认情况下已访问的URL和cookie等信息都是存储在内存中,服务重新启动后将会丢失这部分信息,且无法在多个机器直接共享。

当我们构建一个分布式爬虫时,我们需要一个公共的用于维护状态的持久化存储,例如redis等。

package main

import (
 "fmt"

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

func main() {
 c := colly.NewCollector(
  colly.AllowedDomains("httpbin.org", "go-colly.org"),
  colly.MaxDepth(2),
 )

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

 err := c.SetStorage(storage)
 if err != nil {
  panic(err)
 }
  
  // 清除之前存储的信息,可选
 if err := storage.Clear(); err != nil {
  panic(err)
 }

 defer storage.Client.Close()

 c.OnHTML("a[href]", func(e *colly.HTMLElement) {
  link := e.Attr("href")
  fmt.Printf("Link found: %q -> %s\n", e.Text, link)
  c.Visit(link)
 })

  // ...

 c.Visit("http://httpbin.org/links/20/3")
}

队列

可以使用队列来控制爬取的速率,默认情况下队列也是在内存中的

import (
 "fmt"

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

func main() {
 q, _ := queue.New(
  2, // 消费的进程数
  &queue.InMemoryQueueStorage{MaxSize: 10000}, // 默认的队列,内存队列
 )

 c := colly.NewCollector(
  colly.AllowedDomains("httpbin.org", "go-colly.org"),
  colly.MaxDepth(2),
 )

 c.OnHTML("a[href]", func(e *colly.HTMLElement) {
  link := e.Attr("href")
  fmt.Printf("Link found: %q -> %s\n", e.Text, link)
  q.AddURL(link)
 })

 q.AddURL("http://go-colly.org/")

 q.Run(c)
}

对于构建分布式爬虫来说,可以借助外部的队列提高整体的消费能力。

package main

import (
 "fmt"

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

func main() {
 // 创建redis存储
 storage := &redisstorage.Storage{
  Address:  "127.0.0.1:6379",
  Password: "",
  DB:       0,
  Prefix:   "httpbin_test",
 }

 q, err := queue.New(
  2, // 消费的进程数
  storage,
 )
 if err != nil{
  panic(err)
 }

 c := colly.NewCollector(
  colly.AllowedDomains("httpbin.org", "go-colly.org"),
  colly.MaxDepth(2),
 )
  
  err = c.SetStorage(storage)
 if err != nil {
  panic(err)
 }

 c.OnHTML("a[href]", func(e *colly.HTMLElement) {
  link := e.Attr("href")
  fmt.Printf("Link found: %q -> %s\n", e.Text, link)
  q.AddURL(link)
 })

 q.AddURL("http://go-colly.org/")

 q.Run(c)
}

总结

这篇文章作为一个入门介绍,看完后你应该能Get到普通的http client库和爬虫库的区别了吧。

colly作为一个爬虫框架集成了一系列针对爬虫的API,想要体验colly的更多能力,建议还是好好阅读下colly的文档。


 

结尾推荐一下我研发的画图课,让大家 从零开始学会画系统架构图 ,专栏正在更新中,涵盖了多个程序员内功的提升,现订阅人数已过百,目前价格有优惠,推荐大家订阅。

订阅方式一:公众号专栏《程序员的全能画图课》

订阅方式二:扫描下方二维码或者访问https://xiaobot.net/p/dev_img 订阅。

图片

 

 

阅读原文
阅读 1340
 
 
 
 
 
 
 
posted @ 2024-07-01 19:21  技术颜良  阅读(4)  评论(0编辑  收藏  举报