网络爬虫之网页数据解析(XPath)

引入

有人说,我正则用的不好,处理HTML文档很累,有没有其他的方法?

有!那就是XPath,我们可以先将网络获取的String类型数据转换成 HTML/XML文档,然后用 XPath 查找 HTML/XML 节点或元素。

什么是XML

大家都知道HTML,那XML又是什么呢?

  • XML 指可扩展标记语言(EXtensible Markup Language)
  • XML 是一种标记语言,很类似 HTML
  • XML 的设计宗旨是传输数据,而非显示数据
  • XML 的标签需要我们自行定义。
  • XML 被设计为具有自我描述性。
  • XML 是 W3C 的推荐标准

图解:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vUeRZNSF-1590634079149)(C:\Users\王利钦\Desktop\爬虫总结\XML图解1.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0Pxx1cJ1-1590634079153)(C:\Users\王利钦\Desktop\爬虫总结\XML图解2.jpg)]

XML的节点关系

父、子、同胞、先辈、后代

XPath定义

XPath (XML Path Language) 是一门在 XML 文档中查找信息的语言,可用来在 XML 文档中对元素和属性进行遍历。

XPath表达式

  • 最常用的路径表达式
    通配符 描述
    / 从根节点选取。
    // 从匹配选择的当前节点选择文档中的节点,而不考虑他们的位置。
    . 选取当前节点。
    选取当前节点的父节点。
    @ 选取属性。
  • 常用路径表达式以及表达式的结果
    表达式 结果
    /bookstore 选取根元素bookstore。注释:假如路径起始于正斜杆(/),则此路径始终代表到某元素的绝对路径!
    bookstore/book 选取属于bookstore的子元素的所有book元素
    //book 选取所有book子元素,而不管它们在文档中的位置。
    bookstore//book 选取属于bookstore元素的后代的所有book元素,而不管它们位于bookstore之下的什么位置。
    //@lang 选取名为lang的所有属性
  • 谓语用来查找某个特定的节点或者包含某个指定的值的节点,被嵌在方括号中
    路径表达式 结果
    /bookstore/book[1] 选取属于bookstore子元素的第一个book元素。
    /bookstore/book[last()] 选取属于bookstore子元素的最后一个book元素。
    /bookstore/book[last()-1] 选取属于bookstore子元素的倒数第二个book元素。
    /bookstore/book[postion()❤️] 选取最前面的两个属于bookstore元素的子元素的book元素。
    //title[@lang] 选取所有拥有名为lang的属性的title元素。
    //title[@lang=‘eng’] 选取所有title元素,且这些元素拥有值为eng的lang属性。
    /bookstore/book[price>35.00] 选取bookstore元素的所有book元素,且其中的price元素的值须大于35.00。
    /bookstore/book[price>35.00]/title 选取bookstore元素中的book元素的所有title元素,且其中的price元素的值须大于35.00。
  • 选取未知节点
    通配符 描述
    * 匹配任何元素节点。
    @* 匹配任何属性节点。
    路径表达式 结果
    /bookstore/* 选取bookstore元素的所有子元素。
    //* 选取文档中的所有元素。
    //title[@*] 选取所有带有属性的title元素。
  • 选取若干路径,通过在路径表达式中使用“|”运算符,您可以选取若干个路径
    路径表达式 结果
    //book/title|//book/price 选取book元素的所有title和price元素。
    //title|//price 选取文档中的所有title和price元素。
    /bookstore/book/title|//price 选取属于bookstore元素的book元素的所有title元素,以及文档中所有的price元素。
  • XPath的运算符
    运算符 描述 实例 返回值
    | 计算两个节点集 //book|//cd 返回所有拥有book和cd元素的节点集
    + 加法 6+4 10
    - 减法 6-4 2
    * 乘法 6*4 24
    div 除法 8div4 2
    = 等于 price=9.80 如果price是9.80,则返回true。如果price是9.90,则返回false。
    != 不等于 price!=9.80 如果price是9.90,则返回true。如果price是9.80,则返回false。
    < 小于 price<9.80 如果price是9.00,则返回true。如果price是9.90,则返回false。
    <= 小于或等于 price<=9.80 如果price是9.00,则返回true。如果price是9.90,则返回false。
    > 大于 price>9.80 如果price是9.90,则返回true。如果price是9.80,则返回false。
    >= 大于或等于 price>=9.80 如果price是9.90,则返回true。如果price是9.70,则返回false。
    or price=9.80 or price=9.70 如果price是9.80或者9.70,则返回true。
    and price>9.00 and price<9.90 如果price是9.80,则返回true。如果price是8.50,则返回false。
    mod 计算除法的余数 5 mod 2 1

lxml库

定义
  • lxml 是 一个HTML/XML的解析器,主要的功能是如何解析和提取 HTML/XML 数据。

  • lxml和正则一样,也是用 C 实现的,是一款高性能的 Python HTML/XML 解析器,我们可以利用XPath语法,来快速的定位特定元素以及节点信息。

lxml数据转换
  • 源码

    from lxml import etree
    
    str = '''<div>
        <ul>
             <li class="item-0"><a href="link1.html">first item</a></li>
             <li class="item-1"><a href="link2.html">second item</a></li>
             <li class="item-inactive"><a href="link3.html">third item</a></li>
             <li class="item-1"><a href="link4.html">fourth item</a></li>
             <li class="item-0"><a href="link5.html">fifth item</a>
         </ul>
     </div>
    '''
    #利用etree.HTML,将String字符串解析为HTML文档
    html = etree.HTML(str)
    result = etree.tostring(html)
    print(result.decode('utf-8'))
    
  • 所得数据

 '''<div>
    <ul>
         <li class="item-0"><a href="link1.html">first item</a></li>
         <li class="item-1"><a href="link2.html">second item</a></li>
         <li class="item-inactive"><a href="link3.html">third item</a></li>
         <li class="item-1"><a href="link4.html">fourth item</a></li>
         <li class="item-0"><a href="link5.html">fifth item</a>
     </ul>
 </div>
'''
lxml读取文件
  • 源码

    from lxml import etree
    
    # 读取外部文件 hello.html
    html = etree.parse('./hello.html')
    print(html)
    
  • 文件数据

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>测试页面</title>
</head>
<body>
    <ol>
        <li class="haha">醉卧沙场君莫笑,古来征战几人回</li>
        <li class="heihei">两岸猿声啼不住,轻舟已过万重山</li>
        <li id="hehe" class="nene">一骑红尘妃子笑,无人知是荔枝来</li>
        <li class="xixi">停车坐爱枫林晚,霜叶红于二月花</li>
        <li class="lala">商女不知亡国恨,隔江犹唱后庭花</li>
    </ol>
    <div id="pp">
        <div>
            <a href="http://www.baidu.com">李白</a>
        </div>
        <ol>
            <li class="huanghe">君不见黄河之水天上来,奔流到海不复回</li>
            <li id="tata" class="hehe">李白乘舟将欲行,忽闻岸上踏歌声</li>
            <li class="tanshui">桃花潭水深千尺,不及汪伦送我情</li>
        </ol>
        <div class="hh">
            <a href="http://mi.com">雷军</a>

        </div>
        <div class="jj">
            <b href="http://mi.com"><c>3</c></b>
            <b href="http://mi.com"><c>5</c></b>
            <b href="http://mi.com"><c>6</c></b>
            <b href="http://mi.com"><c>8</c></b>
            <b href="http://mi.com"><c>9</c></b>
            <b href="http://mi.com"><c>3</c></b>


        </div>
        <ol>
            <li class="dudu">are you ok</li>
            <li class="meme">会飞的猪</li>
        </ol>
    </div>
</body>
</html>
XPath具体用法
  • 被解析网页原码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title>测试页面</title>
    </head>
    <body>
        <ol>
            <li class="haha">醉卧沙场君莫笑,古来征战几人回</li>
            <li class="heihei">两岸猿声啼不住,轻舟已过万重山</li>
            <li id="hehe" class="nene">一骑红尘妃子笑,无人知是荔枝来</li>
            <li class="xixi">停车坐爱枫林晚,霜叶红于二月花</li>
            <li class="lala">商女不知亡国恨,隔江犹唱后庭花</li>
        </ol>
        <div id="pp">
            <div>
                <a href="http://www.baidu.com">李白</a>
            </div>
            <ol>
                <li class="huanghe">君不见黄河之水天上来,奔流到海不复回</li>
                <li id="tata" class="hehe">李白乘舟将欲行,忽闻岸上踏歌声</li>
                <li class="tanshui">桃花潭水深千尺,不及汪伦送我情</li>
            </ol>
            <div class="hh">
                <a href="http://mi.com">雷军</a>
    
            </div>
            <div class="jj">
                <b href="http://mi.com"><c>3</c></b>
                <b href="http://mi.com"><c>5</c></b>
                <b href="http://mi.com"><c>6</c></b>
                <b href="http://mi.com"><c>8</c></b>
                <b href="http://mi.com"><c>9</c></b>
                <b href="http://mi.com"><c>3</c></b>
    
    
            </div>
            <ol>
                <li class="dudu">are you ok</li>
                <li class="meme">会飞的猪</li>
            </ol>
        </div>
    </body>
    </html>
    
  • 获取所有的< li >标签

    from lxml import etree
    
    html = etree.parse('hello.html')
    li_list = html.xpath('//li')
    
    print(li_list)  # 打印<li>标签的元素集合
    print(len(li_list))
    
  • 继续获取< li > 标签的所有 class属性

    from lxml import etree
    
    html = etree.parse('hello.html')
    result = html.xpath('//li/@class')
    print(result)
    
  • 继续获取< li >标签下href为 link1.html 的 < a > 标签

    from lxml import etree
    html = etree.parse('./hello.html')
    result = html.xpath('//li/a[@href="link1.html"]')
    print(result)
    
  • 获取< li >标签下的所有< span >标签

    from lxml import etree
    data = '''
    <div>
        <ul>
             <li class="item-0">你好,老段<a href="link1.html">first item</a></li>
             <li class="item-1"><a href="link2.html">second item</a></li>
             <li class="item-inactive"><a href="link3.html"><span class="bold">third item</span></a></li>
             <li class="item-1"><a href="link4.html">fourth item</a></li>
             <li class="item-0"><a href="link5.html">fifth item</a></li>
         </ul>
     </div>'''
    html = etree.HTML(data)
    result = html.xpath('//li//span')
    print(result[0].text)
    
  • 获取< li >标签下的< a >标签的所有class

    # 获取 <li> 标签下的<a>标签里的所有 class
    from lxml import etree
    html = etree.parse('hello.html')
    result = html.xpath('//li/a//@class')
    
    print(result)
    
  • 获取最后一个< li >的< a >的href

    from lxml import etree
    
    xml = etree.parse('./hello.html')
    
    result = xml.xpath('//li[last()]/a/@href')
    
    print(result)
    
  • 获取倒数第二个元素的内容

    from lxml import etree
    
    html = etree.parse('hello.html')
    result = html.xpath('//li[last()-1]/a')
    print(result[0].text)
    print(result)
    
  • 获取class值为bold的标签名

    # 获取 class 值为 bold 的标签名
    from lxml import etree
    html = etree.parse('hello.html')
    result = html.xpath('//*[@class="bold"]')
    # tag方法可以获取标签名
    print(result[0].tag)
    print(result[0].text)
    
  • 条件使用

    • 获取文本数据://li[@id=“hehe”]/text()
    • 包含某个条件://li[contains(@class,“h”)]
    • 等于某个条件://div[@id=“pp”]/ol[last()]/li/@*
    • 条件并用://li[@id=“hehe”] [@class=“nene”]/text()

XPath案例

import requests
from lxml import etree
url1 = 'https://www.neihanba.com/dz/'
url = 'https://www.neihanba.com/dz/list_%d.html'
if __name__ == '__main__':
    fp = open('./duanzi.csv',mode = 'a',encoding='utf-8')
    for i in range(1,101):
        if i == 1:
            url_duanzi = url1
        else:
            url_duanzi = url%(i)
        response = requests.get(url_duanzi)
        response.encoding = 'gbk'
        content = response.text
        html = etree.HTML(content)
        result = html.xpath('//ul[@class="piclist longList"]/li')
        for li in result:
            try:
                title = li.xpath('.//h4/a/b/text()')[0]
                content = li.xpath('.//div[@class="f18 mb20"]/text()')[0].strip().strip('\n')
                info = ''.join(li.xpath('.//div[@class="ft"]/span//text()')[1:])
                fp.write('%s\t%s\t%s\n'%(title,content,info))
            except Exception as e:
                # 异常保存,第二天,分析,单独爬取。
                pass
        print('第%d页内容保存成功!'%(i))
    fp.close()
    # !!!缺少异常捕获

猜你喜欢

转载自blog.csdn.net/qq_42546127/article/details/106398987