引言
之前用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"> / The Shawshank Redemption</span> <span class="other"> / 月黑高飞(港) / 刺激 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 }
这种方式比第一种方式使用起来麻烦一些,并且接口和文档都不是非常的完善。