goquery

calong發表於2020-08-02

作者:Calong

本文參考 飛雪無痕 的 《golang goquery selector(選擇器) 示例大全》

概述

相信很多小夥伴對爬蟲有著很大的興趣,今天我們就來說下go語言的爬蟲利器goquery,有使用過jquery的小夥伴是不是看著這個很眼熟?goquery類似jquery,它是jquery的go版本實現,使用它,可以很方便的對HTML進行處理。

goquery是基於 Go net/htm包和css選擇器庫 cascadia。由於net/htm解析器返回的是DOM節點,而不是完整的DOM樹,因此, jQuery的狀態操作函式沒有實現(像height(), css(), detach)

注意:goquery只支援utf-8編碼,其他編碼需要轉換。

image

安裝
在終端輸入命令安裝goquery。

go get github.com/PuerkitoBio/goquery

簡單使用
我們首先通過一個小例子來介紹goqery。

func main() {
    html := `<html>
            <body>
                <h1 id="title">春曉</h1>
                <p class="content1">
                春眠不覺曉,
                處處聞啼鳥。
                夜來風雨聲,
                花落知多少。
                </p>
            </body>
            </html>
            `
dom,err:=goquery.NewDocumentFromReader(strings.NewReader(html))
    if err!=nil{
        log.Fatalln(err)
    }

    dom.Find("p").Each(func(i int, selection *goquery.Selection) {
        fmt.Println(selection.Text())
    })

得到結果:

春眠不覺曉,
處處聞啼鳥。
夜來風雨聲,
花落知多少。

NewDocumentFromReader()返回了一個*Document和error。Document代表一個將要被操作的HTML文件。

Find()是獲取當前匹配元素集中每個元素的子代,引數是x選擇器 ,它返回一個包含這些匹配元素的新選擇物件。在例子中我們使用的是元素選擇器P,它會幫我們匹配出所有的p標籤 。

Each()是迭代器,它會迴圈遍歷選擇的節點,它的引數是一個匿名函式,匿名函式擁有2個引數,一個是元素的索引位置,還有一個就是選擇的結果集匹配到的內容都在它的裡面。

Text()則是獲取匹配元素集中的文字內容。

選擇器

上面的例子中,我們使用了元素選擇器,goquery跟jquery一樣都支援很多選擇器,我們簡單的介紹下常用的選擇器:

基於HTML Element 元素的選擇器
就是基於a,p等這些HTML的基本元素進行選擇。
使用方法 :使用語法為 dom.Find(“p”),匹配文件中所有的p標籤。

ID 選擇器
ID選擇器是我們使用最頻繁的,假如我們有2個p元素,其實我們只需要其中的一個,那麼我們只需要給這個標記一個唯一的id即可,這樣我們就可以使用id選擇器,精確定位了。
使用方法 :id選擇器以#開頭,緊跟著元素id的值,使用語法為
dom.Find(“#title”) ,匹配文件中所有的 id=title的內容

如果多個標籤的ID都是title,我們可以指定某一個標籤,如dom.Find(“p#title”)

Class選擇器
類選擇跟ID選擇器一樣都是使用很頻繁的,我們可以通過類選擇器快速篩選到需要的內容。

使用方法 : id選擇器以.開頭,緊跟著元素class的值,使用語法為dom.Find(“.content1”),匹配文件中所有的 id=title的元素。

類選擇權器跟ID選擇器一樣,也可以指定某一個標籤dom.Find(“div.content1”)

屬性選擇器
一個HTML元素都有自己的屬性以及屬性值,所以我們也可以通過屬性和值篩選元素。

使用方法 :我們可以通過元素的屬性和屬性值來篩選資料,使用語法為dom.Find(“p[class=content1],匹配文件中所有的 p標籤的class屬性是content1的元素。

當然我們這裡以class屬性為例,還可以用其他屬性,比如href等很多,自定義屬性也是可以的。

剛剛我們使用的是完全相等的匹配方式,屬性選擇器還要很多匹配方式。

選擇器 說明

Find(“div[my=zh]“) 篩選含有my屬性的div元素
Find(“div[my!=zh]“) 篩選my屬性不等於zh的div元素
Find(“div[my¦=zh]“) 篩選my屬性為zh或者zh-開頭的div元素
Find(“div[my*=zh]“) 篩選my屬性包含zh這個字串的div元素
Find(“div[my~=zh]“) 篩選my屬性包含zh這個單詞的div元素,單詞以空格分開的
Find(“div[my$=zh]“) 篩選my屬性以zh結尾的div元素,區分大小寫
Find(“div[my^=zh]“) 篩選my屬性以zh開頭的div元素,區分大小寫

parent > child選擇器

篩選出某個元素下的子元素。

使用方法:使用>符號連線,使用語法 dom.Find(“div>p”) , 篩選div標籤下的p標籤

element + next 相鄰選擇器

如果要篩選的元素沒有規律,但是該元素的上一個元素有規律,我們就可以使用這種下一個相鄰選擇器來進行選擇。

如:

<div>
<p my="a">a</p>
<p>b</p>
<p>c</p>
<div>

我想篩選出b所在的標籤

使用方法:dom.Find(“p[my=a]+p”)篩選出p標籤屬性my的值為a的相鄰p標籤。

element~next 兄弟選擇器
有時候我們需要篩選同一父元素下,不相鄰的標籤,可以使用兄弟選擇器

比如我想篩選出 b 和c 所在標籤
使用方法:dom.Find(“p[my=a]~p”),篩選出p標籤屬性my的值為a的兄弟p標籤。

過濾器
有時候我們選擇出來的結果,並不是我們心目中的最優結果,我們希望對其進行過濾。

:contains過濾器

篩選出的元素要包含指定的文字,比如我想篩選出包含a的p標籤

使用方法:dom.Find(“p:contains(a)”),篩選出內容包含a的p標籤

Find(“:has(selector)”)和contains差不多,只不過這個是包含的是元素節點。

此外還有Find(“:empty”)表示篩選出的元素都不能有子元素(包括文字元素),只篩選那些不包含任何子元素的元素。

:first-child過濾器 和:first-of-type過濾器
篩選出的元素要是他們的父元素的第一個子元素,如果不是,則不會被篩選出來。
使用方法:語法為Find(“p:first-child”),篩選出第一個p標籤

:first-child選擇器限制的比較死,必須得是第一個子元素,如果該元素前有其他在前面,就不能用:first-child了,這時候:first-of-type就派上用場了,它要求只要是這個型別的第一個就可以。

:last-child 和:last-of-type過濾器
這兩個正好和上面的:first-child、:first-of-type相反

:nth-child(n) 過濾器

篩選出的元素是其父元素的第n個元素,n以1開始。所以我們可以知道:first-child和:nth-child(1)是相等的。通過指定n,我們就很靈活的篩選出我們需要的元素。

:nth-of-type(n) 過濾器
:nth-of-type(n)和 :nth-child(n) 類似,只不過它表示的是同型別元素的第n個,所以:nth-of-type(1) 和 :first-of-type是相等的。

:nth-last-child(n) 和:nth-last-of-type(n) 過濾器
這兩個和上面的類似,只不過是倒序開始計算的,最後一個元素被當成了第一個

:only-child 過濾器 和 :only-of-type 過濾器
篩選出父元素中,只有它自己的一個的元素。

常用方法
類似函式的位置操作
Find(selection) *Selection //根據選擇器查詢節點集
Eq(index int) *Selection //根據索引獲取某個節點集
First() *Selection //獲取第一個子節點集
Last() *Selection //獲取最後一個子節點集
Next() *Selection //獲取下一個兄弟節點集
NextAll() *Selection //獲取後面所有兄弟節點集
Prev() *Selection //前一個兄弟節點集
Get(index int) *html.Node //根據索引獲取一個節點
Index() int //返回選擇物件中第一個元素的位置
Slice(start, end int) *Selection //根據起始位置獲取子節點集
迴圈遍歷選擇的節點
Each(f func(int, *Selection)) *Selection //遍歷
EachWithBreak(f func(int, *Selection) bool) *Selection //可中斷遍歷
Map(f func(int, *Selection) string) (result []string) //返回字串陣列

檢測或獲取節點屬性值

Attr(), RemoveAttr(), SetAttr() //獲取,移除,設定屬性的值

AddClass(), HasClass(), RemoveClass(), ToggleClass()

Html() //獲取該節點的html

Length() //返回該Selection的元素個數

Text() //獲取該節點的文字值

在文件樹之間來回跳轉(常用的查詢節點方法)

Children() //返回selection中各個節點下的孩子節點

Contents() //獲取當前節點下的所有節點

Find() //查詢獲取當前匹配的元素

Next() //下一個元素

Prev() //上一個元素

總結

goquery 是解析HTML網頁必備的利器,在爬蟲抓取網頁的過程中,靈活的使用goquery不同的選擇器,可以讓我們的抓取工作事半功倍,大大提升爬蟲的效率。

本作品採用《CC 協議》,轉載必須註明作者和本文連結