go 语言爬虫库goQuery 的详细使用(知乎日报详情页解析示例)

news2025/1/11 7:56:20

上一篇《uniapp小程序开发 | 从零实现一款影视类app 》实现了影视小程序的前端和后台接口,虽然包含了大多数小程序应有的知识,但基本还只是涉及网络接口和vue页面的设计。这里介绍下零一个有趣的练手项目,知乎日报。涉及详情页面的html解析,使用了golang的goquery三方库。

前言

知乎日报这个案例很经典,有比较完整的API,很值得模仿学习。 也很简单,唯一需要注意的是日报详情的设计,因为知乎日报的详情接口,竟返回的是html,这.....

上一篇《uniapp小程序开发 | 从零实现一款影视类app 》链接:uniapp小程序开发 | 从零实现一款影视类app (后台接口实现,go-zero微服务的使用)_uniapp开源影视app-CSDN博客

知乎日报API

启动界面图片

http://news-at.zhihu.com/api/4/start-image/{size}
参数说明
size图片尺寸,格式:宽*高。例如: 768*1024

获取刚进入应用时的显示封面,可以根据传递的尺寸参数来获取适配用户屏幕的封面。

获取最新日报

http://news-at.zhihu.com/api/4/news/latest

返回的数据用于日报的首页列表,首页的结构有上下部分,上部分是图片滑动模块,用于展示热门日报,下部分是首页日报列表,以上接口返回的数据有热门日报和首页日报

获取日报详细api

http://news-at.zhihu.com/api/4/news/{id}

或者直接解析详情页:

https://daily.zhihu.com/story/9773139
参数说明
id日报id

在点击日报列表也的日报项时,需要跳转到日报详情页展示日报的具体信息,这个接口用来获取日报的展示封面和具体内容。

历史日报

http://news.at.zhihu.com/api/4/news/before/{date}
参数说明
date年月日格式时间yyyyMMdd,例如:20150903、20161202

这个接口也是用与首页列表的日报展示,但是不同的是此接口需要传一个日期参数,如20150804格式。获取最新日报接口只能获取当天的日报列表,如果需要获取前天或者更久之前的日报,则需要这个接口单独获取。

日报额外信息

http://news-at.zhihu.com/api/4/story-extra/{id}

参数说明
id日报id

在日报详情页面中,不仅要展示日报的内容,好需要额外获取此日报的评论数目和推荐人数等额外信息。

日报长评

http://news-at.zhihu.com/api/4/story/{id}/long-comments

参数说明
id日报id

日报的评论页面展示长评用到的接口

日报短评

http://news-at.zhihu.com/api/4/story/{id}/short-comments

参数说明
id日报id

日报的评论页面展示段评用到的接口

主题日报栏目列表

http://news-at.zhihu.com/api/4/themes

主页的侧边栏显示有主题日报的列表,需要通过这个接口获取主题日报栏目列表

主题日报具体内容列表

http://news-at.zhihu.com/api/4/theme/{themeId}

参数说明
themeId主题日报栏目id

在主页侧栏点击主题日报进入主题日报的内容页,需要展示此主题日报下的日报列表。

goquery介绍

GoQuery是专为Go(Golang)语言设计的一个强大的HTML解析和查询库。它模仿了jQuery的API风格,使得在Go中处理HTML文档变得简单且直观。

GoQuery主要用于网页抓取(Web Scraping),能够通过CSS选择器来定位、遍历和操作HTML元素。你可以使用它来提取网页中的特定数据、修改DOM结构或进行其他与HTML文档相关的操作。

项目地址:GitHub - PuerkitoBio/goquery: A little like that j-thing, only in Go.

goquery使用

$ go get github.com/PuerkitoBio/goquery

简单示例

package main

import (
	"fmt"
	"log"

	"github.com/PuerkitoBio/goquery"
)

func main() {
	if err := run(); err != nil {
		log.Fatal(err)
	}
}

