【Python 爬虫】XPath的简单使用

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

  1. 最常用的路径表达式

在这里插入图片描述

  1. 常用路径表达式以及表达式的结果

在这里插入图片描述

  1. 谓语用来查找某个特定的节点或者包含某个指定的值的节点,被嵌在方括号中

在这里插入图片描述

  1. 选取未知节点

在这里插入图片描述

  1. 选取若干路径,通过在路径表达式中使用“|”运算符,您可以选取若干个路径

在这里插入图片描述

  1. XPath的运算符

在这里插入图片描述
在这里插入图片描述

二、对于xpath的简单理解

浅析~DOM结构中的元素节点、属性节点、文本节点

上篇博客是我当初对元素节点、属性节点、文本节点的浅要理解。就我个人来说,我习惯把xpath理解成如上博客中的节点,来形成一棵DOM树。/ 表示选取当前层级上的子节点,这样如果把属性和文本都看成节点,那么使用 / 来获取文本和属性就显得很容易理解了。/node 表示选取node节点,/div/@id 选取div的子节点中的id属性节点,/div/text() 选取div的子节点中的文本节点,/div/p 选取div层级下的p标签节点

如图:
在这里插入图片描述

三、xpath的简单语法

注:感觉xpath的匹配策略像是贪心算法,只要有匹配的,就继续向下匹配。xpath的谓语部分就是判断,如果成立返回true,就选取当前节点。具体的判断条件应该是没有限制的,什么都可以,只要能有true和false,就可以选中使用谓语的当前节点。

from lxml import etree


'''
统一说明:
    div[]这种类型中,[]代表筛选条件,当条件成立的时候,就选中当前div,所谓的这个条件可以随便写,只要能够成立,就没问题
'''
# str类型数据
s = '''<div id="head_wrapper">这是第一个div<div id="son_div">这是子一div<div>这是孙一div</div></div><div id="son_div2" class="suner">这是子二div<price>45</price></div></div>
        <div id="s-isindex-wrap">这时第二个div
        </div>
        <div id="nologin" >这时第三个div</div>'''

def print_(item,name):
    print('*' * 25, '%s' % name, '#' * 25,'\n')
    if type(item) != type([]):
        print(etree.tostring(item,encoding='utf-8').decode('utf-8'))
    else:
        for i in item:
            print(etree.tostring(i,encoding='utf-8').decode('utf-8'))
            print('-' * 50)
    print('\n')

def print_l(item,name):
    print('*' * 25, '%s' % name, '#' * 25,'\n')
    print(item)
    print('\n')

if __name__ == '__main__':
    html = etree.HTML(s)
    # 转换成了Element对象,使用了默认的praser
    print(html,type(html))
    # 将Element对象里面的数据用str形式显示出来,自动添加了<html><body></body></html>

    extract_html = etree.tostring(html,encoding='utf-8').decode('utf-8')
    print_l(extract_html,'extract_html')


    # 真正的操作,还是在Element对象中进行
    # 获取/html/body/div的id属性,获取属性要额外加一个/,返回一个列表,列表项为head_wrapper
    # 返回一个列表,不可回溯,不可再次匹配
    result1 = html.xpath('/html/body/div/@id')
    print_l(result1, "/html/body/div的id属性")

    # 返回一个id属性的列表,不可回溯,不可再次匹配
    result2 = html.xpath('//div/@id')  # //不论位置,找到所有
    print_l(result2,'所有div的id属性列表')

    # 返回一个文本内容的列表,不可回溯,不可再次匹配
    result3 = html.xpath('/html/body/div/text()')
    print_l(result3,'/html/body/div/text()')

    # results是一个Element对象的数组,可以再次匹配,可以回溯,遵从匹配原则
    # 即使results中的列表项,是整个文档树中的某一片段,但是他并没有从文档树上摘取下来,按照树的遍历规则,他还可以找到父节点
    # 相当于返回的Element对象是一个节点指针,仍然在树上,而不是单纯的单一节点
    results = html.xpath('//div')
    print_l(len(results),'results的返回长度')
    for index,res in enumerate(results):
        print_(res,'第%d个res'%index)
        print(type(res))  # res 是一个Element对象
        tmp = res.xpath('..')  # 找到res的父节点
        tmp1 = res.xpath('/*')  # 从根节点开始匹配所有节点,应该是只返回一个html,即使当前节点并不是根节点
        print_(tmp,'第%d个res的父节点'%index)

    # xpath索引是从1开始的
    # 这个匹配是按照层级的来算的,获取的虽然是所有的div,但是这些div是分层次的
    # last()是获取所有层级div中,当前层级的最后一个,也就是有几个层级,就会返回几个最后一个div
    div = html.xpath('//div[last()]')
    print_(div,'last() div')

    div2 = html.xpath('//div[position()=2]')
    print_(div2,'position()=2 的div')

    div3 = html.xpath('//div[position()<=2]')
    print_(div3,'position()<=2 的div')

    result4 = html.xpath('//div[@id="son_div"]')
    print_(result4,'带谓语选择条件的div')

    result5 = html.xpath('//div[@id="son_div2"][@class="suner"]')
    print_(result5,'形式一:带多个条件筛选的div')

    # 选取某一节点,对这一节点有两种可选择的条件,and or
    result5 = html.xpath('//div[@id="son_div2" and @class="suner"]')
    print_(result5, '形式二:带多个条件筛选的div')

    result6 = html.xpath('//div[contains(@id,"son")]')
    print_(result6,'模糊查询,只要带有son的都会被找到')

    # 选取两种类型的节点,那个节点都可以,都有的话,就都被选择
    result7 = html.xpath('//div[@id="s-isindex-wrap"] | //div[@id="nologin"]')
    # 注意此时@id的使用
    result7 = html.xpath('//div[./@id="s-isindex-wrap"] | //div[/@id="nologin"]')
    print_(result7,'或者形式的查询,两个条件二选一')

    result8 = html.xpath('//div[price/text()>40]')
    result8 = html.xpath('//div[./price/text()>40]')
    result8 = html.xpath('//div[price>40]')
    print_(result8,'使用子元素节点的text作为筛选条件的')

    result9 = html.xpath('//div[price mod 5 =0]')
    print_(result9,'mod 作为筛选条件的使用')

    result10 = html.xpath('//div[1 = 1]')
    print_(result10,'使用恒等式作为谓语')
    
    result11 = html.xpath('//div[price]')
    print_(result11,'使用是否包含节点作为谓语')

运行结果:

<Element html at 0x1cd41ed9548> <class 'lxml.etree._Element'>
************************* extract_html ######################### 
<html><body><div id="head_wrapper">这是第一个div<div id="son_div">这是子一div<div>这是孙一div</div></div><div id="son_div2" class="suner">这是子二div<price>45</price></div></div>
        <div id="s-isindex-wrap">这时第二个div
        </div>
        <div id="nologin">这时第三个div</div></body></html>
************************* /html/body/div的id属性 ######################### 
['head_wrapper', 's-isindex-wrap', 'nologin']
************************* 所有div的id属性列表 ######################### 
['head_wrapper', 'son_div', 'son_div2', 's-isindex-wrap', 'nologin']
************************* /html/body/div/text() ######################### 
['这是第一个div', '这时第二个div\n        ', '这时第三个div']
************************* results的返回长度 ######################### 
6
*************************0个res ######################### 
<div id="head_wrapper">这是第一个div<div id="son_div">这是子一div<div>这是孙一div</div></div><div id="son_div2" class="suner">这是子二div<price>45</price></div></div>
        
<class 'lxml.etree._Element'>
*************************0个res的父节点 ######################### 
<body><div id="head_wrapper">这是第一个div<div id="son_div">这是子一div<div>这是孙一div</div></div><div id="son_div2" class="suner">这是子二div<price>45</price></div></div>
        <div id="s-isindex-wrap">这时第二个div
        </div>
        <div id="nologin">这时第三个div</div></body>
