[每周一更]-(第148期):使用 Go 进行网页抓取:Colly 与 Goquery 的对比与思路

文章目录

最近公司新项目需要,会对一些文献网站进行爬取工作,如:https://pmc.ncbi.nlm.nih.gov/articles/PMC12156228/,在进行爬取过程中采用了goquery和colly两种,其实从单个页面来看速度上无差异,就是写法的区别。

本质两者都是解析识别html标签,colly可能更能应对复杂场景。基础逻辑都是针对html标签进行解析操作。

网页抓取是提取网站数据的强大技术,而 Go(Golang)提供了多个优秀的库来简化这一过程。在 Go 生态中,Collygoquery 是两个常用的网页抓取工具。本文将介绍它们的特性、使用场景及对比,并为你的文章提供一些写作思路,帮助你深入探讨 Go 语言在网页抓取中的应用。

Colly 概述

Colly 是一个高层次的 Go 网页抓取框架,设计注重简单性和高效性。它提供了简洁的 API,用于处理 HTTP 请求、管理 Cookie 和解析 HTML 内容。Colly 特别适合抓取大规模网站或多页面数据,因为它内置了并发支持和请求管理功能。

Colly 的核心特性

  • HTTP 客户端集成:内置 HTTP 请求处理,支持重试、超时和自定义请求头。
  • 并发支持:支持并行请求,可配置速率限制,避免对服务器造成过大压力。
  • HTML 解析:内部使用 goquery 进行 DOM 遍历和操作。
  • 扩展性:提供请求/响应的中间件支持,并可通过插件扩展功能。
  • 易用性:直观的 API,方便定义抓取规则和处理分页。

适用场景

Colly 适合抓取动态网站或大规模数据集,例如从电商网站提取商品信息,或爬取博客的多篇文章页面。

示例代码

go 复制代码
package main 

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

func main() {
	c := colly.NewCollector(colly.AllowedDomains("example.com"))
	c.OnHTML("a[href]", func(e *colly.HTMLElement) { link := e.Attr("href")
		fmt.Println("发现链接:", link)
		c.Visit(e.Request.AbsoluteURL(link)) 
  })
	c.OnRequest(func(r *colly.Request) { 
    fmt.Println("正在访问", r.URL.String()) 
  })
	c.Visit("http://example.com/")
}

Goquery 概述

Goquery 是一个受 jQuery 启发的 Go 库,专注于 HTML 文档的 DOM 遍历和操作。它不是一个完整的抓取框架,而是擅长解析和查询 HTML 内容。Goquery 适合需要精细控制 DOM 元素且已有 HTML 内容的项目。

Goquery 的核心特性

  • 类 jQuery 语法:对熟悉 JavaScript/jQuery 的开发者友好,易于选择和操作 DOM 元素。
  • 轻量级:专注于 HTML 解析,无内置 HTTP 客户端或爬取功能。
  • 强大选择器:支持 CSS 选择器,精准提取元素。
  • 链式方法:代码简洁且易读,适合 DOM 操作。

适用场景

Goquery 适合解析单页 HTML 内容,例如从静态网页提取特定数据点。

示例代码

go 复制代码
package main 

import ( 
  "fmt" "github.com/PuerkitoBio/goquery" "strings" 
) 

func main() {
	html := `<html><body><div class="post"><h1>Hello, World!</h1></div></body></html>`
	doc, err := goquery.NewDocumentFromReader(strings.NewReader(html))
	if err != nil {
		fmt.Println("错误:", err)
		return
	}
	doc.Find(".post h1").Each(func(i int, s *goquery.Selection) { 
        fmt.Println("标题:", s.Text()) 
    })
}

Colly 与 Goquery 的对比

特性 Colly Goquery
用途 完整网页抓取框架 DOM 解析与操作
HTTP 客户端 内置,支持并发请求 无(需外部 HTTP 客户端)
易用性 高层次 API,适合初学者 类 jQuery,适合熟悉 jQuery 的用户
并发支持 内置并行请求处理 不适用
HTML 解析 内部使用 goquery 核心功能,高度灵活
适用场景 大规模抓取、爬取多页面 单页解析、DOM 操作
性能 优化多请求处理 轻量级,适合解析任务
学习曲线 中等,因涉及抓取功能 较低,特别对 jQuery 用户友好