func run() error {
	doc, err := goquery.NewDocument("http://example.com")
	if err != nil {
		return fmt.Errorf("failed to load document: %w", err)
	}

	doc.Find("a").Each(func(i int, s *goquery.Selection) {
		href, exists := s.Attr("href")
		if exists {
			fmt.Println(href)
		} else {
			fmt.Println(s.Text())
		}
	})

	return nil
}

知乎详情页解析

知乎日报详情页面:知乎日报 - 知乎 

 页面对应的html代码片段:

<html>
<body>
	
<p><strong>语言的疙瘩,就是思想上的疙瘩</strong></p>

<p>“靠嘴写作”的作家不少,王朔就是其一。去年他出了新书,受访时有个细节,说有段时间嗓子不舒服,便没写。乍一看挺蒙:如今写东西还得比吆喝了?</p>
<p>后来看了他的自序,才知道嗓子的用途。他是“拿口语写作的作者,检查文字也须拿口语来回溜,没磕啵儿,才觉得通顺”。</p>

<p>言下之意,<strong>朗读修改不只是勘误这么简单,本身就是创作方式。</strong>作家老舍将朗读文章比作拉胡琴定弦,声音不对马上调整,免得一音毁全曲。</p>

<blockquote>嘴里念,耳朵听,我们会立刻听出文字的毛病来:有的句子太长了,应当改短;有的句子念着绕嘴,必是音节或字眼安排得不对劲,要设法调换修正;有的句子意思好,可是念起来不嘹亮,不干脆,听着不起劲,这必是句子的结构还欠妥当,或某几个字不大现成,应当再加工。一个好句子念起来嘴舒服,耳朵舒服,心里也舒服。<br>——老舍</blockquote>

<p>叶圣陶说得更直接——<strong>“语言的任何疙瘩,也就是思想上的疙瘩。”</strong>我认为很对,句子写得弯弯绕,归根结底是没有想清楚重点,虽极力表达而不得要领,读之如堕云雾,这就不是改几个错别字那么简单了。</p>

<figure><img class="content-image" src="https://pic1.zhimg.com/v2-b74ec227f579df9b7e41fd42b02269ee_720w.jpg?source=8673f162" alt=""><figcaption>Photo by Thirdman</figcaption></figure>

<p><strong>行文优化三步走</strong></p>

<p>用好"朗读修改法"有很多角度和层次。从对象来说,可以自己读给自己听,自己读给别人听,或者别人读给自己听;从功能来看,能思考立意,检视材料,还能梳理逻辑……</p>
<p>不过,从效果上看,<strong>朗读还是最利于优化行文。</strong>汪曾祺曾说:"写小说就是写语言。"让朗读聚焦于语言自然更直接显著。由浅及深,我们可以通过“读”透三个层次来锤炼字句。</p>

<p><strong><strong>第一层:文病</strong></strong></p>

</body>
</html>

如何区分p标签和紧挨着的strong标签? 

区分<p>标签和紧挨着的<strong>标签,可以使用goquery的Each函数来逐个处理每个匹配的元素,并根据需要进行逻辑判断。以下是一个示例,演示如何区分和处理这两种标签:

package main

import (
	"fmt"
	"strings"

	"github.com/PuerkitoBio/goquery"
)

func main() {
	html := `<html><body>
	<p><strong>语言的疙瘩,就是思想上的疙瘩</strong></p>
	<p>“靠嘴写作”的作家不少,王朔就是其一。去年他出了新书,受访时有个细节,说有段时间嗓子不舒服,便没写。乍一看挺蒙:如今写东西还得比吆喝了?</p>
	<p>后来看了他的自序,才知道嗓子的用途。他是“拿口语写作的作者,检查文字也须拿口语来回溜,没磕啵儿,才觉得通顺”。</p>
	<p>言下之意,<strong>内容</strong></p>
	</body></html>`

	doc, err := goquery.NewDocumentFromReader(strings.NewReader(html))
	if err != nil {
		fmt.Println("Error loading HTML:", err)
		return
	}

	var prevTag string
	doc.Find("p, strong").Each(func(i int, s *goquery.Selection) {
		tagName := s.Get(0).Data
		if tagName == "p" {
			prevTag = "p"
			fmt.Println("p tag:", s.Text())
		} else if tagName == "strong" && prevTag == "p" {
			fmt.Println("strong tag:", s.Text())
		}
	})
}

