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


    上一篇《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

    简单示例

    1. package main
    2. import (
    3. "fmt"
    4. "log"
    5. "github.com/PuerkitoBio/goquery"
    6. )
    7. func main() {
    8. if err := run(); err != nil {
    9. log.Fatal(err)
    10. }
    11. }
    12. func run() error {
    13. doc, err := goquery.NewDocument("http://example.com")
    14. if err != nil {
    15. return fmt.Errorf("failed to load document: %w", err)
    16. }
    17. doc.Find("a").Each(func(i int, s *goquery.Selection) {
    18. href, exists := s.Attr("href")
    19. if exists {
    20. fmt.Println(href)
    21. } else {
    22. fmt.Println(s.Text())
    23. }
    24. })
    25. return nil
    26. }

    知乎详情页解析

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

     页面对应的html代码片段:

    1. <html>
    2. <body>
    3. <p><strong>语言的疙瘩,就是思想上的疙瘩strong>p>
    4. <p>“靠嘴写作”的作家不少,王朔就是其一。去年他出了新书,受访时有个细节,说有段时间嗓子不舒服,便没写。乍一看挺蒙:如今写东西还得比吆喝了?p>
    5. <p>后来看了他的自序,才知道嗓子的用途。他是“拿口语写作的作者,检查文字也须拿口语来回溜,没磕啵儿,才觉得通顺”。p>
    6. <p>言下之意,<strong>朗读修改不只是勘误这么简单,本身就是创作方式。strong>作家老舍将朗读文章比作拉胡琴定弦,声音不对马上调整,免得一音毁全曲。p>
    7. <blockquote>嘴里念,耳朵听,我们会立刻听出文字的毛病来:有的句子太长了,应当改短;有的句子念着绕嘴,必是音节或字眼安排得不对劲,要设法调换修正;有的句子意思好,可是念起来不嘹亮,不干脆,听着不起劲,这必是句子的结构还欠妥当,或某几个字不大现成,应当再加工。一个好句子念起来嘴舒服,耳朵舒服,心里也舒服。<br>——老舍blockquote>
    8. <p>叶圣陶说得更直接——<strong>“语言的任何疙瘩,也就是思想上的疙瘩。”strong>我认为很对,句子写得弯弯绕,归根结底是没有想清楚重点,虽极力表达而不得要领,读之如堕云雾,这就不是改几个错别字那么简单了。p>
    9. <figure><img class="content-image" src="https://pic1.zhimg.com/v2-b74ec227f579df9b7e41fd42b02269ee_720w.jpg?source=8673f162" alt=""><figcaption>Photo by Thirdmanfigcaption>figure>
    10. <p><strong>行文优化三步走strong>p>
    11. <p>用好"朗读修改法"有很多角度和层次。从对象来说,可以自己读给自己听,自己读给别人听,或者别人读给自己听;从功能来看,能思考立意,检视材料,还能梳理逻辑……p>
    12. <p>不过,从效果上看,<strong>朗读还是最利于优化行文。strong>汪曾祺曾说:"写小说就是写语言。"让朗读聚焦于语言自然更直接显著。由浅及深,我们可以通过“读”透三个层次来锤炼字句。p>
    13. <p><strong><strong>第一层:文病strong>strong>p>
    14. body>
    15. html>

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

    区分

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

    1. package main
    2. import (
    3. "fmt"
    4. "strings"
    5. "github.com/PuerkitoBio/goquery"
    6. )
    7. func main() {
    8. html := `
    9. 语言的疙瘩,就是思想上的疙瘩

    10. “靠嘴写作”的作家不少,王朔就是其一。去年他出了新书,受访时有个细节,说有段时间嗓子不舒服,便没写。乍一看挺蒙:如今写东西还得比吆喝了?

    11. 后来看了他的自序,才知道嗓子的用途。他是“拿口语写作的作者,检查文字也须拿口语来回溜,没磕啵儿,才觉得通顺”。

    12. 言下之意,内容

    13. `
    14. doc, err := goquery.NewDocumentFromReader(strings.NewReader(html))
    15. if err != nil {
    16. fmt.Println("Error loading HTML:", err)
    17. return
    18. }
    19. var prevTag string
    20. doc.Find("p, strong").Each(func(i int, s *goquery.Selection) {
    21. tagName := s.Get(0).Data
    22. if tagName == "p" {
    23. prevTag = "p"
    24. fmt.Println("p tag:", s.Text())
    25. } else if tagName == "strong" && prevTag == "p" {
    26. fmt.Println("strong tag:", s.Text())
    27. }
    28. })
    29. }

    在上面的代码中,定义一个prevTag变量来跟踪前一个处理过的标签,然后在Each函数中根据标签类型执行不同的操作。当遍历到

    标签时,打印出其文本内容并将prevTag设置为"p",然后当遍历到紧挨着的标签时,检查前一个处理过的标签类型,如果是

    ,则打印出标签的内容。

    完整解析代码:

    1. func (l *ZhiDetailLogic) ZhiDetail(req *types.ZhiDetailReq) (resp *types.ZhiDetailResp, err error) {
    2. // todo: add your logic here and delete this line
    3. url := "https://daily.zhihu.com/story/" + req.Id
    4. res, err_ := httpc.Do(l.ctx, http.MethodGet, url, nil)
    5. if err_ != nil {
    6. l.Error(err_)
    7. return nil, err_
    8. }
    9. defer res.Body.Close()
    10. // Load the HTML document
    11. doc, err := goquery.NewDocumentFromReader(res.Body)
    12. if err != nil {
    13. l.Error(err)
    14. }
    15. //var zhi types.CtItem
    16. var responseData []types.CtItem
    17. title := doc.Find(".DailyHeader-title").Text()
    18. image, _ := doc.Find(".DailyHeader-image").Find("img").Attr("src")
    19. author := ""
    20. avatar := ""
    21. bio := ""
    22. doc.Find(".meta").Each(func(i int, s *goquery.Selection) {
    23. author = s.Find("span[class=author]").Text()
    24. l.Debugf("author:", author)
    25. bio = s.Find("span[class=bio]").Text()
    26. avatar, _ = s.Find("img[class=avatar]").Attr("src")
    27. })
    28. // 遍历.content下的所有子节点
    29. doc.Find(".content *").Each(func(i int, s *goquery.Selection) {
    30. var itm types.CtItem
    31. // 判断节点类型并相应处理
    32. tagName := s.Get(0).DataAtom.String()
    33. switch tagName {
    34. case "p":
    35. // 在p内部查找strong标签
    36. strong := s.Find("strong")
    37. if strong.Length() == 1 {
    38. itm.Types = "strong"
    39. itm.Value = s.Text()
    40. responseData = append(responseData, itm)
    41. } else {
    42. itm.Types = "p"
    43. itm.Value = s.Text()
    44. responseData = append(responseData, itm)
    45. }
    46. case "li":
    47. itm.Types = "li"
    48. itm.Value = s.Text()
    49. responseData = append(responseData, itm)
    50. case "figure":
    51. // 在figure内部查找img标签
    52. img := s.Find("img")
    53. if img.Length() > 0 {
    54. src, _ := img.Attr("src")
    55. itm.Types = "img"
    56. itm.Value = src
    57. responseData = append(responseData, itm)
    58. }
    59. }
    60. })
    61. resp = &types.ZhiDetailResp{
    62. Code: 0,
    63. Message: res.Status,
    64. Content: responseData,
    65. Title: title,
    66. Author: author,
    67. Bio: bio,
    68. Avatar: avatar,
    69. Image: image,
    70. }
    71. return
    72. }

    其他资源

    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. 微信小程序示例 

  • 相关阅读:
    纯js实现在线文字识别,从图片中提取文本信息
    【译】Visual Studio 2013 退役 :旧版本 Visual Studio 的支持提醒
    (5)打印nXn方阵
    MVC、MVP、MVVM理解 在什么情况下用什么页面架构
    从北京到南京:偶数在能源行业的数据迁移实践
    第十六章 协程
    GCC内建函数C语言实现__builtin_popcountll,__builtin_ctzll,__buitlin_clzll
    ssl证书申请流程
    Linux打包和使用动静态库
    vue2实例
  • 原文地址:https://blog.csdn.net/qq8864/article/details/139774361