XPath是专门针对xml设计的,在复杂结构化数据中查找信息的语言。简单来说,就是利用一条路径表达式,找到我们需要的数据位置。
html可以看做不标准的xml,在html中也能使用XPath查找数据。
xpath可以用来在XML文档中对元素和属性进行遍历。
在 xpath 中,所有事物都是节点。共有七种类型的节点:
- 元素节点 : 元素是由从开始标签到结束标签的所有代码
- 属性节点 : 每一个元素节点有一个相关联的属性节点集合
- 文本节点 : 文本节点包含了一组字符数据,即cdata中包含的字符
- 命名空间节点 : 每一个元素节点都有一个相关的命名空间节点集
- 处理指令节点 : 处理指令节点对应于XML文档中的每一条处理指令
- 注释节点 : 注释节点对应于文档中的注释
- 文档(根)节点 : 根节点是节点树的最上层,根节点是唯一的
★ XPath可以用来选择这7种节点,但今天只涉及最常用的元素节点。因此,可以将下文中的节点和元素视为同义词。
节点关系
- 父(Parent) 每个元素以及属性都有一个父
- 子(Children) 元素节点可有零个、一个或多个子
- 兄弟(Sibling) 拥有相同的父的节点
- 先辈(Ancestor) 某节点的父、父的父,等等
- 后代(Descendant) 某个节点的子,子的子,等等
1)选取节点常用的路径表达式
- / 从根节点选取
- // 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置
- . 选取当前节点
- @选取属性
2)谓语
为路径表达式的附加条件,对节点进行进一步的筛选,被镶嵌在[ ]中
常用的Xpath函数
- text(): 文本定位位置,精确匹配 – text()=’’
- contains(): 判断字符串的一部分,模糊匹配 – contains(text(),’’) contains(@class,’’)
- starts/ends-with: 匹配一个字符串开始/结束位置的关键字 – starts/ends-with(@class,’’)
- position(): 表示节点的序号 – position()=1 position()>2
- last() : 最后一个节点 – last()
- and/or : and 与 ; or 或 – a[@class and @href] a[@class or @href]
- not() : 表示否定 – a[not(@class)]
3)通配符 - 匹配任何元素节点@*的任何属性
- //* 选择文档中的所有元素节点
- //ul/* 选择ul元素的所有子节点
- //a[@*] 选择所有带有属性的a节点
4)运算符 - | 可以选取多个并列的路径
- 例如://a[@tppabs]|//a[@class] 选择所有具有tppabs属性的a标签或具有class属性的a标签
Xpath的书写示例Harry Potter J K. Rowling 2005 29.99 # 文档节点 # 元素节点,属于 的子节点 / / / # 元素节点,属于 节点的子节点 Harry Potter lang # 属性节点,是节点的属性 Harry Potter # 文本节点,是 节点的文本
| 路径表达式 | 结果 |
|---|---|
| /bookstore/book[1] | 选取属于bookstore子元素的第一个book元素 |
| /bookstore/book[last()] | 选取属于 bookstore 子元素的最后一个 book 元素 |
| /bookstore/book[position()<4] | 选取最前面的三个属于 bookstore 元素的子元素的 book 元素 |
| /bookstore/book[last()-1] | 选取属于 bookstore 子元素的倒数第二个 book 元素 |
| //title[@lang] | 选取所有拥有名为lang属性的title元素 |
| //title[@lang=’en’] | 选取所有 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 |
Xpath的通配符可以解决选取未知的XML元素的问题
| 路径表达式 | 结果 |
|---|---|
| //book/title | //book/price | 选取 book 元素的所有 title 和 price 元素 |
| //title | // price | 选取文档中的所有 title 和 price 元素 |
| /bookstore/book/title | //price | 选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素 |
lxml库的介绍
lxml库是解释一个HTML/XML的解析器,主要功能是解析和提取HTML/XML的数据。
lxml库的安装
方法一:在cmd命令行中安装,语句如下:
pip install lxml
方法二:在pycharm中安装
file->settings->Project Python->Project Interpreter->点击+号->输入lxml->点击install,进行下载
lxml的简单使用
etree将文本转成html:
# 将文本转成html对象 html = etree.HTML(text) # 将html对象转成html的文本信息 etree.tostring(html)
示例:
from lxml import etree
text = '''
- first item
- second item
- third item
- fourth item
- fifth item
输出的结果会自动的添加标签并且补齐缺少的标签
Xpath和lxml的联合使用
此处将以爬取湖北经济学院官网的新闻为例
1、分析网页- 从上图可以看出每页内容全部在ul标签下的li中。
- 标题在li/div[1]/span[2]/a中,时间在li/div[2]/span,具体内容链接为li/div[1]/span[2]/a/@href
import requests from lxml import etree import pandas as pd3、分析url链接
url1='http://news.hbue.edu.cn/jyyw/list1.htm' url2='http://news.hbue.edu.cn/jyyw/list2.htm' #开始的url链接,每换一个页面,其list后面的数字会发生变化,可以采用for循环进行遍历 http://news.hbue.edu.cn/b5/7d/c7592a243069/page.htm http://edu.cnhubei.com/2019xajc/2021-07/06/c13911566.html #此处为具体内容的链接,可以通过再一次get其链接,但是存在一些其他的链接,需要进行数据的筛选4、循环访问标题页面并将内容保存在相应的列表中
for page in range(1,251):
url='http://news.hbue.edu.cn/jyyw/list'+str(page)+'.htm'
r=requests.get(url)
r.encoding='utf-8'
rqs=r.text
html=etree.HTML(rqs)
li_list=html.xpath('//*[@id="wp_news_w7"]/ul')
for li in li_list:
title=li.xpath('./li/div[1]/span[2]/a/text()')
new_url=li.xpath('./li/div[1]/span[2]/a/@href')
time=li.xpath('./li/div[2]/span/text()')
titles.append(title)
new_urls.append(new_url)
times.append(time)
- 第一步:通过for循环对初始页面进行访问,想要的内容都在li标签下,将所有的li的内容保存在li_list中。
- 第二步:通过遍历li_list将要爬取的内容保存在对应的列表中。
注意:此处保存的内容是一个矩阵,需要以下步骤将矩阵元素存储在一个列表中。
for time_lists in times:
for c in time_lists:
time_list.append(c)
for title_lists in titles:
for b in title_lists:
title_list.append(b)
for url_lists in new_urls:
for a in url_lists:
url_list.append(a)
6、处理属性标签中数据的异构
content_lists = []
for i in range(len(url_list)):
if 'http:' in url_list[i]:
content = 'other thing'+url_list[i]
elif 'https:' in url_list[i]:
content = 'other thing'+url_list[i]
else:
new_url = 'http://news.hbue.edu.cn' + url_list[i]
new_r = requests.get(new_url)
new_r.encoding = 'utf-8'
new_r1 = new_r.text
new_html = etree.HTML(new_r1)
content = new_html.xpath('//div[@]/div/p//text()')
content = ''.join(content)
content_lists.append(content)
- 第一步:遍历具体内容的url的列表,判断if-else列表元素是否已经存在**‘http’或者‘https**’的前缀,存在就将content赋值为other thing+url内容。
- 第二步:将对应的内容保存在content_list列表中。
dataframe=pd.Dataframe({'标题':title_list,'时间':time_list,"内容":content_lists})
dataframe.to_csv('D:/桌面2/湖北经济学院新闻.csv',sep=',',index=False,encoding='utf-8-sig')
- 通过pandas库将数据转换为Dataframe并保存在CSV文件中。
注意:此处的编码采用utf-8-sig,以避免文件出现乱码
如果采用utf-8在CSV文件中可能会出现乱码,建议将utf-8改成utf-8-sig
utf-8和utf-8-sig的区别- ”utf-8“ 是以字节为编码单元,它的字节顺序在所有系统中都是一样的,没有字节序问题,因此它不需要BOM,所以当用"utf-8"编码方式读取带有BOM的文件时,它会把BOM当做是文件内容来处理, 也就会发生类似上边的错误.
- “utf-8-sig"中sig全拼为 signature 也就是"带有签名的utf-8”, 因此"utf-8-sig"读取带有BOM的"utf-8文件时"会把BOM单独处理,与文本内容隔离开,也是我们期望的结果.
import requests
from lxml import etree
import pandas as pd
# url='http://news.hbue.edu.cn/jyyw/list.htm'
# http://news.hbue.edu.cn/b5/7d/c7592a243069/page.htm
# http://news.hbue.edu.cn/jyyw/list2.htm
# http://edu.cnhubei.com/2019xajc/2021-07/06/c13911566.html
titles = []
times=[]
new_urls=[]
news_urls=[]
url_list=[]
title_list=[]
time_list=[]
for page in range(1,251):
url='http://news.hbue.edu.cn/jyyw/list'+str(page)+'.htm'
r=requests.get(url)
r.encoding='utf-8'
rqs=r.text
html=etree.HTML(rqs)
li_list=html.xpath('//*[@id="wp_news_w7"]/ul')
for li in li_list:
title=li.xpath('./li/div[1]/span[2]/a/text()')
new_url=li.xpath('./li/div[1]/span[2]/a/@href')
time=li.xpath('./li/div[2]/span/text()')
titles.append(title)
new_urls.append(new_url)
times.append(time)
for time_lists in times:
for c in time_lists:
time_list.append(c)
for title_lists in titles:
for b in title_lists:
title_list.append(b)
for url_lists in new_urls:
for a in url_lists:
url_list.append(a)
content_lists = []
for i in range(len(url_list)):
if 'http:' in url_list[i]:
content = 'other thing'+url_list[i]
elif 'https:' in url_list[i]:
content = 'other thing'+url_list[i]
else:
new_url = 'http://news.hbue.edu.cn' + url_list[i]
new_r = requests.get(new_url)
new_r.encoding = 'utf-8'
new_r1 = new_r.text
new_html = etree.HTML(new_r1)
content = new_html.xpath('//div[@]/div/p//text()')
content = ''.join(content)
content_lists.append(content)
dataframe=pd.Dataframe({'标题':title_list,'时间':time_list,"内容":content_lists})
dataframe.to_csv('D:/桌面2/湖北经济学院新闻.csv',sep=',',index=False,encoding='utf-8-sig')
结果
- 一共爬取了3490条数据。
2021年9月29日



