Project Icon

goquery

Go语言实现的jQuery风格HTML解析工具

goquery是Go语言生态中的一个HTML解析工具,其API设计借鉴了jQuery的语法风格。它构建在Go标准库的net/html包和第三方CSS选择器库cascadia之上,为开发者提供了简洁的链式调用方式和易于理解的方法名。goquery能够进行HTML文档的解析、元素查询、DOM遍历和修改等操作,广泛应用于网页内容抓取和数据分析场景。该库需要Go 1.18或更高版本,并保证与Go语言的最新两个版本保持兼容。

goquery - 有点像那个j东西,只不过是用Go语言实现的

构建状态 Go参考 Sourcegraph徽章

goquery为Go语言带来了类似jQuery的语法和一系列功能。它基于Go的net/html包和CSS选择器库cascadia。由于net/html解析器返回的是节点而非完整的DOM树,jQuery的有状态操作函数(如height()、css()、detach())被省略了。

此外,由于net/html解析器要求UTF-8编码,goquery也是如此:确保源文档提供UTF-8编码的HTML是调用者的责任。有关各种实现方法,请参阅wiki

在语法上,它尽可能接近jQuery,尽可能使用相同的函数名,并保留了那种温暖而模糊的可链式接口。鉴于jQuery是如此流行的库,我觉得编写一个类似的HTML操作库最好遵循其API,而不是重新开始(与Go的fmt包精神相同),尽管它的一些方法不太直观(说的就是你,index()...)。

目录

安装

请注意,从goquery的v1.9.0版本开始,由于使用了泛型,需要Go 1.18+。对于之前的goquery版本,由于net/html依赖,需要Go 1.1+版本。正在进行的goquery开发在最新的两个Go版本上进行测试。

$ go get github.com/PuerkitoBio/goquery

(可选)运行单元测试:

$ cd $GOPATH/src/github.com/PuerkitoBio/goquery
$ go test

(可选)运行基准测试(警告:需要几分钟时间):

$ cd $GOPATH/src/github.com/PuerkitoBio/goquery
$ go test -bench=".*"

更新日志

请注意,goquery的API现已稳定,不会发生破坏性变更。

  • 2024-04-29 (v1.9.2) : 更新 go.mod 依赖。
  • 2024-02-29 (v1.9.1) : 改进 Map 函数和 Selection.Map 方法的内存分配和性能,更好地记录cascadia的差异(感谢 @jwilsson)。
  • 2024-02-22 (v1.9.0) : 添加通用 Map 函数,goquery现在需要Go 1.18+版本(感谢 @Fesaa)。
  • 2023-02-18 (v1.8.1) : 更新 go.mod 依赖,更新CI工作流程。
  • 2021-10-25 (v1.8.0) : 添加 Render 函数,将 Selection 渲染到 io.Writer(感谢 @anthonygedeon)。
  • 2021-07-11 (v1.7.1) : 更新go.mod依赖并添加dependabot配置(感谢 @jauderho)。
  • 2021-06-14 (v1.7.0) : 添加 SingleSingleMatcher 函数以优化首次匹配选择(感谢 @gdollardollar)。
  • 2021-01-11 (v1.6.1) : 修复在包含非元素节点的 Selection 上调用 {Prepend,Append,Set}Html 时的崩溃问题。
  • 2020-10-08 (v1.6.0) : 对所有处理html字符串的函数(AfterHtmlAppendHtml等)在容器节点的上下文中解析html。感谢 @thiemok@davidjwilkins 的贡献。
  • 2020-02-04 (v1.5.1) : 更新模块依赖。
  • 2018-11-15 (v1.5.0) : 支持Go模块(感谢 @Zaba505)。
  • 2018-06-07 (v1.4.1) : 添加 NewDocumentFromReader 示例。
  • 2018-03-24 (v1.4.0) : 废弃 NewDocument(url)NewDocumentFromResponse(response)
  • 2018-01-28 (v1.3.0) : 在 Slice 中添加 ToEnd 常量,用于选择到末尾(感谢 @davidjwilkins 提出问题)。
  • 2018-01-11 (v1.2.0) : 添加 AddBack* 并废弃 AndSelf(感谢 @davidjwilkins)。
  • 2017-02-12 (v1.1.0) : 添加 SetHtmlSetText(感谢 @glebtv)。
  • 2016-12-29 (v1.0.2) : 优化 Selection.Text 的内存分配(感谢 @radovskyb)。
  • 2016-08-28 (v1.0.1) : 优化大型文档的性能。
  • 2016-07-27 (v1.0.0) : 标记1.0.0版本。
  • 2016-06-15 : 无效的选择器字符串内部编译为永不匹配任何节点的 Matcher 实现(而不是引发panic)。例如,doc.Find("~") 返回一个空的 *Selection 对象。
  • 2016-02-02 : 添加 NodeName 实用函数,类似于DOM的 nodeName 属性。它返回选择中第一个元素的标签名,以及非元素节点的其他相关值(详见文档)。添加 OuterHtml 实用函数,类似于DOM的 outerHTML 属性(命名为 OuterHtml,小写以保持与现有 Selection 上的 Html 方法一致)。
  • 2015-04-20 : 添加 AttrOr 辅助方法,返回属性值或缺失时的默认值。感谢 piotrkowalczuk
  • 2015-02-04 : 添加更多操作函数 - Prepend* - 再次感谢 Andrew Stone
  • 2014-11-28 : 添加更多操作函数 - ReplaceWith*、Wrap* 和 Unwrap - 再次感谢 Andrew Stone
  • 2014-11-07 : 添加操作函数(感谢 Andrew Stone)和 *Matcher 函数,接收已编译的cascadia选择器而非选择器字符串,从而避免goquery通过 cascadia.MustCompile 调用可能引发的panic。这提高了性能(选择器可以编译一次并重复使用)并使错误处理更符合惯例(您可以处理cascadia的编译错误,而不是从panic中恢复,这一直困扰着我)。注意,实际期望的类型是 Matcher 接口,cascadia.Selector 实现了该接口。可以使用其他匹配器实现。
  • 2014-11-06 : 将net/html的导入路径更改为golang.org/x/net/html(参见 https://groups.google.com/forum/#!topic/golang-nuts/eD8dh3T9yyA)。当您使用 html.Node 调用goquery时,请确保更新代码以使用新的导入路径。
  • v0.3.2 : 添加 NewDocumentFromReader()(感谢jweir),允许从io.Reader创建goquery文档。
  • v0.3.1 : 添加 NewDocumentFromResponse()(感谢assassingj),允许从http响应创建goquery文档。
  • v0.3.0 : 添加 EachWithBreak(),允许通过返回false来跳出 Each() 循环。添加此函数而不是更改现有的 Each() 以避免破坏兼容性。
  • v0.2.1 : 使其可通过go get获取,现在go.net/html与Go1.0兼容(感谢@matrixik指出这一点)。
  • v0.2.0 :在Slice()中添加对负索引的支持。重大变更 移除了Document.RootDocument现在本身就是一个Selection(单一选择,即根元素,与之前的Document.Root相同)。添加了jQuery的Closest()方法。
  • v0.1.1 :添加基准测试作为重构的基线,重构Next...()和Prev...()方法以使用新的html包的链表特性(Next/PrevSibling, FirstChild)。性能显著提升(在某些情况下提升40%以上)。
  • v0.1.0 :初始发布。

API

与jQuery不同,goquery不是作为DOM文档的一部分加载,也不会作用于包含它的文档。因此,goquery需要被告知要操作哪个HTML文档。这就是Document类型的用途。它持有根文档节点作为初始的Selection值进行操作。

goquery暴露了两个结构体DocumentSelection,以及Matcher接口。jQuery通常对同一个函数有多个变体(无参数、选择器字符串参数、jQuery对象参数、DOM元素参数等)。goquery没有使用带有可变空接口参数的单一方法来提供相同的功能,而是使用静态类型的签名,遵循以下命名约定:

  • 当jQuery等效函数可以无参数调用时,无参数签名与jQuery使用相同的名称(如Prev()),而带选择器字符串参数的版本称为XxxFiltered()(如PrevFiltered()
  • 当jQuery等效函数必须有一个参数时,选择器字符串版本使用与jQuery相同的名称(如Is()
  • jQuery中接受jQuery对象作为参数的签名在goquery中定义为XxxSelection(),并接受*Selection对象作为参数(如FilterSelection()
  • jQuery中接受DOM元素作为参数的签名在goquery中定义为XxxNodes(),并接受*html.Node类型的可变参数(如FilterNodes()
  • jQuery中接受函数作为参数的签名在goquery中定义为XxxFunction(),并接受一个函数作为参数(如FilterFunction()
  • 可以用选择器字符串调用的goquery方法有对应的版本,接受Matcher接口,定义为XxxMatcher()(如IsMatcher()

在jQuery中不存在但在Go中有用的实用函数被实现为函数(接受*Selection作为参数),以避免与*Selection的方法(保留用于jQuery等效行为)可能发生的命名冲突。

完整的包参考文档可以在这里找到

请注意,Cascadia的选择器不一定匹配jQuery(Sizzle)支持的所有选择器。详情请参见cascadia项目。此外,选择器的工作方式更像DOM的querySelectorAll,而不是jQuery的匹配器 - 它们没有上下文匹配的概念(关于这意味着什么的一些具体例子,请参见此票据)。实践中,这通常不太重要,但值得一提。无效的选择器字符串会编译成一个无法匹配任何节点的Matcher。接受选择器字符串作为参数的各种函数的行为遵循这一事实,例如(其中~是一个无效的选择器字符串):

  • Find("~")返回一个空选择,因为选择器字符串不匹配任何内容。
  • Add("~")返回一个新的选择,持有与原始选择相同的节点,因为它没有添加任何节点(选择器字符串不匹配任何内容)。
  • ParentsFiltered("~")返回一个空选择,因为选择器字符串不匹配任何内容。
  • ParentsUntil("~")返回选择的所有父元素,因为选择器字符串没有匹配到任何元素来停止在顶层元素之前。

示例

wiki中查看一些技巧和窍门。

改编自example_test.go:

package main

import (
  "fmt"
  "log"
  "net/http"

  "github.com/PuerkitoBio/goquery"
)

func ExampleScrape() {
  // 请求HTML页面
  res, err := http.Get("http://metalsucks.net")
  if err != nil {
    log.Fatal(err)
  }
  defer res.Body.Close()
  if res.StatusCode != 200 {
    log.Fatalf("状态码错误: %d %s", res.StatusCode, res.Status)
  }

  // 加载HTML文档
  doc, err := goquery.NewDocumentFromReader(res.Body)
  if err != nil {
    log.Fatal(err)
  }

  // 查找评论项目
  doc.Find(".left-content article .post-title").Each(func(i int, s *goquery.Selection) {
    // 对于找到的每个项目,获取标题
    title := s.Find("a").Text()
    fmt.Printf("评论 %d: %s\n", i, title)
  })
}

func main() {
  ExampleScrape()
}

相关项目

  • Goq,一个基于goquery和结构体标签的HTML反序列化和抓取库。
  • andybalholm/cascadia,goquery使用的CSS选择器库。
  • suntong/cascadia,cascadia CSS选择器库的命令行界面,用于测试选择器。
  • gocolly/colly,一个快速优雅的网络爬虫框架。
  • gnulnx/goperf,一个网站性能测试工具,也可以获取静态资源。
  • MontFerret/ferret,声明式网络爬虫。
  • tacusci/berrycms,一个现代简单易用的CMS,易于编写插件。
  • Dataflow kit,Go语言的Web爬虫框架。
  • Geziyor,一个快速的Go语言网络爬虫和抓取框架。支持JS渲染。
  • Pagser,一个简单、易用、可扩展、可配置的HTML解析器,基于goquery和结构体标签。
  • stitcherd,一个使用CSS选择器和DOM更新进行服务器端包含的服务器。
  • goskyr,一个易于配置的Go语言命令行爬虫。
  • goGetJS,一个用于提取、搜索和保存JavaScript文件的工具(可选无头浏览器)。
  • fitter,一个用于从JSON、XML、HTML和XPath格式页面中选择值的工具。
  • seltabl,一个类似ORM的包和支持语言服务器,用于从HTML中提取值。

支持

您可以通过以下几种方式支持本项目:

  • 使用它、为它加星、用它构建项目、传播它!
    • 如果您确实用它构建了开源或其他公开可见的项目,请告诉我,我可以将其添加到相关项目部分!
  • 提出问题以改进项目(注意:文档错别字和澄清也是问题!)
    • 在提出新问题之前请先搜索现有问题 - 可能已经有人提出过了。
  • 拉取请求:除非修复非常简单,否则请先在问题中讨论新代码。
    • 确保新代码经过测试。
    • 注意现有代码 - 破坏现有代码的PR很可能会被拒绝,除非它修复了一个严重问题。
  • 赞助开发者
    • 查看Github仓库顶部的赞助按钮
    • 或通过下方的BuyMeACoffee.com

Buy Me A Coffee

许可证

BSD 3-Clause许可证,与Go语言相同。Cascadia的许可证在这里

项目侧边栏1项目侧边栏2
推荐项目
Project Cover

豆包MarsCode

豆包 MarsCode 是一款革命性的编程助手,通过AI技术提供代码补全、单测生成、代码解释和智能问答等功能,支持100+编程语言,与主流编辑器无缝集成,显著提升开发效率和代码质量。

Project Cover

AI写歌

Suno AI是一个革命性的AI音乐创作平台,能在短短30秒内帮助用户创作出一首完整的歌曲。无论是寻找创作灵感还是需要快速制作音乐,Suno AI都是音乐爱好者和专业人士的理想选择。

Project Cover

有言AI

有言平台提供一站式AIGC视频创作解决方案,通过智能技术简化视频制作流程。无论是企业宣传还是个人分享,有言都能帮助用户快速、轻松地制作出专业级别的视频内容。

Project Cover

Kimi

Kimi AI助手提供多语言对话支持,能够阅读和理解用户上传的文件内容,解析网页信息,并结合搜索结果为用户提供详尽的答案。无论是日常咨询还是专业问题,Kimi都能以友好、专业的方式提供帮助。

Project Cover

阿里绘蛙

绘蛙是阿里巴巴集团推出的革命性AI电商营销平台。利用尖端人工智能技术,为商家提供一键生成商品图和营销文案的服务,显著提升内容创作效率和营销效果。适用于淘宝、天猫等电商平台,让商品第一时间被种草。

Project Cover

吐司

探索Tensor.Art平台的独特AI模型,免费访问各种图像生成与AI训练工具,从Stable Diffusion等基础模型开始,轻松实现创新图像生成。体验前沿的AI技术,推动个人和企业的创新发展。

Project Cover

SubCat字幕猫

SubCat字幕猫APP是一款创新的视频播放器,它将改变您观看视频的方式!SubCat结合了先进的人工智能技术,为您提供即时视频字幕翻译,无论是本地视频还是网络流媒体,让您轻松享受各种语言的内容。

Project Cover

美间AI

美间AI创意设计平台,利用前沿AI技术,为设计师和营销人员提供一站式设计解决方案。从智能海报到3D效果图,再到文案生成,美间让创意设计更简单、更高效。

Project Cover

AIWritePaper论文写作

AIWritePaper论文写作是一站式AI论文写作辅助工具,简化了选题、文献检索至论文撰写的整个过程。通过简单设定,平台可快速生成高质量论文大纲和全文,配合图表、参考文献等一应俱全,同时提供开题报告和答辩PPT等增值服务,保障数据安全,有效提升写作效率和论文质量。

投诉举报邮箱: service@vectorlightyear.com
@2024 懂AI·鲁ICP备2024100362号-6·鲁公网安备37021002001498号