• <fieldset id="8imwq"><menu id="8imwq"></menu></fieldset>
  • <bdo id="8imwq"><input id="8imwq"></input></bdo>
    最新文章專(zhuān)題視頻專(zhuān)題問(wèn)答1問(wèn)答10問(wèn)答100問(wèn)答1000問(wèn)答2000關(guān)鍵字專(zhuān)題1關(guān)鍵字專(zhuān)題50關(guān)鍵字專(zhuān)題500關(guān)鍵字專(zhuān)題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關(guān)鍵字專(zhuān)題關(guān)鍵字專(zhuān)題tag2tag3文章專(zhuān)題文章專(zhuān)題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專(zhuān)題3
    問(wèn)答文章1 問(wèn)答文章501 問(wèn)答文章1001 問(wèn)答文章1501 問(wèn)答文章2001 問(wèn)答文章2501 問(wèn)答文章3001 問(wèn)答文章3501 問(wèn)答文章4001 問(wèn)答文章4501 問(wèn)答文章5001 問(wèn)答文章5501 問(wèn)答文章6001 問(wèn)答文章6501 問(wèn)答文章7001 問(wèn)答文章7501 問(wèn)答文章8001 問(wèn)答文章8501 問(wèn)答文章9001 問(wèn)答文章9501
    當(dāng)前位置: 首頁(yè) - 科技 - 知識(shí)百科 - 正文

    zhihu-go源碼解析:用goquery解析HTML_html/css_WEB-ITnose

    來(lái)源:懂視網(wǎng) 責(zé)編:小采 時(shí)間:2020-11-27 16:38:39
    文檔

    zhihu-go源碼解析:用goquery解析HTML_html/css_WEB-ITnose

    zhihu-go源碼解析:用goquery解析HTML_html/css_WEB-ITnose:上一篇博客 簡(jiǎn)單介紹了 zhihu-go項(xiàng)目的緣起,本篇簡(jiǎn)單介紹一下關(guān)于處理 HTML 的細(xì)節(jié)。 因?yàn)橹鯖](méi)有開(kāi)發(fā) API,所以只能通過(guò)模擬瀏覽器操作的方式獲取數(shù)據(jù),這些數(shù)據(jù)有兩種格式:普通的 HTML 文檔和某些 Ajax 接口返回的 JSON(返回的數(shù)據(jù)實(shí)際上也是 H
    推薦度:
    導(dǎo)讀zhihu-go源碼解析:用goquery解析HTML_html/css_WEB-ITnose:上一篇博客 簡(jiǎn)單介紹了 zhihu-go項(xiàng)目的緣起,本篇簡(jiǎn)單介紹一下關(guān)于處理 HTML 的細(xì)節(jié)。 因?yàn)橹鯖](méi)有開(kāi)發(fā) API,所以只能通過(guò)模擬瀏覽器操作的方式獲取數(shù)據(jù),這些數(shù)據(jù)有兩種格式:普通的 HTML 文檔和某些 Ajax 接口返回的 JSON(返回的數(shù)據(jù)實(shí)際上也是 H

    上一篇博客 簡(jiǎn)單介紹了 zhihu-go項(xiàng)目的緣起,本篇簡(jiǎn)單介紹一下關(guān)于處理 HTML 的細(xì)節(jié)。

    因?yàn)橹鯖](méi)有開(kāi)發(fā) API,所以只能通過(guò)模擬瀏覽器操作的方式獲取數(shù)據(jù),這些數(shù)據(jù)有兩種格式:普通的 HTML 文檔和某些 Ajax 接口返回的 JSON(返回的數(shù)據(jù)實(shí)際上也是 HTML)。其實(shí)也就是爬蟲(chóng)了,抓取網(wǎng)頁(yè),然后提取數(shù)據(jù)。一般來(lái)說(shuō)從 HTML 文檔提取數(shù)據(jù)有這些做法:正則、XPath、CSS 選擇器等。對(duì)我來(lái)說(shuō),正則寫(xiě)起來(lái)比較復(fù)雜,代碼可讀性差而且維護(hù)起來(lái)麻煩;XPath 沒(méi)有詳細(xì)了解,不過(guò)用起來(lái)應(yīng)該不難,而且 Chrome 瀏覽器可以直接提取 XPath. zhihu-go 里用的是選擇器的方式,使用了 goquery.

    goquery 是 “a little like that j-thing, only in Go”,也就是用 jQuery 的方式去操作 DOM. jQuery 大家都很熟,API 也很簡(jiǎn)單明了。本文不詳細(xì)介紹 goquery,下面選幾個(gè)場(chǎng)景(API)講講在 zhihu-go 里的應(yīng)用。

    創(chuàng)建 Document 對(duì)象

    goquery 暴露了兩個(gè)結(jié)構(gòu)體: Document和 Selection. Document表示一個(gè) HTML 文檔, Selection用于像 jQuery 一樣操作,支持鏈?zhǔn)秸{(diào)用。goquery 需要指定一個(gè) HTML 文檔才能繼續(xù)后續(xù)的操作,有以下幾個(gè)構(gòu)造方式:

  • NewDocumentFromNode(root *html.Node) *Document: 傳入 *html.Node對(duì)象,也就是根節(jié)點(diǎn)。
  • NewDocument(url string) (*Document, error): 傳入 URL,內(nèi)部用 http.Get獲取網(wǎng)頁(yè)。
  • NewDocumentFromReader(r io.Reader) (*Document, error): 傳入 io.Reader,內(nèi)部從 reader 中讀取內(nèi)容并解析。
  • NewDocumentFromResponse(res *http.Response) (*Document, error): 傳入 HTTP 響應(yīng),內(nèi)部拿到 res.Body(實(shí)現(xiàn)了 io.Reader) 后的處理方式類(lèi)似 NewDocumentFromReader.
  • 因?yàn)橹醯捻?yè)面需要登錄才能訪問(wèn)(還需要偽造請(qǐng)求頭),而且我們并不想手動(dòng)解析 HTML 來(lái)獲取 *html.Node,最后用到了另外兩個(gè)構(gòu)造方法。大致的使用場(chǎng)景是:

  • 請(qǐng)求 HTML 頁(yè)面(如問(wèn)題頁(yè)面),調(diào)用 NewDocumentFromResponse
  • 請(qǐng)求 Ajax 接口,返回的 JSON 數(shù)據(jù)里是一些 HTML 片段,用 NewDocumentFromReader,其中 r = strings.NewReader(html)
  • 為了方便舉例說(shuō)明,下文采用這個(gè)定義: var doc *goquery.Document.

    查找到指定節(jié)點(diǎn)

    Selection有一系列類(lèi)似 jQuery 的方法, Document結(jié)構(gòu)體內(nèi)嵌了 *Selection,因此也能直接調(diào)用這些方法。主要的方法是 Selection.Find(selector string),傳入一個(gè)選擇器,返回一個(gè)新的,匹配到的 *Selection,所以能夠鏈?zhǔn)秸{(diào)用。

    比如在用戶主頁(yè)(如 黃繼新),要獲取用戶的 BIO. 首先用 Chrome 定位到對(duì)應(yīng)的 HTML:

    和知乎在一起

    對(duì)應(yīng)的 go 代碼就是:

    doc.Find("span.bio")

    如果一個(gè)選擇器對(duì)應(yīng)多個(gè)結(jié)果,可以使用 First(), Last(), Eq(index int), Slice(start, end int)這些方法進(jìn)一步定位。

    還是在用戶主頁(yè),在用戶資料欄的底下,從左往右展示了提問(wèn)數(shù)、回答數(shù)、文章數(shù)、收藏?cái)?shù)和公共編輯的次數(shù)。查看 HTML 源碼后發(fā)現(xiàn)這幾項(xiàng)的 class 是一樣的,所以只能通過(guò)下標(biāo)索引來(lái)區(qū)分。

    先看 HTML 源碼:

    提問(wèn)1336回答785文章91收藏44公共編輯51648

    如果要定位找到回答數(shù),對(duì)應(yīng)的 go 代碼是:

    doc.Find("div.profile-navbar").Find("span.num").Eq(1)

    屬性操作

    經(jīng)常需要獲取一個(gè)標(biāo)簽的內(nèi)容和某些屬性值,使用 goquery 可以很容易做到。

    繼續(xù)上面獲取回答數(shù)的例子,用 Text() string方法可以獲取標(biāo)簽內(nèi)的文本內(nèi)容,其中包含所有子標(biāo)簽。

    text := doc.Find("div.profile-navbar").Find("span.num").Eq(1).Text() // "785"

    需要注意的是, Text()方法返回的字符串,可能前后有很多空白字符,可以視情況做清除。

    獲取屬性值也很容易,有兩個(gè)方法:

  • Attr(attrName string) (val string, exists bool): 返回屬性值和該屬性是否存在,類(lèi)似從 map中取值
  • AttrOr(attrName, defaultValue string) string: 和上一個(gè)方法類(lèi)似,區(qū)別在于如果屬性不存在,則返回給定的默認(rèn)值
  • 常見(jiàn)的使用場(chǎng)景就是獲取一個(gè) a 標(biāo)簽的鏈接。繼續(xù)上面獲取回答的例子,如果想要得到用戶回答的主頁(yè),可以這么做:

    href, _ := doc.Find("div.profile-navbar").Find("a.item").Eq(1).Attr("href")

    還有其他設(shè)置屬性、操作 class 的方法,就不展開(kāi)討論了。

    迭代

    很多場(chǎng)景需要返回列表數(shù)據(jù),比如問(wèn)題的關(guān)注者列表、所有回答,某個(gè)答案的點(diǎn)贊的用戶列表等。這種情況下一般需要用到迭代,遍歷所有的同類(lèi)節(jié)點(diǎn),做某些操作。

    goquery 提供了三個(gè)用于迭代的方法,都接受一個(gè)匿名函數(shù)作為參數(shù):

  • Each(f func(int, *Selection)) *Selection: 其中函數(shù) f的第一個(gè)參數(shù)是當(dāng)前的下標(biāo),第二個(gè)參數(shù)是當(dāng)前的節(jié)點(diǎn)
  • EachWithBreak(f func(int, *Selection) bool) *Selection: 和 Each類(lèi)似,增加了中途跳出循環(huán)的能力,當(dāng) f返回 false時(shí)結(jié)束迭代
  • Map(f func(int, *Selection) string) (result []string): f的參數(shù)與上面一樣,返回一個(gè) string 類(lèi)型,最終返回 []string.
  • 比如獲取一個(gè)收藏夾(如 黃繼新的收藏:關(guān)于知乎的思考)下所有的問(wèn)題,可以這么做(見(jiàn) zhihu-go/collections.go):

    func getQuestionsFromDoc(doc *goquery.Document) []*Question {	questions := make([]*Question, 0, pageSize)	items := doc.Find("div#zh-list-answer-wrap").Find("h2.zm-item-title")	items.Each(func(index int, sel *goquery.Selection) {	a := sel.Find("a")	qTitle := strip(a.Text())	qHref, _ := a.Attr("href")	thisQuestion := NewQuestion(makeZhihuLink(qHref), qTitle)	questions = append(questions, thisQuestion)	})	return questions}

    EachWithBreak在 zhihu-go 中也有用到,可以參見(jiàn) Answer.GetVotersN 方法: zhihu-go/answer.go.

    刪除節(jié)點(diǎn)、插入 HTML、導(dǎo)出 HTML

    有一個(gè)需求是把回答內(nèi)容輸出到 HTML,說(shuō)白了其實(shí)就是修復(fù)和清洗 HTML,具體的細(xì)節(jié)可以看 answer.go 里的 answerSelectionToHtml 函數(shù). 其中用到了一些需要修改文檔的操作。

    比如,調(diào)用 Remove()方法把一個(gè)節(jié)點(diǎn)刪掉:

    sel.Find("noscript").Each(func(_ int, tag *goquery.Selection) { tag.Remove() // 把無(wú)用的 noscript 去掉})

    在節(jié)點(diǎn)后插入一段 HTML:

    sel.Find("img").Each(func(_ int, tag *goquery.Selection) { var src string if tag.HasClass("origin_image") { src, _ = tag.Attr("data-original") } else { src, _ = tag.Attr("data-actualsrc") } tag.SetAttr("src", src) if tag.Next().Size() == 0 { tag.AfterHtml("
    ") // 在 img 標(biāo)簽后插入一個(gè)換行 }})

    在標(biāo)簽尾部 append 一段內(nèi)容:

    wrapper := ``doc, _ := goquery.NewDocumentFromReader(strings.NewReader(wrapper))doc.Find("body").AppendSelection(sel)

    最終輸出為 html 文檔:

    html, err := doc.Html()

    總結(jié)

    上面的例子基本涵蓋了 zhihu-go 中關(guān)于 HTML 操作的場(chǎng)景,得益于 goquery 和 jQuery 的 API 風(fēng)格,實(shí)現(xiàn)起來(lái)還是非常簡(jiǎn)單的。

    聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

    文檔

    zhihu-go源碼解析:用goquery解析HTML_html/css_WEB-ITnose

    zhihu-go源碼解析:用goquery解析HTML_html/css_WEB-ITnose:上一篇博客 簡(jiǎn)單介紹了 zhihu-go項(xiàng)目的緣起,本篇簡(jiǎn)單介紹一下關(guān)于處理 HTML 的細(xì)節(jié)。 因?yàn)橹鯖](méi)有開(kāi)發(fā) API,所以只能通過(guò)模擬瀏覽器操作的方式獲取數(shù)據(jù),這些數(shù)據(jù)有兩種格式:普通的 HTML 文檔和某些 Ajax 接口返回的 JSON(返回的數(shù)據(jù)實(shí)際上也是 H
    推薦度:
    標(biāo)簽: 使用 知乎 解析
    • 熱門(mén)焦點(diǎn)

    最新推薦

    猜你喜歡

    熱門(mén)推薦

    專(zhuān)題
    Top
    主站蜘蛛池模板: 四虎国产精品免费久久5151| 亚洲第一永久AV网站久久精品男人的天堂AV | 欧美亚洲国产精品久久蜜芽| 亚洲国产综合精品一区在线播放| 欧美精品华人在线| 免费精品无码AV片在线观看| 久久久久成人精品无码| 国产成人亚洲精品影院| 久久99国产精品久久99| 国产精品99精品久久免费| 亚洲AV永久无码精品成人| 精品国产高清在线拍| 国产高清在线精品一区小说| 久久精品这里热有精品| 92国产精品午夜福利| 精品无码国产一区二区三区AV| 亚洲国产精品无码AAA片| 亚洲精品无码久久不卡| 免费精品精品国产欧美在线欧美高清免费一级在线 | 精品精品国产理论在线观看| 国产精品高清2021在线| 欧美精品一区二区精品久久| 大伊香蕉精品视频在线导航| 国产人妖乱国产精品人妖| 亚洲人精品午夜射精日韩| 久久99精品久久久久久9蜜桃| 国内精品久久久久久麻豆| 国产午夜精品一区二区三区小说 | 91精品一区国产高清在线| 国产精品美脚玉足脚交欧美| 久久精品国产亚洲av麻豆小说| 亚洲精品tv久久久久久久久| 亚洲欧美日韩久久精品第一区| 中文国产成人精品久久不卡| 亚洲国产精品VA在线观看麻豆| 亚洲精品国产品国语在线| 亚洲AV无码国产精品麻豆天美 | 亚洲精品麻豆av| 少妇人妻无码精品视频| 欧美精品黑人粗大免费| 成人区人妻精品一区二区不卡视频 |