golang goquery selector(選取器) 樣本大全

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

本文為原創文章,轉載註明出處,歡迎掃碼關注公眾號flysnow_org或者網站http://www.flysnow.org/,第一時間看後續精彩文章。覺得好的話,順手分享到朋友圈吧,感謝支援。

最近研究Go爬蟲相關的知識,使用到goquery這個庫比較多,尤其是對爬取到的HTML進行選擇和尋找匹配的內容時,goquery的選取器使用尤其多,而且還有很多不常用但又很有用的選取器,這裡總結下,以供參考。

如果大家以前做過前端開發,對jquery不會陌生,goquery類似jquery,它是jquery的go版本實現。使用它,可以很方便的對HTML進行處理。

基於HTML Element 元素的選取器

這個比較簡單,就是基於a,p等這些HTML的基本元素進行選擇,這種直接使用Element名稱作為選取器即可。比如dom.Find("div")

12345678910111213141516171819
func main() {html := `<body><div>DIV1</div><div>DIV2</div><span>SPAN</span></body>`dom,err:=goquery.NewDocumentFromReader(strings.NewReader(html))if err!=nil{log.Fatalln(err)}dom.Find("div").Each(func(i int, selection *goquery.Selection) {fmt.Println(selection.Text())})}

以上樣本,可以把div元素篩選出來,而body,span並不會被篩選。

識別碼選取器

這個是使用頻次最多的,類似於上面的例子,有兩個div元素,其實我們只需要其中的一個,那麼我們只需要給這個標記一個唯一的id即可,這樣我們就可以使用id選取器,精確定位了。

12345678910111213141516171819
func main() {html := `<body><div id="div1">DIV1</div><div>DIV2</div><span>SPAN</span></body>`dom,err:=goquery.NewDocumentFromReader(strings.NewReader(html))if err!=nil{log.Fatalln(err)}dom.Find("#div1").Each(func(i int, selection *goquery.Selection) {fmt.Println(selection.Text())})}

Element 識別碼選取器

id選取器以#開頭,緊跟著元素id的值,使用文法為dom.Find(#id),後面的例子我會簡寫為Find(#id),大家知道這是代表goquery選取器的即可。