--------------------------------------------------
*************************1个res ######################### 
<div id="son_div">这是子一div<div>这是孙一div</div></div>
<class 'lxml.etree._Element'>
*************************1个res的父节点 ######################### 
<div id="head_wrapper">这是第一个div<div id="son_div">这是子一div<div>这是孙一div</div></div><div id="son_div2" class="suner">这是子二div<price>45</price></div></div>
        
--------------------------------------------------
*************************2个res ######################### 
<div>这是孙一div</div>
<class 'lxml.etree._Element'>
*************************2个res的父节点 ######################### 
<div id="son_div">这是子一div<div>这是孙一div</div></div>
--------------------------------------------------
*************************3个res ######################### 
<div id="son_div2" class="suner">这是子二div<price>45</price></div>
<class 'lxml.etree._Element'>
*************************3个res的父节点 ######################### 
<div id="head_wrapper">这是第一个div<div id="son_div">这是子一div<div>这是孙一div</div></div><div id="son_div2" class="suner">这是子二div<price>45</price></div></div>
        
--------------------------------------------------
*************************4个res ######################### 
<div id="s-isindex-wrap">这时第二个div
        </div>
        
<class 'lxml.etree._Element'>
*************************4个res的父节点 ######################### 
<body><div id="head_wrapper">这是第一个div<div id="son_div">这是子一div<div>这是孙一div</div></div><div id="son_div2" class="suner">这是子二div<price>45</price></div></div>
        <div id="s-isindex-wrap">这时第二个div
        </div>
        <div id="nologin">这时第三个div</div></body>
--------------------------------------------------
*************************5个res ######################### 
<div id="nologin">这时第三个div</div>
<class 'lxml.etree._Element'>
*************************5个res的父节点 ######################### 
<body><div id="head_wrapper">这是第一个div<div id="son_div">这是子一div<div>这是孙一div</div></div><div id="son_div2" class="suner">这是子二div<price>45</price></div></div>
        <div id="s-isindex-wrap">这时第二个div
        </div>
        <div id="nologin">这时第三个div</div></body>
--------------------------------------------------
************************* last() div ######################### 
<div>这是孙一div</div>
--------------------------------------------------
<div id="son_div2" class="suner">这是子二div<price>45</price></div>
--------------------------------------------------
<div id="nologin">这时第三个div</div>
--------------------------------------------------
************************* position()=2 的div ######################### 
<div id="son_div2" class="suner">这是子二div<price>45</price></div>
--------------------------------------------------
<div id="s-isindex-wrap">这时第二个div
        </div>
        
--------------------------------------------------
************************* position()<=2 的div ######################### 
<div id="head_wrapper">这是第一个div<div id="son_div">这是子一div<div>这是孙一div</div></div><div id="son_div2" class="suner">这是子二div<price>45</price></div></div>
        
--------------------------------------------------
<div id="son_div">这是子一div<div>这是孙一div</div></div>
--------------------------------------------------
<div>这是孙一div</div>
--------------------------------------------------
<div id="son_div2" class="suner">这是子二div<price>45</price></div>
--------------------------------------------------
<div id="s-isindex-wrap">这时第二个div
        </div>
        
--------------------------------------------------
************************* 带谓语选择条件的div ######################### 
<div id="son_div">这是子一div<div>这是孙一div</div></div>
--------------------------------------------------
************************* 形式一:带多个条件筛选的div ######################### 
<div id="son_div2" class="suner">这是子二div<price>45</price></div>
--------------------------------------------------
************************* 形式二:带多个条件筛选的div ######################### 
<div id="son_div2" class="suner">这是子二div<price>45</price></div>
--------------------------------------------------
************************* 模糊查询,只要带有son的都会被找到 ######################### 
<div id="son_div">这是子一div<div>这是孙一div</div></div>
--------------------------------------------------
<div id="son_div2" class="suner">这是子二div<price>45</price></div>
--------------------------------------------------
************************* 或者形式的查询,两个条件二选一 ######################### 
<div id="s-isindex-wrap">这时第二个div
        </div>
        
