在这个系列文章里面已经介绍了BeautifulSoup的替代库soup和Pyquery的替代库goquery,但其实我写Python爬虫最愿意用的页面解析组合是lxml+XPath。为何呢?先分别说一下lxml和XPath的优点吧html

lxml

lxml是HTML/XML的解析器,它用 C 语言实现的 libxml2 和l ibxslt 的P ython 绑定。除了效率高,还有一个特色是文档容错能力强。node

XPath

XML Path LanguageXML路径语言

XPath与BeautifulSoup(soup)、Pyquery(goquery)相比,学习曲线要高一些,可是学会它是很是有价值的,你会爱上它。你看我如今,原来用Python写爬虫学会了XPath,如今能够直接找支持XPath的库直接用了。github

另外说一点,若是你很是喜欢BeautifulSoup,必定要选择BeautifulSoup+lxml这个组合,由于BeautifulSoup默认的HTML解析器用的是Python标准库中的html.parser,虽然文档容错能力也很强,可是效率会差不少。golang

我学习XPath是经过w3school,能够从延伸阅读找到连接web

Golang中的Xpath库

用Golang写的Xpath库是不少的,因为我尚未什么实际开发经验,因此能搜到的几个库都试用一下,而后再出结论吧。bash

首先把豆瓣Top250的部分HTML代码贴出来函数

<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>
复制代码

仍是原来的需求:得到条目 ID 和标题post

github.com/lestrrat-go/libxml2

lestrrat-go/libxml2是一个libxml2的Golang绑定库,性能

首先安装它:

❯ go get github.com/lestrrat-go/libxml2
复制代码

接着改代码

import (
        "log"
        "time"
        "strings"
        "strconv"
        "net/http"

        "github.com/lestrrat-go/libxml2"
        "github.com/lestrrat-go/libxml2/types"
        "github.com/lestrrat-go/libxml2/xpath"
)

func fetch(url string) types.Document {
        log.Println("Fetch Url", url)
        client := &http.Client{}
        req, _ := http.NewRequest("GET", url, nil)
        req.Header.Set("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)")
        resp, err := client.Do(req)
        if err != nil {
                log.Fatal("Http get err:", err)
        }
        if resp.StatusCode != 200 {
                log.Fatal("Http status code:", resp.StatusCode)
        }
        defer resp.Body.Close()
        doc, err := libxml2.ParseHTMLReader(resp.Body)
        if err != nil {
                log.Fatal(err)
        }
        return doc
}
复制代码
libxml2.ParseHTMLReader(resp.Body)
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
}
复制代码
NodeList()[index].TextContent
xpath.NewContextxpath.String(ctx.Find("/foo/bar"))

github.com/antchfx/htmlquery

htmlquery如其名,是一个对HTML文档作XPath查询的包。它的核心是antchfx/xpath,项目更新频繁,文档也比较完整。

首先安装它:

❯ go get github.com/antchfx/htmlquery
复制代码

接着按需求修改:

import (
    "log"
    "time"
    "strings"
    "strconv"
    "net/http"

    "golang.org/x/net/html"
    "github.com/antchfx/htmlquery"
)

func fetch(url string) *html.Node {
    log.Println("Fetch Url", url)
    client := &http.Client{}
    req, _ := http.NewRequest("GET", url, nil)
    req.Header.Set("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)")
    resp, err := client.Do(req)
    if err != nil {
        log.Fatal("Http get err:", err)
    }
    if resp.StatusCode != 200 {
        log.Fatal("Http status code:", resp.StatusCode)
    }
    defer resp.Body.Close()
    doc, err := htmlquery.Parse(resp.Body)
    if err != nil {
        log.Fatal(err)
    }
    return doc
}
复制代码
htmlquery.Parse(resp.Body)*html.NodeparseUrls
func parseUrls(url string, ch chan bool) {
    doc := fetch(url)
    nodes := htmlquery.Find(doc, `//ol[@class="grid_view"]/li//div[@class="hd"]`)
    for _, node := range nodes {
        url := htmlquery.FindOne(node, "./a/@href")
        title := htmlquery.FindOne(node, `.//span[@class="title"]/text()`)
        log.Println(strings.Split(htmlquery.InnerText(url), "/")[4],
            htmlquery.InnerText(title))
    }
    time.Sleep(2 * time.Second)
    ch <- true
}
复制代码
antchfx/htmlquerylestrrat-go/libxml2

后记

gopkg.in/xmlpath.v2

随便说一下gopkg.in,gopkg是一种包管理方式,实际上是用约定好的方式「代理」Github上对应项目的对应分支的包。具体的请看延伸阅读连接2。

xmlpath.v2这个包就是Github上的go-xmlpath/xmlpath, 分支是v2。

antchfx/htmlquery

代码地址

完整代码能够在这个地址找到。

延伸阅读