当前位置: 首页 > news >正文

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

在这里插入图片描述

文章目录

    • Colly 概述
      • Colly 的核心特性
      • 适用场景
    • Goquery 概述
      • Goquery 的核心特性
      • 适用场景
    • Colly 与 Goquery 的对比
      • 何时使用 Colly
      • 何时使用 Goquery
    • 结合 Colly 和 Goquery
    • 网页抓取的最佳实践
    • 更多demo
    • 1. 标准库 net/http
      • 特性
      • 适用场景
      • 优点与局限
    • 2. Chromedp
      • 特性
      • 适用场景
      • 优点与局限
    • 3. Ferret
      • 特性
      • 适用场景
      • 优点与局限
    • 4. GoCrawl
      • 特性
      • 适用场景
      • 优点与局限
    • 5. Surf
      • 特性
      • 适用场景
      • 优点与局限
    • 6. 正则表达式与 HTML 解析器
      • 特性
      • 适用场景
      • 优点与局限
    • 对比与选择指南
    • 需要注意:
    • 扩展资源

最近公司新项目需要,会对一些文献网站进行爬取工作,如: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 适合抓取动态网站或大规模数据集,例如从电商网站提取商品信息,或爬取博客的多篇文章页面。

示例代码

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 内容,例如从静态网页提取特定数据点。

示例代码

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 的对比

特性CollyGoquery
用途完整网页抓取框架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 的内置解析功能不足以应对复杂需求时特别有用。

示例代码

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 或正则表达式)。

示例代码

package mainimport ( "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 应用)。
  • 模拟用户交互的复杂场景(如登录后抓取数据)。
  • 需要截图或生成页面快照的任务。

示例代码

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 查询):

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 解析器):

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 和网站条款。

扩展资源

  • net/http 文档:https://golang.org/pkg/net/http/

  • Chromedp GitHub:https://github.com/chromedp/chromedp

  • Ferret 官网:https://www.montferret.dev/

  • GoCrawl GitHub:https://github.com/amir/gocrawl

  • Surf GitHub:https://github.com/headzoo/surf

  • Colly 官网:http://go-colly.org/

  • Goquery GitHub:https://github.com/PuerkitoBio/goquery

  • Go 官方文档:https://golang.org/pkg/net/http/

http://www.dtcms.com/a/301425.html

相关文章:

  • QT---概览
  • 优化Linux高并发:文件描述符与端口范围的协同调优
  • SPSC无锁环形队列技术(C++)
  • FreeRTOS—空闲任务
  • 【Python系列】Flask 应用中的主动垃圾回收
  • idea打开后project窗口未显示项目名称的解决方案
  • LangGraph快速入门项目部署
  • C++ 中实现 `Task::WhenAll` 和 `Task::WhenAny` 的两种方案
  • 从0搭建YOLO目标检测系统:实战项目+完整流程+界面开发(附源码)
  • jenkins只能运行2个任务,提示:“等待下一个可用的执行器”
  • Redis C++客户端——命令使用
  • 实战演练1:实战演练之命名实体识别
  • Docker 的数据持久化-数据卷
  • (AC)架子鼓
  • 基于Java的KTV点歌系统的设计与实现
  • 【CF】Day112——杂题 (逆向思维 | 二分 + 贪心 | 单调队列优化DP | 二进制 + 前缀和 | 二分图判断 | 暴力枚举)
  • JavaEE--3.多线程
  • python-装饰器
  • 【ST表、倍增】P7167 [eJOI 2020] Fountain (Day1)
  • QT6 源,七章对话框与多窗体(15)多文档 MDI 窗体 QMdiArea 篇一:属性,公共成员函数,信号与槽函数
  • 多智能体架构
  • 《计算机组成原理与汇编语言程序设计》实验报告四 Debug及指令测试
  • setnonblocking函数用途和使用案例
  • 在本地环境中运行 ‘dom-distiller‘ GitHub 库的完整指南
  • OSPF路由协议 多区域
  • 【ESP32】无法找到: “${env:IDF_PATH}/components/“的路径报错问题以及CMAKE构建不成功问题
  • Cursor报错解决【持续更新中】
  • 金融科技中的远程开户、海外个人客户在线开户、企业客户远程开户
  • 深入解析Java运行机制与JVM内存模型
  • 【Web APIs】JavaScript 节点操作 ⑩ ( 节点操作综合案例 - 动态生成表格案例 )