前言:
最近有个项目,需要用一个通用化的html提取规则,之前是使用bs4的,但是考虑到bs4的速度慢和规则繁琐,就想弃坑。同时正则也是可以提取
xpath(XML Path Language)重要吗,那是肯定的,因为这是一套标准,类似于是正则表达式
,起初是 XML 路径语言,用于对元素和属性进行查找,而且解析速度超级快。
看到这里,你就知道了,为什么我要学习 xpath 了,因为爬虫中最重要的就是数据提取。Python中的主流的提取库有:
- BeautifulSoup(bs4):美丽汤,语法多,文档完善。缺点:速度慢,因为是建立在别的解析器的基础上的封装。
- lxml:支持xpath中的部分语法,并有额外的功能
- re:正则,适用于少量数据提取。
另外有网友对比了 bs4 和 lxml 的解析速度,发现还是 lxml 最快最方便,所以我选择系统的学习xpath语法和lxml库。
xpath的语法扫盲:
下面我将从一个初学者的角度来写文章,
1、 xpath节点
xpath中的有七种节点有:元素、属性、文本、命名空间、处理指令、评论、文档节点。通常把 xml 的代码看做是节点树,里面的所有内容都可以看做是节点,这样就利于数据的提取。
<bookstore> <book> <title>Harry Potter</title> <author>J K. Rowling</author> <year>2005</year> <price>29.99</price> </book> </bookstore>
1.1 父节点(Parent)
其实是相对而言的,比如 title
和 year
的父节点就是book
,而book
的父节点就是bookstore
,
1.2 孩子节点
一个节点的孩子节点可以有很多,比如 book 节点的子节点有:title、author、year、price
1.3 兄弟节点(Sibling)
比如:title、author、year、price都是兄弟节点
1.4 祖先节点(Ancestor)
节点的父节点,以及父节点的父节点,父节点的父节点,依次类推都是祖先,说直白点就是该节点上的所有父节点都是被称为:祖先节点。
1.5 后代节点(Descendant)
某个节点的所有后代子节点,子节点以及子节点的子节点依次类推。比如 bookstore 的所有所有字节就有:book、title、author、year、price。
2、xpath语法:
2.1 选取节点
表达式 | 描述 |
---|---|
nodename | 选取此节点的所有子节点。 |
/ | 从根节点开始取元素 |
// | 从当前匹配的节点后选择节点,不考虑位置 |
. | 选取当前节点 |
.. | 选取当前节点的父节点 |
@ | 取节点的属性 |
说实在的上面的解释确实抽象,尤其是第一次学习的时候,所以建议配合下面的xpath实战例子进行理解。
1)实战例子:
光说不练假把式,下面请看实际例子。我以网页:https://www.w3cschool.cn/xpath/xpath-syntax.html作为例子,使用xpath
路径表达式 | 结果 |
---|---|
bookstore | 选取 bookstore 元素的所有子节点。 | />
/bookstore | 选取根元素 bookstore。注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径! | />
bookstore/book | 选取属于 bookstore 的子元素的所有 book 元素。例如下面的 | />
//book | 选取所有 book 子元素,而不管它们在文档中的位置。 | />
bookstore//book | 选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。 | />
//@lang | 选取名为 lang 的属性的所有节点。例如,这里面我是://@class :表示选取所有带有class 属性的节点,返回的结果是 class 属性对应的属性值。 | />
2.2 xpath谓语
按照谓语的字面意思就是,具有动作的xpath语法,比如取某个节点的孩子节点中的第3个、取 class=“test”
的 div 节点等。前面学习的选取语法就做不到了,需要使用谓语进行筛选结果。
谓语都是放在[]
中,具有筛选作用。
<bookstore> <book price="45"> <name>book1</name> <title class="book1_title">Harry Potter</title> <author>J K. Rowling</author> <year>2005</year> <price>29</price> </book> <book price="68"> <name>book2</name> <title class="book2_title">Harry Potter02</title> <author>J K. Rowling02</author> <year>2005 02</year> <price>68</price> </book> </bookstore>
下面表格中的结果,我以
路径表达式 | 含义 | 结果 |
---|---|---|
/bookstore/book[1]或者//book[1] | 取bookstore的子节点中第一个 book 元素。[index],index是索引号,从1开始。 | book1 |
/bookstore/book[last()] | 取bookstore的子节点中最后一个 book 元素。last()表示最后一个元素 | book2 |
/bookstore/book[last()-1] | 取bookstore的子节点中倒数第二个 book 元素。last()-index,表示取倒数第index个元素。 | book1 |
/bookstore/book[positon()<2] | 取bookstore的子节点中位置序号小于2 book 元素。position()是位置序号,从1开始,positon()<2=1,positon()>2=3, 4, …… | book1 |
/name[@lang] | 表示所有拥有名为 lang 的属性的 name 节点。例如:/name[@class],则结果为:book1和book2 | |
/name[@lang=“ch”] | 选取所有名为 lang 的属性,且值为 ch 的 name 节点。例如:/name[@class=“book1_title”],则结果为:book1 | |
/bookstore/book[price>30] | 选取 bookstore 下所有 book 节点中,其中book的子节点 price 的值大于30。 | book1, book2 |
/bookstore/book[price>30]/title | 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 30。 |
其他用法:
通配符 | 描述 |
---|---|
* | 匹配任何节点。比如://book[1]/*,表示匹配到第一个 book 节点下的所有节点,结果:name、title、price、author、year |
@* | 匹配任何属性节点。比如://name[@*],表示所有带有属性的 name 节点。 |
node() | 匹配任何类型的节点。比如://book/node(),结果:name、title、price、author、year |
2.3 xpath轴
总结:
参考链接: