本文爲原創文章,轉載註明出處,歡迎掃碼關注公衆號
flysnow_org
或者網站www.flysnow.org/,第一時間看後續精彩文章。以爲好的話,順手分享到朋友圈吧,感謝支持。html
最近研究Go爬蟲相關的知識,使用到goquery這個庫比較多,尤爲是對爬取到的HTML進行選擇和查找匹配的內容時,goquery的選擇器使用尤爲多,並且還有不少不經常使用但又頗有用的選擇器,這裏總結下,以供參考。前端
若是你們之前作過前端開發,對jquery不會陌生,goquery相似jquery,它是jquery的go版本實現。使用它,能夠很方便的對HTML進行處理。jquery
這個比較簡單,就是基於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
並不會被篩選。測試
這個是使用頻次最多的,相似於上面的例子,有兩個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())
})
}
複製代碼
id
選擇器以#
開頭,緊跟着元素id
的值,使用語法爲dom.Find(#id)
,後面的例子我會簡寫爲Find(#id)
,你們知道這是表明goquery選擇器的便可。spa
若是有相同的ID,可是它們又分別屬於不一樣的HTML元素怎麼辦?有好辦法,和Element結合起來。好比咱們篩選元素爲div
,而且id
是div1
的元素,就可使用Find(div#div1)
這樣的篩選器進行篩選。code
因此這類篩選器的語法爲Find(element#id)
,這是經常使用的組合方法,好比後面講的過濾器也能夠採用這種方式組合使用。cdn
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())
})
}
複製代碼
以上示例中,就篩選出來class
爲name
的這個div
元素。
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
這個屬性值爲name
的div
元素。
固然咱們這裏以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]")
,用多箇中括號連起來便可。當有多個屬性篩選器的時候,要同時知足這些篩選器的元素才能被篩選出來。
若是咱們想篩選出某個元素下符合條件的子元素,咱們就可使用子元素篩選器,它的語法爲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())
})
複製代碼
假設咱們要篩選的元素沒有規律,可是該元素的上一個元素有規律,咱們就可使用這種下一個相鄰選擇器來進行選擇。
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/,第一時間看後續精彩文章。一些比較可恥的網站抓取個人文章會去掉版權信息,這裏再寫一段,你們見諒。
有相鄰就有兄弟,兄弟選擇器就不必定要求相鄰了,只要他們共有一個父元素就能夠。
dom.Find("div[lang=zh]~p").Each(func(i int, selection *goquery.Selection) {
fmt.Println(selection.Text())
})
複製代碼
剛剛的例子,只須要把+
號換成~
號,就能夠把P2
也篩選出來,由於P2
、P1
和DIV1
都是兄弟。
兄弟選擇器的語法是("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
過濾器,語法爲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
後,就只有DIV1
和DIV4
了,由於只有這兩個是他們父元素的第一個子元素,其餘的DIV
都不知足。
: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
類型,被忽略。
這兩個正好和上面的:first-child
、:first-of-type
相反,表示最後一個,這裏再也不舉例,你們能夠本身試試。
這個表示篩選出的元素是其父元素的第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-child(n)
相似,只不過它表示的是同類型元素的第n個,因此:nth-of-type(1)
和 :first-of-type
是相等的,你們能夠本身試試,這裏再也不舉例。
這兩個和上面的相似,只不過是倒序開始計算的,最後一個元素被當成了第一個。你們本身測試下看看效果,很明顯。
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
就不是,因此不能唄篩選出來。
上面的例子,若是想篩選出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/,第一時間看後續精彩文章。以爲好的話,順手分享到朋友圈吧,感謝支持。