何时使用 Colly

  • 需要爬取多个页面或整个网站。
  • 要求内置 HTTP 请求管理和并发支持。
  • 项目涉及动态内容或复杂导航(如跟踪链接、处理分页)。

何时使用 Goquery

  • 已通过其他方式(如 net/http)获取 HTML 内容。
  • 需要使用 CSS 选择器进行精细的 DOM 操作。
  • 项目专注于静态页面或小规模抓取任务。

结合 Colly 和 Goquery

由于 Colly 内部使用 goquery 进行 HTML 解析,你可以在 Colly 框架中利用 goquery 的强大选择器进行更复杂的 DOM 查询。这种结合在 Colly 的内置解析功能不足以应对复杂需求时特别有用。

示例代码

go 复制代码
package main 

import ( "fmt" "github.com/PuerkitoBio/goquery" "github.com/gocolly/colly" ) 

func main() { 
    c := colly.NewCollector() 	
    c.OnHTML("div.post", func(e *colly.HTMLElement) { 	
        doc := e.DOM // goquery 选择器 	
        title := doc.Find("h1").Text() 	
        fmt.Println("文章标题:", title) }) 	
        c.Visit("http://example.com/") 
}

网页抓取的最佳实践

  1. 遵守 Robots.txt:谨慎使用 Colly 的 colly.IgnoreRobotsTxt(),并尊重网站的使用条款。
  2. 速率限制:在 Colly 中配置延迟,避免对服务器造成过大压力(例如:c.Limit(&colly.LimitRule{DomainGlob: "*", Parallelism: 2, Delay: 1 * time.Second}))。
  3. 错误处理:为 HTTP 请求和 HTML 解析添加健壮的错误处理逻辑。
  4. 代理与伪装:对于需要绕过反爬机制的场景,可配置代理或自定义 User-Agent。
  5. 数据存储:将抓取的数据结构化存储(如 JSON、CSV 或数据库),便于后续分析。

更多demo

1. 标准库 net/http

Go 的标准库 net/http 是最基础的 HTTP 客户端工具,提供了发送 HTTP 请求和处理响应的核心功能。它适合需要高度自定义抓取逻辑的场景。

特性

  • 灵活性:支持 GET、POST 等多种 HTTP 方法,可自定义请求头、Cookie 和超时设置。
  • 轻量级:无额外依赖,适合简单抓取任务。
  • 可扩展:可与 html 包或其他解析库(如 goquery)结合使用。
  • 并发支持:结合 Go 的 goroutine 实现高并发请求。

适用场景

  • 简单的网页数据抓取,如获取单个页面的 HTML。
  • 需要完全控制请求逻辑的场景。
  • 与其他解析库结合使用(如 goquery 或正则表达式)。

示例代码

go 复制代码
package main

import ( "fmt" "io" "net/http" )
func main() {
	resp, err := http.Get("http://example.com")
	if err != nil {
		fmt.Println("错误:", err)
		return
	}
	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("读取错误:", err)
		return
	}
	fmt.Println(string(body))
}

优点与局限

  • 优点:内置于 Go 标准库,无需额外依赖;高度可定制。
  • 局限:没有内置的 DOM 解析或爬取框架,需手动处理 HTML 和分页逻辑。

2. Chromedp

Chromedp 是一个基于 Chrome DevTools Protocol 的 Go 库,用于控制 headless Chrome 或 Chromium 浏览器,适合抓取动态渲染的网页(如依赖 JavaScript 的内容)。

特性

  • 动态内容抓取:能够执行 JavaScript,获取页面渲染后的 DOM。
  • 浏览器自动化:支持模拟用户操作(如点击、输入表单)。
  • 截图与 PDF 生成:可生成页面截图或导出为 PDF。
  • 并发支持:通过 goroutine 可实现多页面抓取。

适用场景

  • 抓取需要 JavaScript 渲染的动态网页(如 SPA 应用)。
  • 模拟用户交互的复杂场景(如登录后抓取数据)。
  • 需要截图或生成页面快照的任务。

示例代码

go 复制代码
package main 

import ( "context" "fmt" "github.com/chromedp/chromedp" ) 

func main() { 
  ctx, cancel := chromedp.NewContext(context.Background()) 
  defer cancel() 	
  var title string 
  err := chromedp.Run(ctx, 	chromedp.Navigate("http://example.com"), 	chromedp.Text("h1", &title), ) 
  if err != nil { 	
    fmt.Println("错误:", err) 	
    return 
  } 
  
  fmt.Println("页面标题:", title) 
}

优点与局限

  • 优点:处理动态内容能力强,接近真实浏览器行为。
  • 局限:依赖 Chrome/Chromium,资源占用较高;学习曲线较陡。

3. Ferret

Ferret 是一个声明式网页抓取框架,结合了 Go 和动态查询语言(类似 SQL),通过嵌入 Chrome 引擎支持动态内容抓取。

特性

  • 声明式查询:使用类似 SQL 的语法定义抓取规则,易于维护。
  • 动态内容支持:通过内置 Chrome 引擎处理 JavaScript 渲染。
  • 模块化:支持多种数据输出格式(如 JSON、CSV)。
  • 跨平台:无需手动安装浏览器,内置运行时。

适用场景

  • 需要快速定义抓取规则的场景。
  • 动态网页抓取,尤其是对非开发人员友好的场景。
  • 需要将抓取结果结构化输出的项目。

示例代码(简化的 Ferret 查询):

go 复制代码
package main 
import ( "context" "fmt" "github.com/MontFerret/ferret/pkg/runtime" ) 

func main() { 
  query := ` 	LET doc = DOCUMENT('http://example.com') 	FOR el IN doc.querySelectorAll('h1') 		RETURN el.innerText ` 
  prog, err := runtime.Compile(query) 
  if err != nil { 	
    fmt.Println("编译错误:", err) 	
    return 
  } 	
  ctx := context.Background() 
  result, err := prog.Run(ctx) 
  if err != nil { 	
    fmt.Println("运行错误:", err) 	return 
  } 
  fmt.Println(string(result)) 
}

优点与局限

  • 优点:声明式语法降低开发复杂度;支持动态内容。
  • 局限:依赖外部运行时,配置稍复杂;社区较小,文档有限。

4. GoCrawl

GoCrawl 是一个高性能的网页爬取框架,专注于分布式爬取和大规模数据采集,适合需要爬取整个网站的任务。

特性

  • 分布式爬取:支持多节点并行爬取,适合大规模任务。
  • 可配置性:提供灵活的爬取策略(如深度优先、广度优先)。
  • HTML 解析:内置简单的解析器,也可与 goquery 结合。
  • Robots.txt 支持:默认遵守网站爬取规则。

适用场景

  • 爬取整个网站或大量页面(如构建搜索引擎索引)。
  • 需要分布式部署的抓取任务。
  • 对性能和资源优化要求高的场景。

优点与局限

  • 优点:高性能,适合大规模爬取;支持分布式架构。
  • 局限:API 较复杂,适合有经验的开发者;社区活跃度较低。

5. Surf

Surf 是一个轻量级的 Go 浏览器库,基于 WebKit 引擎,支持简单的动态内容抓取和页面导航。

特性

  • 浏览器模拟:支持 JavaScript 执行和页面导航。
  • 简单 API:易于上手,适合中小型抓取任务。
  • 表单处理:支持模拟表单提交和用户交互。

适用场景

  • 需要轻量级动态内容抓取的场景。
  • 模拟浏览器行为但不想依赖 Chrome 的项目。
  • 快速原型开发。

优点与局限

  • 优点:轻量级,易于使用;支持部分动态内容。
  • 局限:功能不如 Chromedp 强大;对复杂 JavaScript 支持有限。

6. 正则表达式与 HTML 解析器

对于简单场景,可以结合 net/http 和 Go 的 regexp 包或 golang.org/x/net/html 进行手动解析。

