【Golang实战】——XPath解析网页

陆仲渊
2023-12-01

引言

之前用Python写过一个解析网页的脚本,最近开始研究golang,所以准备用golang将其重构,但是这个脚本中使用了到了python中的xpath库,所以研究了下,golang也有对应的库,这个库比我们使用正则从网页中获取我们需要的内容更简单些。

实例

我们以解析网页中的ip+端口为例:网站:https://www.kuaidaili.com/free/inha

1、引入包

github.com/antchfx/htmlquery v1.2.5 

 2、获得网页内容

func getHtml(url_ string) string {
	req, _ := http.NewRequest("GET", url_, nil)
	req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3776.0 Safari/537.36")
	client := &http.Client{Timeout: time.Second * 5}
	resp, err := client.Do(req)
	if err != nil {
		log.Fatalln(err)
	}
	defer resp.Body.Close()
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil && data == nil {
		log.Fatalln(err)
	}
	return fmt.Sprintf("%s", data)
}

3、解析内容

func main()  {
urlTemplate := "https://www.kuaidaili.com/free/inha/%d/"

	for i := 1; i < 4000; i++ {
		var proxies []string
		html := getHtml(fmt.Sprintf(urlTemplate, i))
		root, _ := htmlquery.Parse(strings.NewReader(html))
		tr := htmlquery.Find(root, "//*[@id='list']/table/tbody/tr")
		for _, row := range tr {
			item := htmlquery.Find(row, "./td")
			ip := htmlquery.InnerText(item[0])
			port := htmlquery.InnerText(item[1])
			//type_ := htmlquery.InnerText(item[3])
			p := ip + ":" + port
			proxies = append(proxies, p)
		}
    }
}

通过上面代码我们就可以从页面中将我们需要的ip+port内容获取到,我们可以根据这种格式解析我们自己想要的网页内容。

4、xpath语法

XPath 语法 | 菜鸟教程

5、其它是实现

golang中多个包来实现xpath,我们再看下libxml2

包引入

go get github.com/lestrrat-go/libxml2

解析内容

<ol class="grid_view">
  <li>
    <div class="item">
      <div class="info">
        <div class="hd">
          <a href="https://movie.douban.com/subject/1292052/" class="">
            <span class="title">肖申克的救赎</span>
            <span class="title">&nbsp;/&nbsp;The Shawshank Redemption</span>
            <span class="other">&nbsp;/&nbsp;月黑高飞(港)  /  刺激 1995(台)</span>
          </a>
          <span class="playable">[可播放]</span>
        </div>
      </div>
    </div>
  </li>
  ....
</ol>

golang代码

func parseUrls(url string, ch chan bool) {
        doc := fetch(url)
        defer doc.Free()
        nodes := xpath.NodeList(doc.Find(`//ol[@class="grid_view"]/li//div[@class="hd"]`))
        for _, node := range nodes {
                urls, _ := node.Find("./a/@href")
                titles, _ := node.Find(`.//span[@class="title"]/text()`)
                log.Println(strings.Split(urls.NodeList()[0].TextContent(), "/")[4],
                        titles.NodeList()[0].TextContent())
        }
        time.Sleep(2 * time.Second)
        ch <- true
}

这种方式比第一种方式使用起来麻烦一些,并且接口和文档都不是非常的完善。

 类似资料: