XPath在python中的高级应用

XPath在python的爬虫学习中,起着举足轻重的地位,对比正则表达式 re两者可以完成同样的工作,实现的功能也差不多,但XPath明显比re具有优势,在网页分析上使re退居二线。

XPath介绍:

是什么? 全称为XML Path Language 一种小型的查询语言
说道XPath是门语言,不得不说它所具备的优点:
1) 可在XML中查找信息
2) 支持HTML的查找
3) 通过元素和属性进行导航

python开发使用XPath条件:

由于XPath属于lxml库模块,所以首先要安装库lxml,具体的安装过程可以查看博客,包括easy_install 和 pip 的安装方法。

XPath的简单调用方法:

from lxml import etree
selector=etree.HTML(源码)  #将源码转化为能被XPath匹配的格式
selector.xpath(表达式)  #返回为一列表

XPath的使用方法:

首先讲一下XPath的基本语法知识:
四种标签的使用方法
1) // 双斜杠 定位根节点,会对全文进行扫描,在文档中选取所有符合条件的内容,以列表的形式返回。
2) / 单斜杠 寻找当前标签路径的下一层路径标签或者对当前路标签内容进行操作
3) /text() 获取当前路径下的文本内容
4) /@xxxx 提取当前路径下标签的属性值
5) | 可选符 使用|可选取若干个路径 如//p | //div 即在当前路径下选取所有符合条件的p标签和div标签。
6) . 点 用来选取当前节点
7) .. 双点 选取当前节点的父节点
另外还有starts-with(@属性名称,属性字符相同部分),string(.)两种重要的特殊方法后面将重点讲。

利用实例讲解XPath的使用:

from lxml import etree
html="""
    <!DOCTYPE html>
    <html>
        <head lang="en">
        <title>测试</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        </head>
        <body>
            <div id="content">
                <ul id="ul">
                    <li>NO.1</li>
                    <li>NO.2</li>
                    <li>NO.3</li>
                </ul>
                <ul id="ul2">
                    <li>one</li>
                    <li>two</li>
                </ul>
            </div>
            <div id="url">
                <a href="http:www.58.com" title="58">58</a>
                <a href="http:www.csdn.net" title="CSDN">CSDN</a>
            </div>
        </body>
    </html>
"""
selector=etree.HTML(html)
content=selector.xpath('//div[@id="content"]/ul[@id="ul"]/li/text()') #这里使用id属性来定位哪个div和ul被匹配 使用text()获取文本内容
for i in content:
    print i
#输出为
NO.1
NO.2
NO.3

con=selector.xpath('//a/@href') #这里使用//从全文中定位符合条件的a标签,使用“@标签属性”获取a便签的href属性值
for each in con:
    print each
#输出结果为:
http:www.58.com
http:www.csdn.net

con=selector.xpath('/html/body/div/a/@title') #使用绝对路径定位a标签的title
con=selector.xpath('//a/@title') #使用相对路径定位 两者效果是一样的
print len(con)
print con[0]con[1]

#输出结果为:
2
58 CSDN

介绍XPath的特殊用法:

1) starts-with 解决标签属性值以相同字符串开头的情况

举例说明

from lxml import etree
html="""
    <body>
        <div id="aa">aa</div>
        <div id="ab">ab</div>
        <div id="ac">ac</div>
    </body>
    """
selector=etree.HTML(html)
content=selector.xpath('//div[starts-with(@id,"a")]/text()') #这里使用starts-with方法提取div的id标签属性值开头为a的div标签
for each in content:
    print each
#输出结果为:
aa
ab
ac

2) string(.) 标签套标签(string里面的点,表示当前节点的意思)

html="""
    <div id="a">
    left
        <span id="b">
        right
            <ul>
            up
                <li>down</li>
            </ul>
        east
        </span>
        west
    </div>
"""
#下面是没有用string方法的输出
sel=etree.HTML(html)
con=sel.xpath('//div[@id="a"]/text()')
for i in con:
    print i   #输出内容为left west

data=sel.xpath('//div[@id="a"]')[0]
info=data.xpath('string(.)')
content=info.replace('\n','').replace(' ','')
for i in content:
    print i #输出为 全部内容

XPath提供的几个特殊的方法:

XPath中需要取的标签如果没有属性,可以使用text(),posision()来识别标签。
xpath抓取的值有空格换行符等问题,解决方式: normalize-space()
举两个简单的例子:

from lxml import etree
html="""
    <div>hello
        <p>H</p>
        <p>D</p>
</div>
<div>hehe</div>
"""
sel=etree.HTML(html)
con=sel.xpath('//div[normalize-space(text())="hello"]/p/text()')
print(con)
# ['H', 'D']

这里使用text()的方法来判别是哪个div标签

from lxml import etree
html="""
    <div>hello
        <p>H</p>
        <p>J</p>
        <p>I</p>
</div>
<div>hehe</div>
"""
sel=etree.HTML(html)
con=sel.xpath('//div[normalize-space(text())="hello"]/p[position()=2]/text()') #或者//div[normalize-space(text())="hello"]/p[2]/text()
print(con)
# ['J']

另外,在XPath中可以使用多重过滤方法寻找标签,例如ul[3][@id=”a”] 这里使用【3】来寻找第三个ul标签 并且它的id属性值为a

获取XPath的方式有两种:

1) 使用以上等等的方法通过观察找规律的方式来获取XPath
2) 使用Chrome浏览器来获取 在网页中右击->选择审查元素(或者使用F12打开) 就可以在elements中查看网页的html标签了,找到你想要获取XPath的标签,右击->Copy XPath 就已经将XPath路径复制到了剪切板。

一些高手的xpath例子赏析:

第一个:抓取网站里面的 http 代理信息(gethub传送门):

# 需要利用xpath 定位代理IP 的站点
PROXY_SITES_BY_XPATH = [
    {
        'urls': ['http://www.66ip.cn/%s.html' % page for page in ['index'] + list(range(2, 11))],
        'ip_xpath': ".//*[@id='main']/div/div[1]/table/tr[position()>1]/td[1]/text()" , 
        'port_xpath': ".//*[@id='main']/div/div[1]/table/tr[position()>1]/td[2]/text()" 
    },
    {
        'urls': ['http://www.mimiip.com/gngao/%s' % page for page in range(2, 10)],
        'ip_xpath': ".//table[@class='list']/tbody/tr/td[1]/text()",
        'port_xpath': ".//table[@class='list']/tbody/tr/td[2]/text()" 
    },
    {
        'urls': ['http://www.ip181.com/daili/%s.html' % page for page in range(1, 8)],
        'ip_xpath': ".//div[@class='row']/div[3]/table/tbody/tr[position()>1]/td[1]/text()" ,
        'port_xpath': ".//div[@class='row']/div[3]/table/tbody/tr[position()>1]/td[2]/text()" 
    }
]

第二个(github传送门):

#parserList是网址解析规则表,大家可以将发现的代理网址,将提取规则添加到其中,方便爬虫的爬取。
parserList = [
    {
        'urls': ['http://www.66ip.cn/%s.html' % n for n in ['index'] + list(range(2, 12))],
        'type': 'xpath',
        'pattern': ".//*[@id='main']/div/div[1]/table/tr[position()>1]",
        'position': {'ip': './td[1]', 'port': './td[2]', 'type': './td[4]', 'protocol': ''}
    },

   ......


    {
        'urls': ['http://www.cnproxy.com/proxy%s.html' % i for i in range(1, 11)],
        'type': 'module',
        'moduleName': 'CnproxyPraser',
        'pattern': r'<tr><td>(\d+\.\d+\.\d+\.\d+)<SCRIPT type=text/javascript>document.write\(\"\:\"(.+)\)</SCRIPT></td><td>(HTTP|SOCKS4)\s*',
        'position': {'ip': 0, 'port': 1, 'type': -1, 'protocol': 2}
    }
]

转载地址

猜你喜欢

转载自blog.csdn.net/marvel__dead/article/details/79381185