在上面的代码中,定义一个prevTag变量来跟踪前一个处理过的标签,然后在Each函数中根据标签类型执行不同的操作。当遍历到<p>标签时,打印出其文本内容并将prevTag设置为"p",然后当遍历到紧挨着的<strong>标签时,检查前一个处理过的标签类型,如果是<p>,则打印出<strong>标签的内容。

完整解析代码:

func (l *ZhiDetailLogic) ZhiDetail(req *types.ZhiDetailReq) (resp *types.ZhiDetailResp, err error) {
	// todo: add your logic here and delete this line
	url := "https://daily.zhihu.com/story/" + req.Id
	res, err_ := httpc.Do(l.ctx, http.MethodGet, url, nil)
	if err_ != nil {
		l.Error(err_)
		return nil, err_
	}
	defer res.Body.Close()
	// Load the HTML document
	doc, err := goquery.NewDocumentFromReader(res.Body)
	if err != nil {
		l.Error(err)
	}
	//var zhi types.CtItem
	var responseData []types.CtItem
	title := doc.Find(".DailyHeader-title").Text()
	image, _ := doc.Find(".DailyHeader-image").Find("img").Attr("src")
	author := ""
	avatar := ""
	bio := ""
	doc.Find(".meta").Each(func(i int, s *goquery.Selection) {
		author = s.Find("span[class=author]").Text()
		l.Debugf("author:", author)
		bio = s.Find("span[class=bio]").Text()
		avatar, _ = s.Find("img[class=avatar]").Attr("src")
	})

	// 遍历.content下的所有子节点
	doc.Find(".content *").Each(func(i int, s *goquery.Selection) {
		var itm types.CtItem
		// 判断节点类型并相应处理
		tagName := s.Get(0).DataAtom.String()
		switch tagName {
		case "p":
			// 在p内部查找strong标签
			strong := s.Find("strong")
			if strong.Length() == 1 {
				itm.Types = "strong"
				itm.Value = s.Text()
				responseData = append(responseData, itm)
			} else {
				itm.Types = "p"
				itm.Value = s.Text()
				responseData = append(responseData, itm)
			}
		case "li":
			itm.Types = "li"
			itm.Value = s.Text()
			responseData = append(responseData, itm)
		case "figure":
			// 在figure内部查找img标签
			img := s.Find("img")
			if img.Length() > 0 {
				src, _ := img.Attr("src")
				itm.Types = "img"
				itm.Value = src
				responseData = append(responseData, itm)
			}
		}
	})

	resp = &types.ZhiDetailResp{
		Code:    0,
		Message: res.Status,
		Content: responseData,
		Title:   title,
		Author:  author,
		Bio:     bio,
		Avatar:  avatar,
		Image:   image,
	}
	return
}

其他资源

go 语言爬虫库goquery的具体使用_Golang_脚本之家

goquery: 快速高效的HTML解析库-CSDN博客

Golang学习日志 ━━ goQuery 的使用-CSDN博客

https://www.51cto.com/article/785226.html

Go语言colly框架的快速入门_Golang_脚本之家

https://news-at.zhihu.com/api/7/news/9773139

go-zero超强工具goctl的常用命令api,rpc,model及其构建的服务解析-CSDN博客

go-zero 缩短从需求到上线的距离

GitHub - cwuom/MusicHelper: 一个通过Python编写的QQ、网易云音乐无损音乐爬取工具

https://www.cnblogs.com/oopsguy/p/5968447.html

GitHub - oopsguy/wechat-miniprogram-examples: WeChat mini program examples. 微信小程序示例 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1836764.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MYSQL无法启动的修复过程

记录一次MySQ无法启动的修复过程。 1. 错误表现 今天在用python操作数据库时可能有些错误&#xff08;具体来说就是我尝试创建了一个已经存在的database&#xff09;&#xff0c;结果我发现MySQL中的那个database不存在了&#xff0c;我重启了一下电脑&#xff0c;结果mysql…

移植案例与原理 - build lite配置目录全梳理

命令行工具hb(HarmonyOS|OpenHarmony Build 编译构建系统的缩写)都很熟悉了。这是一个基于gn和ninja的构建系统&#xff0c;以支持OpenHarmony组件化开发为目标&#xff0c;提供以下基本功能&#xff1a; 支持按组件拼装产品并编译。 独立构建芯片解决方案厂商源码。 独立构建…

【AI基础】租用云GPU之autoDL部署大模型ollama+llama3

在这个显卡昂贵的年代&#xff0c;很多想要尝试一下AI的人可能都止步于第一步。这个时候我们可以租用在线的GPU资源来使用AI。autoDL就是这样的一个云平台。 一、创建服务器 1.1 注册账号 官网&#xff1a;https://www.autodl.com/ | 租GPU就上AutoDL 帮助文档&#xff1a;…

6月18日(周二)A股行总结:A股震荡收涨,车路云概念全日强势,10年、30年国债期货齐创新高

车路云概念股发力上涨&#xff0c;中海达、华铭智能等多股20CM涨停。半导体板块走强&#xff0c;中芯国际港股上涨近&#xff13;% 。白酒板块下跌&#xff0c;贵州茅台跌1.3% 。30年期及10年期国债期货主力合约均创上市以来新高。 周二&#xff0c;A股全日窄幅震荡 沪指收涨0…

项目3:从0开始的RPC框架(扩展版)-2

六. 自定义协议 1. 需求分析 在目前的RPC框架中&#xff0c;采用Vert.x的HttpServer作为服务提供者的Web服务器&#xff0c;底层使用HTTP协议进行网络传输。但HTTP协议只是RPC框架网络传输的一种可选方式&#xff0c;还有其它更好的选择。 RPC框架注重性能&#xff0c;但HTT…

SpringBoot配置第三方专业缓存框架j2cache

j2cache的使用 这不是一个缓存 这是一个缓存框架 J2Cache, 也称为Java Cache或JSR-107&#xff0c;是一个用于缓存管理的标准API&#xff0c;它允许开发者在Java应用程序中实现分布式、基于内存的缓存。J2Cache主要通过javax.cache.Cache接口提供功能&#xff0c;用于存储和…

利用C#和Snap7工具模拟S7通信(包含DB地址讲解)

之前写过一篇用KepServerEx做模拟S7的通信数据&#xff0c;参考链接&#xff1a; 通过C#和KepServer完成模拟S7协议通信_c# 与kepserver-CSDN博客 但KepServerEx是收费的&#xff0c;而且模拟的DB块超过64就不行了&#xff0c;当然Snap7在本文中也是只能模拟DB1、DB2和DB3的数…

npm发布自己的插件包:新手教程

npm&#xff08;Node Package Manager&#xff09;是Node.js的包管理工具&#xff0c;广泛用于JavaScript项目中。本文将为你介绍如何从零开始发布一个npm插件包。 前提条件 在开始之前&#xff0c;你需要确保以下几点&#xff1a; 安装Node.js和npm&#xff1a;你可以在No…

python库离线安装方法(pyqt5离线安装方法)

在某些情况下&#xff0c;我们的计算机是无法联网的。 网上大部分方法&#xff1a; 这些方法都有个问题&#xff0c;就是库是需要依赖其它库的&#xff0c;你不知道它需要依赖什么库&#xff0c;就是提供了依赖库的列表也麻烦&#xff0c;依赖库也是有对应版本要求的&#xf…

NVIDIA新模型Nemotron-4:98%的训练数据是合成生成的,你敢信?

获取本文论文原文PDF&#xff0c;请公众号 AI论文解读 留言&#xff1a;论文解读 标题&#xff1a;Nemotron-4 340B Technical Report 模型概述&#xff1a;Nemotron-4 340B系列模型的基本构成 Nemotron-4 340B系列模型包括三个主要版本&#xff1a;Nemotron-4-340B-Base、…

zookeeper学习、配置文件参数详解

zookeeper学习、配置文件参数详解 zookeeper 配置文件参数详解tickTime 、session 的过期时间、maxSessionTimeout 三者之间的关系initLimit&#xff0c;syncLimit什么区别minSessionTimeout 默认值,**他的单位是ms** zookeeper 配置文件参数详解 ZooKeeper 是一个分布式协调服…

字节豆包大模型API吞吐、函数调用能力、长上下文能力测试总结

离开模型能力谈API价格都是耍流氓&#xff0c;豆包大模型作为API最便宜的模型之一&#xff0c;最近向个人开发者开放了&#xff0c;花了300元和一些时间对模型的API吞吐、函数调用能力、长上下文能力等进行了深度测试&#xff0c;看看它的能力究竟适合做 AI 应用开发吗&#xf…

Study--Oracle-03-数据库常规操作

一路走来&#xff0c;所有遇到的人&#xff0c;帮助过我的、伤害过我的都是朋友&#xff0c;没有一个是敌人。 一、oracle 版本及主要功能 二、数据安装完成后常用操作SQL 1、检查数据库监听状态 监听的常用命令 启动&#xff1a;[oracleoracle u01]$ lsnrctl stop 停止&am…

【车载音视频AI电脑】全国产海事船载视频监控系统解决方案

海事船载视频监控系统解决方案针对我国快速发展的内河航运、沿海航运和远洋航运中存在的航行安全和航运监管难题&#xff0c;为船舶运营方、政府监管部门提供一套集视频采集、存储、回放调阅为一体的视频监控系统&#xff0c;对中大型船舶运行中的内部重要部位情况和外部环境进…

Zabbix+Garafana监控部署

ZabbixGarafana监控部署 一、IP规划 服务器IP备注zabbix-server192.168.100.128zabbix服务端Garafanazabbix-mysql192.168.100.130数据库zabbix-client192.168.100.132zabbix客户端 二、zabbix-server安装zabbix ​ 配置IP地址为&#xff1a;192.168.100.128 ​ 关闭防火墙…

Linux文件与内容查阅、归档压缩

#Linux系统基础 文件与内容查找、归档压缩 find命令搜索文件 grep对文件的内容进行搜索 tar命令进行文件的压缩与解压缩 一、文件的查找 格式实例&#xff1a;find . -name "123.txt"说明find起始目录查找类型查找条件./ 或 .-name“123.txt”在当前目录中查找…

[保姆级教程]uniapp实现页面路由配置

文章目录 新建目录新建页面配置页面路由修改tabBar地址其他&#xff1a;在package.json中的pages配置详细 新建目录 先点击src–》新建–》目录 输入名称&#xff0c;并以此类推完成所有新建目录 新建页面 右击目录&#xff0c;点击新建–》vue文件 弹出弹框&#xff0c;…

“首秀”欧洲杯,海信冰箱欧洲市占率居国产品牌首位

随着欧洲杯的火热开赛&#xff0c;挑灯夜战、观看球赛的时刻已经来临。此时&#xff0c;你需要何物相伴&#xff1f;是打开冰箱&#xff0c;取出真空腌制的食材&#xff0c;亲手烹饪一场观赛盛宴&#xff1f;还是取出极致保鲜的荔枝、樱桃&#xff0c;一边观赛一边品味&#xf…

failed to create network xxxx: Error response from daemon

问题描述&#xff1a; 启动项目时&#xff0c;docker内部网络冲突。 解决方案&#xff1a; 1.删除所有docker容器&#xff08;强制删除一个或多个容器&#xff0c;即使它们正在运行&#xff09; docker rm -f $(docker ps -aq) 2.验证docker容器是否删除成功 docker ps --…

HuggingFace烧钱做了一大批实验,揭示多模态大模型哪些trick真正有效

构建多模态大模型时有很多有效的trick&#xff0c;如采用交叉注意力机制融合图像信息到语言模型中&#xff0c;或直接将图像隐藏状态序列与文本嵌入序列结合输入至语言模型。 但是这些trick为什么有效&#xff0c;其计算效率如何&#xff0c;往往解释得很粗略或者或者缺乏充分…