特性

  • 正则表达式:通过 regexp 匹配特定模式,适合提取简单数据。
  • HTML 解析器golang.org/x/net/html 提供低层次的 HTML 解析,适合自定义 DOM 处理。
  • 无额外依赖:仅依赖标准库或轻量扩展包。

适用场景

  • 提取结构化数据(如特定字段或链接)。
  • 对性能要求极高且 HTML 结构简单的场景。
  • 不需要复杂爬取逻辑的小型任务。

示例代码(使用 html 解析器):

go 复制代码
package main 

import ( "fmt" "golang.org/x/net/html" "net/http" "strings" ) 

func main() { 
  
  resp, err := http.Get("http://example.com")
  if err != nil { 	
    fmt.Println("错误:", err) 	
    return 
  } 
  defer resp.Body.Close() 	
  doc, err := html.Parse(resp.Body) 
  if err != nil { 	
    fmt.Println("解析错误:", err) 	
    return 
  } 	
  var f func(*html.Node) 
  f = func(n *html.Node) { 	
    if n.Type == html.ElementNode && n.Data == "h1" { 		
      fmt.Println("标题:", strings.TrimSpace(n.FirstChild.Data)) 	
    } 	
    for c := n.FirstChild; c != nil; c = c.NextSibling { 		
      f(c) 	
    } 
  } 
  f(doc) 
}

优点与局限

  • 优点:高度灵活,适合特定需求;无外部依赖。
  • 局限:正则表达式难以维护;手动解析复杂 HTML 费时。

对比与选择指南

工具 动态内容支持 并发性 易用性 适用场景
net/http 中等 简单请求,高度自定义
Chromedp 较高 动态网页,浏览器自动化
Ferret 中等 中等 声明式抓取,动态内容
GoCrawl 有限 较高 大规模、分布式爬取
Surf 有限 中等 轻量级动态内容,快速开发
正则/Html 较低 简单数据提取,性能敏感场景

需要注意:

在你的开发中,可以通过以下方式融入这些技术方案:

  1. 分类介绍:将工具分为三类:基础请求(net/http)、动态内容(Chromedp、Ferret、Surf)、大规模爬取(GoCrawl、Colly)。
  2. 场景分析:针对不同场景(如静态网页、动态 SPA、大规模爬取)推荐合适的工具,并说明原因。
  3. 代码对比:展示同一抓取任务(如提取网页标题)在不同工具中的实现,突出代码复杂度与功能的差异。
  4. 反爬机制应对:讨论如何结合代理、User-Agent 伪装或 Chromedp 的 headless 模式绕过反爬限制。
  5. 性能与维护性:分析各工具的性能开销和代码维护成本,例如 net/http 的轻量 vs Chromedp 的资源占用。
  6. 道德与合规:强调抓取时的合法性问题,建议检查 Robots.txt 和网站条款。

扩展资源

相关推荐
汽车功能安全啊25 分钟前
利用对称算法及非对称算法实现安全启动
java·开发语言·安全
paopaokaka_luck1 小时前
基于Spring Boot+Vue的吉他社团系统设计和实现(协同过滤算法)
java·vue.js·spring boot·后端·spring
Flobby5291 小时前
Go语言新手村:轻松理解变量、常量和枚举用法
开发语言·后端·golang
nbsaas-boot2 小时前
SQL Server 窗口函数全指南(函数用法与场景)
开发语言·数据库·python·sql·sql server
东方佑2 小时前
递归推理树(RR-Tree)系统:构建认知推理的骨架结构
开发语言·r语言·r-tree
Warren982 小时前
Java Stream流的使用
java·开发语言·windows·spring boot·后端·python·硬件工程
伍哥的传说3 小时前
Radash.js 现代化JavaScript实用工具库详解 – 轻量级Lodash替代方案
开发语言·javascript·ecmascript·tree-shaking·radash.js·debounce·throttle
程序视点3 小时前
IObit Uninstaller Pro专业卸载,免激活版本,卸载清理注册表,彻底告别软件残留
前端·windows·后端
xidianhuihui4 小时前
go install报错: should be v0 or v1, not v2问题解决
开发语言·后端·golang
架构师沉默4 小时前
Java优雅使用Spring Boot+MQTT推送与订阅
java·开发语言·spring boot