如果有相同的ID,但是它們又分別屬於不同的HTML元素怎麼辦?有好辦法,和Element結合起來。比如我們篩選元素為div,並且iddiv1的元素,就可以使用Find(div#div1)這樣的篩選器進行篩選。

所以這類篩選器的文法為Find(element#id),這是常用的組合方法,比如後面講的過濾器也可以採用這種方式組合使用。

Class選取器

class也是HTML中常用的屬性,我們可以通過class選取器來快速的篩選需要的HTML元素,它的用法和ID選取器類似,為Find(".class")

12345678910111213141516171819
func main() {html := `<body><div id="div1">DIV1</div><div class="name">DIV2</div><span>SPAN</span></body>`dom,err:=goquery.NewDocumentFromReader(strings.NewReader(html))if err!=nil{log.Fatalln(err)}dom.Find(".name").Each(func(i int, selection *goquery.Selection) {fmt.Println(selection.Text())})}

以上樣本中,就篩選出來classname的這個div元素。

Element Class 選取器

class選取器和id選取器一樣,也可以結合著HTML元素使用,他們的文法也類似Find(element.class),這樣就可以篩選特定element、並且指定class的元素。

屬性選取器

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

12345678910111213141516171819
func main() {html := `<body><div>DIV1</div><div class="name">DIV2</div><span>SPAN</span></body>`dom,err:=goquery.NewDocumentFromReader(strings.NewReader(html))if err!=nil{log.Fatalln(err)}dom.Find("div[class]").Each(func(i int, selection *goquery.Selection) {fmt.Println(selection.Text())})}

樣本中我們通過div[class]這個選取器,篩選出Element為div並且有class這個屬性的,所以第一個div沒有被篩選到。

剛剛上面這個樣本是採用是否存在某個屬性為篩選器,同理,我們可以篩選出屬性為某個值的元素。

123
   dom.Find("div[class=name]").Each(func(i int, selection *goquery.Selection) {fmt.Println(selection.Text())})

這樣我們就可以篩選出class這個屬性值為namediv元素。

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

除了完全相等,還有其他匹配方式,使用方式類似,這裡統一列舉下,不再舉例

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

以上是屬性篩選器的用法,都是以一個屬性篩選器為例,當然你也可以使用多個屬性篩選器組合使用,比如:
Find("div[id][lang=zh]"),用多個中括弧連起來即可。當有多個屬性篩選器的時候,要同時滿足這些篩選器的元素才能被篩選出來。

parent>child選取器

如果我們想篩選出某個元素下合格子項目,我們就可以使用子項目篩選器,它的文法為Find("parent>child"),表示篩選parent這個父元素下,符合child這個條件的最直接(一級)的子項目。

12345678910111213141516171819202122
func main() {html := `<body><div lang="ZH">DIV1</div><div lang="zh-cn">DIV2</div><div lang="en">DIV3</div><span><div>DIV4</div></span></body>`dom,err:=goquery.NewDocumentFromReader(strings.NewReader(html))if err!=nil{log.Fatalln(err)}dom.Find("body>div").Each(func(i int, selection *goquery.Selection) {fmt.Println(selection.Text())})}

以上樣本,篩選出body這個父元素下,合格最直接的子項目div,結果是DIV1、DIV2、DIV3,雖然DIV4也是body的子項目,但不是一級的,所以不會被篩選到。

那麼問題來了,我就是想把DIV4也篩選出來怎麼辦?就是要篩選body下所有的div元素,不管是一級、二級還是N級。有辦法的,goquery考慮到了,只需要把大於符號(>)改為空白格就好了。比如上面的例子,改為如下選取器即可。

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

prev+next相鄰選取器

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

123456789101112131415161718192021222324
func main() {html := `<body><div lang="zh">DIV1</div><p>P1</p><div lang="zh-cn">DIV2</div><div lang="en">DIV3</div><span><div>DIV4</div></span><p>P2</p></body>`dom,err:=goquery.NewDocumentFromReader(strings.NewReader(html))if err!=nil{log.Fatalln(err)}dom.Find("div[lang=zh]+p").Each(func(i int, selection *goquery.Selection) {fmt.Println(selection.Text())})}

這個樣本示範了這種用法,我們想選擇<p>P1</p>這個元素,但是沒啥規律,我們發現它前面的<div lang="zh">DIV1</div>很有規律,可以選擇,所以我們就可以採用Find("div[lang=zh]+p")達到選擇P元素的目的。

這種選取器的文法是("prev+next"),中間是一個加號(+),+號前後也是選取器。

本文為原創文章,轉載註明出處,歡迎掃碼關注公眾號flysnow_org或者網站http://www.flysnow.org/,第一時間看後續精彩文章。一些比較可恥的網站抓取我的文章會去掉著作權資訊,這裡再寫一段,大家見諒。

prev~next選取器

有相鄰就有兄弟,兄弟選取器就不一定要求相鄰了,只要他們共有一個父元素就可以。

123
dom.Find("div[lang=zh]~p").Each(func(i int, selection *goquery.Selection) {fmt.Println(selection.Text())})

剛剛的例子,只需要把+號換成~號,就可以把P2也篩選出來,因為P2P1DIV1都是兄弟。

兄弟選取器的文法是("prev~next"),也就是相鄰選取器的+換成了~

內容過濾器

有時候我們使用選取器選擇出來後後,希望再過濾一下,這時候就用到過濾器了,過濾器有很多,我們先講內容過濾器這一種。

123
dom.Find("div:contains(DIV2)").Each(func(i int, selection *goquery.Selection) {fmt.Println(selection.Text())})

Find(":contains(text)")表示篩選出的元素要包含指定的文本,我們例子中要求選擇出的div元素要包含DIV2文本,那麼只有一個DIV2元素滿足要求。

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

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

123
   dom.Find("span:has(div)").Each(func(i int, selection *goquery.Selection) {fmt.Println(selection.Text())})

以上樣本表示篩選出包含div元素的span節點。

:first-child過濾器

:first-child過濾器,文法為Find(":first-child"),表示篩選出的元素要是他們的父元素的第一個子項目,如果不是,則不會被篩選出來。

1234567891011121314151617181920212223242526
func main() {html := `<body><div lang="zh">DIV1</div><p>P1</p><div lang="zh-cn">DIV2</div><div lang="en">DIV3</div><span><div style="display:none;">DIV4</div><div>DIV5</div></span><p>P2</p><div></div></body>`dom,err:=goquery.NewDocumentFromReader(strings.NewReader(html))if err!=nil{log.Fatalln(err)}dom.Find("div:first-child").Each(func(i int, selection *goquery.Selection) {fmt.Println(selection.Html())})}

以上例子中,我們使用Find("div")會篩選出所有的div元素,但是我們加了:first-child後,就只有DIV1DIV4了,因為只有這兩個是他們父元素的第一個子項目,其他的DIV都不滿足。

:first-of-type過濾器

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

12345678910111213141516171819202122232425
func main() {html := `<body><div lang="zh">DIV1</div><p>P1</p><div lang="zh-cn">DIV2</div><div lang="en">DIV3</div><span><p>P2</p><div>DIV5</div></span><div></div></body>`dom,err:=goquery.NewDocumentFromReader(strings.NewReader(html))if err!=nil{log.Fatalln(err)}dom.Find("div:first-of-type").Each(func(i int, selection *goquery.Selection) {fmt.Println(selection.Html())})}

改動很簡單,把原來的DIV4換成了P2,如果我們還使用:first-child,DIV5是不能被篩選出來的,因為它不是第一個子項目,它前面還有一個P2。這時候我們使用:first-of-type就可以達到目的,因為它要求是同類型第一個就可以。DIV5就是這個div類型的第一個元素,P2不是div類型,被忽略。

:last-child 和 :last-of-type過濾器

這兩個正好和上面的:first-child:first-of-type相反,表示最後一個,這裡不再舉例,大家可以自己試試。

:nth-child(n) 過濾器

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

12345678910111213141516171819202122232425
func main() {html := `<body><div lang="zh">DIV1</div><p>P1</p><div lang="zh-cn">DIV2</div><div lang="en">DIV3</div><span><p>P2</p><div>DIV5</div></span><div></div></body>`dom,err:=goquery.NewDocumentFromReader(strings.NewReader(html))if err!=nil{log.Fatalln(err)}dom.Find("div:nth-child(3)").Each(func(i int, selection *goquery.Selection) {fmt.Println(selection.Html())})}

這個樣本會篩選出DIV2,因為DIV2是其父元素body的第三個子項目。

: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 過濾器

Find(":only-child") 過濾器,從字面上看,可以猜測出來,它表示篩選的元素,在其父元素中,只有它自己,它的父元素沒有其他子項目,才會被匹配篩選出來。

12345678910111213141516171819
func main() {html := `<body><div lang="zh">DIV1</div><span><div>DIV5</div></span></body>`dom,err:=goquery.NewDocumentFromReader(strings.NewReader(html))if err!=nil{log.Fatalln(err)}dom.Find("div:only-child").Each(func(i int, selection *goquery.Selection) {fmt.Println(selection.Html())})}

樣本中DIV5就可以被篩選出來,因為它是它的父元素span達到唯一子項目,但DIV1就不是,所以不能唄篩選出來。

:only-of-type 過濾器

上面的例子,如果想篩選出DIV1怎麼辦?可以使用Find(":only-of-type"),因為它是它的父元素中,唯一的div元素,這就是:only-of-type過濾器所要做的,同類型元素只要只有一個,就可以被篩選出來。大家把上面的例子改成:only-of-type試試,看看是否有DIV1

選取器或(|)運算

如果我們想同時篩選出div,span等元素怎麼辦?這時候可以採用多個選取器進行組合使用,並且以逗號(,)分割,Find("selector1, selector2, selectorN")表示,只要滿足其中一個選取器就可以被篩選出來,也就是選取器的或(|)運算操作。

12345678910111213141516171819
func main() {html := `<body><div lang="zh">DIV1</div><span><div>DIV5</div></span></body>`dom,err:=goquery.NewDocumentFromReader(strings.NewReader(html))if err!=nil{log.Fatalln(err)}dom.Find("div,span").Each(func(i int, selection *goquery.Selection) {fmt.Println(selection.Html())})}

小結

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

本文為原創文章,轉載註明出處,歡迎掃碼關注公眾號flysnow_org或者網站http://www.flysnow.org/,第一時間看後續精彩文章。覺得好的話,順手分享到朋友圈吧,感謝支援。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.