Xpath语法

本文介绍了XPath语法,包括XML的基本概念、与HTML的区别,XPath的节点选择、谓语和选取路径。此外,详细讲解了Python的lxml模块,包括安装、基本使用,以及如何利用XPath解析XML和HTML,结合实例展示了XPath在爬虫中的应用。

Xpath语法

一、XML简介
1、什么是XML
  • XML 指可扩展标记语言(EXtensible Markup Language)

  • XML 是一种标记语言,很类似 HTML

  • XML 的设计宗旨是传输数据,而非显示数据

  • XML 的标签需要我们自行定义

  • XML 被设计为具有自我描述性

  • XML 是 W3C 的推荐标准

W3School 官方文档:http://www.w3school.com.cn/xml/index.asp

二、XML和HTML的区别

他们两者都是用于操作数据或者结构数据,在结构上大致相同的,但他们在本质上却存在着明显的区别

语法不同: xml的语法比html要求更加严格

  • xml属性不能为空,html属性可以只有属性名
  • xml的标签是严格闭合的。html是可以省略未标签的
数据格式描述设计目标
XMLExtensible Markup Language (可扩展标记语言)被设计为传输和存储数据,其焦点是数据的内容
HTMLHyperText Markup Language (超文本标记语言)显示数据以及如何更好显示数据
HTML DOMDocument Object Model for HTML (文档对象模型)通过 HTML DOM,可以访问所有的 HTML 元素,连同它们所包含的文本和属性,可以对其中的内容进行修改和删除,同时也可以创建新的元素

XML文档示例:

<?xml version="1.0" encoding="utf-8"?>

<bookstore>

  <book category="cooking">
    <title lang="en">Everyday Italian</title>  
    <author>Giada De Laurentiis</author>  
    <year>2005</year>  
    <price>30.00</price>
  </book>  
</bookstore>

HTML DOM模型:

HTML DOM 定义了访问和操作 HTML 文档的标准方法,以树结构方式表达 HTML 文档

1、XML的节点关系

1、父(Parent)

每个元素以及属性都有一个父

下面是一个简单的XML例子中,book 元素是 title、author、year 以及 price 元素的父:

<?xml version="1.0" encoding="utf-8"?>

<book>
  <title>Harry Potter</title>
  <author>J K. Rowling</author>
  <year>2005</year>
  <price>29.99</price>
</book>

2、子(Children)

元素节点可有零个、一个或多个子

在下面的例子中,title、author、year 以及 price 元素都是 book 元素的子:

<?xml version="1.0" encoding="utf-8"?>

<book>
  <title>Harry Potter</title>
  <author>J K. Rowling</author>
  <year>2005</year>
  <price>29.99</price>
</book>

3、同胞(Sibling)

拥有相同的父的节点

在下面的例子中,title、author、year 以及 price 元素都是同胞:

<?xml version="1.0" encoding="utf-8"?>

<book>
  <title>Harry Potter</title>
  <author>J K. Rowling</author>
  <year>2005</year>
  <price>29.99</price>
</book>

4、先辈(Ancestor)

某节点的父、父的父,等等

在下面的例子中,title 元素的先辈是 book 元素和 bookstore 元素:

<?xml version="1.0" encoding="utf-8"?>

<bookstore>

<book>
  <title>Harry Potter</title>
  <author>J K. Rowling</author>
  <year>2005</year>
  <price>29.99</price>
</book>

</bookstore>

5、后代(Descendant)

某个节点的子,子的子,等等

在下面的例子中,bookstore 的后代是 book、title、author、year 以及 price 元素:

<?xml version="1.0" encoding="utf-8"?>

<bookstore>

<book>
  <title>Harry Potter</title>
  <author>J K. Rowling</author>
  <year>2005</year>
  <price>29.99</price>
</book>

</bookstore>
三、XPath简介
1、什么是XPath?

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

W3School官方文档:http://www.w3school.com.cn/xpath/index.asp

2、选取节点

XPath 使用路径表达式来选取 XML 文档中的节点或者节点集。这些路径表达式和我们 在常规的电脑文件系统中看到的表达式非常相似。下面列出了最常用的路径表达式:

表达式描述
nodename选取此节点的所有子节点
/从节点选取
//匹配文档所有的节点,而不考虑他们的位置
.选取当前节点
选取当前节点的父节点
@选取属性

在下面的表格中,我们已列出了一些路径表达式以及表达式的结果:

路径路径表达式
bookstore选取 bookstore 元素的所有子节点
/bookstore选取根元素 bookstore。/代表元素的绝对路径
bookstore/book选取属于 bookstore 的子元素的所有 book 元素
//book选取所有 book 子元素,而不管它们在文档中的位置
bookstore//book选择属于 booksore 元素的后代所有的 book 元素,而不管他们位于 bookstore 之下的什么位置
//@lang选取名为 lang 的所有属性
text()取标签当中的值
3、谓语(Predicates)

谓语用来查找某个特定的节点或者包含某个指定的值的节点,被
嵌在方括号中,在下面的表格中,我们列出了带有谓语的一些路
径表达式,以及表达式的结果:

路径表达式结果
/bookstore/book[l]选取属于 bookstore 子元素的第一个 book 元素
/bookstore/book[last()]选取属于 bookstore 子元素的最后一个 book 元素
/bookstore/book[last()-1]选取属于 bookstore 子元素的倒数第二个 book 元素
/bookstore/book[position()❤️]选最前面的两个属于 bookstore 元素的子元素的 book 元素
//title[@lang]选取所有属性名为 lang 的属性的 title 元素
//titlel@lang=‘eng’]选取所有 tltle 元素,且这些元素有属性值为 eng 的 lang 属性
/bookstore/book[price>35.00]选取 bookstore 元素的所有 book 元素, 且其中的 price 元素的值须大于 35
/bookstore/book[price>35.00]/title选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素
4、选取未知节点
通用符描述
*匹配任何元素节点
@*匹配任何属性节点
node()匹配任何类型节点

在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:

路径表达式结果
/bookstore/*选取 bookstore 元素的所有子元素
//*选取文档中的所有元素
//title[@*]选取所有带有属性的 title 元素
5、选取若干路径

通过在路径表达式中使用“|”运算符,您可以选取若干个路径。在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:

路径表达式结果
//book/title//book/price选取 book 元素的所有 title 和 price 元素
//title//price选取文档中所有 title 和 price 元素
//price选取文档中所有的 price 元素
四、lxml模块
1、lxml简介与安装

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

我们可以利用之前学习的 XPath 语法,来快速的定位特定元素以及节点信息

安装方法:pip install lxml

2、初步使用

1、解析 HTML 字符串

XML 素材:http://www.cnblogs.com/zhangboblogs/p/10114698.html

小结: lxml 可以自动修正 html 代码,例子里不仅补全了 li 标签,还添加了 body,html 标签

2、lxml 文件读取

XML 素材:http://www.cnblogs.com/zhangboblogs/p/10114698.htm

除了直接读取字符串,lxml 还支持从文件里读取内容。我们新建一个 hello.html 文件,再利用 etree.parse()方法来读取文件

注意:从文件中读取数据,要求文件内容符合 xml 格式,如果标签缺失,则不能正常读取

五、XPath节点信息解析:
from lxml import etree

text = '''
<div>
    <ul>
        <li class="item-0"><a href="link1.html" class = "a_class1">first item</a></li>
        <li class="item-1"><a href="link2.html">second item</a></li>
        <li class="item-inactive">
         <a href="link3.html" class="bold"></a>
         <span class="span_item1">span_text1</span>
        </li>
        <li class="item-1">
            test
         <a href="link4.html">fourth item<span class="span_item2">span_text2</span></a>
      </li>
        <li class="item-0">
         <a href="link5.html">fifth item</a>
    </ul>

</div>
'''
# 1、将html_str---》html(element对象)
tree = etree.HTML(text)
# print(tree)

# 2、将element对象变成字符串
# print(etree.tostring(tree,pretty_print=True).decode('utf-8'))

# 1. 获取所有的 <li> 标签
# result = tree.xpath('//li')
# print(result)

# 2.继续获取<li> 标签的所有 class属性
# result = tree.xpath('//li/@class')
# print(result)

# 3.继续获取<li>标签下href 为 link1.html 的 <a> 标签
# result = tree.xpath('//li/a[@href="link1.html"]')
# print(result)

# 4.获取<li> 标签下的所有 <span> 标签(包括孙子span)
#标签/标签:两个标签是父子关系
#标签//标签:两个标签是先辈和后代关系
# result = tree.xpath('//li/span')
# print(result)

# 5.获取 <li> 标签下的<a>标签里的所有 class
# result = tree.xpath('//li/a/@class')
# print(result)

# 6.获取最后一个 <li> 的 <a> 的 href
# result = tree.xpath('//li[last()]/a/@href')
# print(result)

# 7.获取倒数第二个元素的内容
# result = tree.xpath('//*[last()-1]/text()')
# print(result)

# 8.获取 class 值为 bold 的标签名
# result = tree.xpath('//*[@class="bold"]')
# print(result)
六、爬虫案例
(1)、爬取扇贝单词
import requests
from lxml import etree
from excel_utils.excel_utils import Excel_Utils
def get_content(url):
    '''
    请求url,获取页面tree对象
    :param url:
    :return:
    '''
    headers= {
        'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36',

    }
    respone = requests.get(url,headers= headers)
    #测试--为了确定header请求头是否正确
    # print(respone.text)
    return etree.HTML(respone.text)

def pase_word(tree):
    '''
    解析每页的单词
    :param tree: 改页面的element对象
    :return:
    '''
    en_word_list = tree.xpath('//td[@class="span2"]/strong/text()')
    zh_word_list = tree.xpath('//td[@class="span10"]/text()')
    # print(en_word_list)
    # print(zh_word_list)
    for i,word in enumerate(en_word_list):
        item = {}
        item['en_word'] = word
        item['zh_word'] = zh_word_list[i]
        # print(item)
        infos.append(item)
def main():

    #1、确定url
    base_url = 'https://www.shanbay.com/wordlist/110521/232414/?page=%s'
    #2、实现分页
    for i in range(1,4):
        tree = get_content(base_url %i)
        #解析
        pase_word(tree)
    #4、保存数据
    Excel_Utils.write_to_excel(infos,'单词.xls')
if __name__ == '__main__':
    infos = []
    main()
(2)、网易云歌手爬取
import requests
from lxml import etree
from day04.excel_utils.excel_utils import Excel_Utils
def get_xpath(url):
    '''
    请求url,返回响应element对象
    :param url:
    :return:
    '''
    headers = {
        'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36',

    }
    response = requests.get(url,headers=headers)
    #测试
    # print(response.text)
    return etree.HTML(response.text)



def get_area_urls(url):
    '''
    获取区域列表
    :param url: 歌手首页
    :return: list
    '''
    #请求url,获取tree
    tree = get_xpath(url)
    return tree.xpath('//div[@class="blk"]/ul/li/a/@href')


def get_singer_words(url):
    '''
    获取歌手字母列表
    :param url:
    :return:
    '''
    tree = get_xpath(url)
    return tree.xpath('//ul[@id="initial-selector"]/li[position()>1]/a/@href')

def parse_singers(url):
    '''
    从歌手字母页面中解析歌手信息
    :param url:
    :return:
    '''
    tree = get_xpath(url)
    #歌手的名称
    singer_names = tree.xpath('//ul[@id="m-artist-box"]/li/p/a/text()|//ul[@id="m-artist-box"]/li/a/text()')
    singer_urls = tree.xpath('//ul[@id="m-artist-box"]/li/p/a/@href|//ul[@id="m-artist-box"]/li/a/@href')
    # print(singer_names)
    # print(singer_urls)
    for i ,name in enumerate(singer_names):
        item = {}
        item['singer_name'] = name
        item['singer_url'] = singer_urls[i]
        print(item)
        singers.append(item)
    #歌手详情页链接
def main():
    #1、获取区域列表,urls
    #经验:如果url里面有锚点,我们要特别注意了。
    base_url = 'https://music.163.com/discover/artist'
    area_urls = get_area_urls(base_url)
    # print(area_urls)
    #2、循环请求区域列表的url,进入各个区域
    for area_url in area_urls:
        # 3、获取歌手字母列表(urls)
        singer_words = get_singer_words('https://music.163.com'+area_url)
        # print(singer_words)
        #4、分别请求歌手名字字母url进入该页面。
        for word in singer_words:
            # 5、从页面中获取歌手信息。
            parse_singers('https://music.163.com'+word)

    #写入excel
    Excel_Utils.write_to_excel(infos=singers,filename='网易云歌手信息.xls')


if __name__ == '__main__':
    singers = []
    main()
(3)、古诗文爬取
import os
import re
from excel_utils.excel_utils import Excel_Utils
import requests
from lxml import etree


def get_xpath(url):
    '''
    请求url,获取element对象
    :param url:
    :return:
    '''
    headers = {
        'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36',

    }
    response = requests.get(url, headers=headers)
    # print(response.text)
    return etree.HTML(response.text)


def parse_song(tree, url):
    '''
    解析诗歌
    :param tree: 诗歌详情页的element对象
    :return:
    '''
    print(1)
    # 待爬取字段:古诗文名、作者、朝代、内容、译文及注释;
    song_title = tree.xpath('//h1/text()')[0]
    # print(song_title)
    song_author = tree.xpath('//p[@class="source"]/a/text()')[1]
    song_dynasty = tree.xpath('//p[@class="source"]/a/text()')[0]
    song_id = re.search(r'_(\w+)\.aspx', url).group(1)
    # print(song_id)
    song_content = tree.xpath('string(//div[@id="contson{}"])'.format(song_id))
    # print(song_content)
    song_fanyi = tree.xpath('string(//div[@class="contyishang"]/p[1])')
    song_zhushi = tree.xpath('string(//div[@class="contyishang"]/p[2])')
    item = {}
    item['song_title'] = song_title
    item['song_author'] = song_author
    item['song_dynasty'] = song_dynasty
    item['song_id'] = song_id
    item['song_content'] = song_content
    item['song_zhushi'] = song_zhushi
    item['song_fanyi'] = song_fanyi
    print(item)
    infos.append(item)


def write_to_file():
    # print(2)
    # 写入excel
    filename = '古诗文.xls'
    if not os.path.exists(filename):
        Excel_Utils.write_to_excel(infos, filename)
        infos.clear()
    else:
        Excel_Utils.append_to_excel(infos, filename)
        infos.clear()


def main():
    # 1、从首页中获取诗词分类url
    base_url = 'https://www.gushiwen.org/'
    tree_index = get_xpath(base_url)
    song_type_urls = tree_index.xpath('//div[@class="right"]/div[@class="sons"]/div[@class="cont"]/a/@href')
    # print(song_type_urls)
    # 2、循环氢气分类url,进入分类页面。
    for type_url in song_type_urls:
        # 请求
        tree_type = get_xpath(type_url)
        # 3、获取该分类下的所有古诗的url
        song_urls = tree_type.xpath('//div[@class="typecont"]/span/a/@href')
        # 4、循环请求这些url,从每个古诗详情页中解析古诗信息。
        # print(song_urls)
        for url in song_urls:
            tree_song = get_xpath(url)
            parse_song(tree_song, url)
        # 写入文件
        write_to_file()


if __name__ == '__main__':
    infos = []
    main()

(4)、小说爬取
import os

import requests
from lxml import etree
from excel_utils.excel_utils import Excel_Utils


def get_xpath(url):
    '''
    请求url,返回响应element对象
    :param url:
    :return:
    '''
    headers = {
        'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36',

    }
    response = requests.get(url, headers=headers)
    # 测试
    # print(response.text)
    return etree.HTML(response.text)


def get_text(text):
    if text:
        return text[0]
    return ''


def parse_page(tree):
    '''
    解析每页
    :param tree:
    :return:
    '''
    li_list = tree.xpath('//div[@class="ls"]/ul/li')
    # print(li_list)
    for li in li_list:
        book_name = get_text(li.xpath('.//div[@class="sm"]/a/text()'))
        book_url = get_text(li.xpath('.//div[@class="sm"]/a/@href'))
        book_desc = get_text(li.xpath('.//div[@class="jj"]/a/text()'))
        book_word_num = get_text(li.xpath('.//div[@class="zs"]/text()'))
        book_author = get_text(li.xpath('.//div[@class="zz"]/text()'))
        item = {}

        item['book_name'] = book_name
        item['book_url'] = book_url
        item['book_desc'] = book_desc
        item['book_author'] = book_author
        item['book_word_num'] = book_word_num
        print(item)
        infos.append(item)


def main():
    # 1、确定url
    base_url = 'https://xiaoshuo.2345daohang.com/sort.html'
    # 2、获取分类url
    tree_index = get_xpath(base_url)
    type_urls = tree_index.xpath('//div[@id="left"]/div[position()>2]/a/@href')

    # 3、请求分类,点击分类,在右边的显示中取找数据。
    for t_url in type_urls:

        # 4、确定分类中分页。
        # 获取最大也的页码
        # result = tree_type.xpath('//div[@class="page"]/text()')[0]
        t_url = 'https://xiaoshuo.2345daohang.com' + t_url
        for i in range(1, 101):

            if i == 1:
                tree_type = get_xpath(t_url)
            else:
                # /list_20.htm
                t_url_new = t_url.replace('.htm', '_{}.htm'.format(i))
                # print(t_url_new)
                # print('=================')
                tree_type = get_xpath(t_url_new)

            parse_page(tree_type)
            # 保存数据
            if not os.path.exists(filename):
                Excel_Utils.write_to_excel(infos, filename)
                infos.clear()
            else:
                Excel_Utils.append_to_excel(infos, filename)
                infos.clear()


if __name__ == '__main__':
    filename = '小说.xls'
    infos = []
    main()

七、将爬取信息写入Excel文件中
import xlwt

class Excel_Utils:

    @staticmethod
    def write_to_excel(infos,filename,sheetname = 'sheet1'):
        '''
        写入excel
        :param infos: 写入的数据:[{item},{}]
        :param filename: 文件名
        :param sheetname: sheet名
        :return:
        '''
        #1、创建工作博
        work_book = xlwt.Workbook(encoding='utf-8')
        #2、创建一个sheet
        sheet = work_book.add_sheet(sheetname)
        #3、写标头
        head = list(infos[0].keys())
        print(head)
        for i in range(len(head)):
            sheet.write(0,i,head[i])

        #4|写内容
        rows = 1
        for item in infos:
            for j in range(len(head)):
                sheet.write(rows,j,item[head[j]])

            rows+=1
        #5、保存
        work_book.save(filename)
        print('写入成功!')
八、将爬取信息追加到Excel中
    from xlutils.copy import copy
    

    @staticmethod
    def append_to_excel(infos, filename, sheetname='sheet1'):
        '''
        追加数据到excel
        :param infos: 【{item},{item}。。。。】
        :param filename:
        :param sheetname:
        :return:
        '''
        # 1、打开excel文件
        work_book = xlrd.open_workbook(filename=filename)
        # 2、获取所有表单的名称
        sheet_names = work_book.sheet_names()
        # 3、通过名字来获取sheet表单
        sheet = work_book.sheet_by_name(sheet_names[0])
        # 4、读取行数
        old_rows = sheet.nrows
        # 5、将xlrd的workbook变成xlwt的workbook
        new_work_book = copy(work_book)
        new_sheet = new_work_book.get_sheet(0)
        # 写入
        head = sheet.row_values(0)
        for item in infos:
            for j in range(len(head)):
                new_sheet.write(old_rows, j, item[head[j]])
                old_rows += 1
        new_work_book.save(filename)
        print('追加成功!')
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值