golang goquery selector(選擇器) 示例大全

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

最近研究Go爬蟲相關的知識,使用到goquery這個庫比較多,尤爲是對爬取到的HTML進行選擇和查找匹配的內容時,goquery的選擇器使用尤爲多,並且還有不少不經常使用但又頗有用的選擇器,這裏總結下,以供參考。前端

若是你們之前作過前端開發,對jquery不會陌生,goquery相似jquery,它是jquery的go版本實現。使用它,能夠很方便的對HTML進行處理。jquery

基於HTML Element 元素的選擇器

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

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並不會被篩選。測試

ID 選擇器

這個是使用頻次最多的,相似於上面的例子,有兩個div元素,其實咱們只須要其中的一個,那麼咱們只須要給這個標記一個惟一的id便可,這樣咱們就可使用id選擇器,精肯定位了。網站

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選擇器以#開頭,緊跟着元素id的值,使用語法爲dom.Find(#id),後面的例子我會簡寫爲Find(#id),你們知道這是表明goquery選擇器的便可。spa

若是有相同的ID,可是它們又分別屬於不一樣的HTML元素怎麼辦?有好辦法,和Element結合起來。好比咱們篩選元素爲div,而且iddiv1的元素,就可使用Find(div#div1)這樣的篩選器進行篩選。code

因此這類篩選器的語法爲Find(element#id),這是經常使用的組合方法,好比後面講的過濾器也能夠採用這種方式組合使用。cdn

Class選擇器

class也是HTML中經常使用的屬性,咱們能夠經過class選擇器來快速的篩選須要的HTML元素,它的用法和ID選擇器相似,爲Find(".class")htm

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元素都有本身的屬性以及屬性值,因此咱們也能夠經過屬性和值篩選元素。

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沒有被篩選到。

剛剛上面這個示例是採用是否存在某個屬性爲篩選器,同理,咱們能夠篩選出屬性爲某個值的元素。

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這個條件的最直接(一級)的子元素。

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,結果是DIV一、DIV二、DIV3,雖然DIV4也是body的子元素,但不是一級的,因此不會被篩選到。

那麼問題來了,我就是想把DIV4也篩選出來怎麼辦?就是要篩選body下全部的div元素,不論是一級、二級仍是N級。有辦法的,goquery考慮到了,只須要把大於號(>)改成空格就行了。好比上面的例子,改成以下選擇器便可。

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

prev+next相鄰選擇器

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

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或者網站www.flysnow.org/,第一時間看後續精彩文章。一些比較可恥的網站抓取個人文章會去掉版權信息,這裏再寫一段,你們見諒。

prev~next選擇器

有相鄰就有兄弟,兄弟選擇器就不必定要求相鄰了,只要他們共有一個父元素就能夠。

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

剛剛的例子,只須要把+號換成~號,就能夠把P2也篩選出來,由於P2P1DIV1都是兄弟。

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

內容過濾器

有時候咱們使用選擇器選擇出來後後,但願再過濾一下,這時候就用到過濾器了,過濾器有不少,咱們先講內容過濾器這一種。

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差很少,只不過這個是包含的是元素節點。

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"),表示篩選出的元素要是他們的父元素的第一個子元素,若是不是,則不會被篩選出來。

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就派上用場了,它要求只要是這個類型的第一個就能夠,咱們把上面的例子微調下。

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,咱們就很靈活的篩選出咱們須要的元素。

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") 過濾器,從字面上看,能夠猜想出來,它表示篩選的元素,在其父元素中,只有它本身,它的父元素沒有其餘子元素,纔會被匹配篩選出來。

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")表示,只要知足其中一個選擇器就能夠被篩選出來,也就是選擇器的或(|)運算操做。

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或者網站www.flysnow.org/,第一時間看後續精彩文章。以爲好的話,順手分享到朋友圈吧,感謝支持。

掃碼關注
相關文章
相關標籤/搜索