--------------------------------------------------
************************* 使用子元素节点的text作为筛选条件的 ######################### 
<div id="son_div2" class="suner">这是子二div<price>45</price></div>
--------------------------------------------------
************************* mod 作为筛选条件的使用 ######################### 
<div id="son_div2" class="suner">这是子二div<price>45</price></div>
--------------------------------------------------
************************* 使用恒等式作为谓语 ######################### 
<div id="head_wrapper">这是第一个div<div id="son_div">这是子一div<div>这是孙一div</div></div><div id="son_div2" class="suner">这是子二div<price>45</price></div></div>
        
--------------------------------------------------
<div id="son_div">这是子一div<div>这是孙一div</div></div>
--------------------------------------------------
<div>这是孙一div</div>
--------------------------------------------------
<div id="son_div2" class="suner">这是子二div<price>45</price></div>
--------------------------------------------------
<div id="s-isindex-wrap">这时第二个div
        </div>
        
--------------------------------------------------
<div id="nologin">这时第三个div</div>
--------------------------------------------------
************************* 使用是否包含节点作为谓语 ######################### 
<div id="son_div2" class="suner">这是子二div<price>45</price></div>
--------------------------------------------------

四、xpath案例

爬取段子网的段子,并存储起来。

import requests
from lxml import etree
import time
import random

url = 'https://duanziwang.com/page/%d/'
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3766.400 QQBrowser/10.6.4163.400"
}

if __name__ == '__main__':
    f = open('./joke.csv',mode='a',encoding='utf-8')
    for i in range(1,11):
        response = requests.get(url=url%i,headers=headers)
        response.encoding = 'utf-8'
        html = etree.HTML(response.text)
        # print(etree.tostring(html,encoding='utf-8').decode('utf-8'))
        node_list = html.xpath('//article[@class="post"]')
        for item in node_list:
            title = item.xpath('./div[@class="post-head"]/h1/a/text()')[0]
            datetime = item.xpath('./div/div/time[1]/text()')[0]
            hotRate = item.xpath('.//time[2]/text()')[0]
            like = item.xpath('.//span/text()')[0]
            content = item.xpath('./div[@class="post-content"]//code/text()')[0]
            # print(title,'\t',content)
            # strip()无参数代表两边去掉空格,strip('\n')代表两边去掉换行
            f.write('%s\t时间:%s\t热度:%s\t点赞%s\n%s\n'%(title.strip(),datetime.strip(),hotRate.strip(),like.strip(),content.strip()))
        time.sleep(random.randint(1,3))
    f.close()


运行结果:

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你好!要使用Python进行爬虫并解析网页内容,可以使用xpath进行数据提取。我可以帮你了解如何使用xpath进行爬虫。 首先,你需要安装相关的库。在Python中,你可以使用`lxml`库来解析HTML或XML文档,并使用`requests`库来发送HTTP请求并获取网页内容。你可以使用以下命令安装这些库: ``` pip install lxml pip install requests ``` 安装完成后,你可以在Python脚本中导入这些库: ```python import requests from lxml import etree ``` 接下来,你可以使用`requests`库发送HTTP请求来获取网页的内容。例如,你可以使用以下代码获取一个网页的内容: ```python url = "http://example.com" response = requests.get(url) html_content = response.text ``` 获取到网页内容后,你可以使用`lxml`库中的`etree.HTML`函数将其转换为可供解析的HTML对象。然后,你可以使用xpath表达式从HTML对象中提取数据。例如,假设你要提取网页中所有`<a>`标签的文本和链接,你可以使用以下代码: ```python html = etree.HTML(html_content) links = html.xpath("//a") for link in links: text = link.text href = link.get("href") print(text, href) ``` 在上述代码中,`//a`是xpath表达式,用于匹配所有`<a>`标签。然后,通过遍历匹配结果,你可以获取每个`<a>`标签的文本和链接。 这只是一个简单的示例,你可以根据具体的网页结构和需求来编写更复杂的xpath表达式来提取数据。 希望这些信息能对你有所帮助!如果你有任何其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值