​ 通过 requests 模块,我们可以很简单地把网页下载到本地,但是此时获取的网页都是未经处理的,冗余的信息太多,无法进行分析和利用。那么怎么从网页中筛选自己需要的信息呢?

​ 说到信息筛选我们可能会想到正则表达式,不过由于正则表达式过于复杂而且容错率低,网页有稍微的改动就要重写匹配表达式,对于新手来说十分不友好。

​ 那么我们应该使用什么呢?别担心,我们还有很多种解析 HTML页面的方法,例如:Xpath

lxml

lxml 是一款高性能的 Python XML 解析库,它天生支持 XPath1.0,XSLT1.0,定制元素类,甚至 Python风格的数据绑定接口。它构建在两个 C 库之上:libxml2libxslt。它们为执行解析,序列化和转换等核心任务提供了主要动力

安装

pip install lxml

使用

lxml 中大部分的功能都位于 lxml.etree 模块中,导入 lxml.etree 模块的常见方式如下:

from lxml import etree
  • etree.HTML(string)

    将 string 解析为 Element 或者 ElementTree,同时修复非法标签

  • etree.parse(<file_name>)

    将文件或者是 file_like 对象解析为 ElementTree (not an Element object)

  • etree.tostring(Element, encoding=‘utf-8’)

    将一个 Element 或者 ElementTree 转换为 string 形式

  • ElementTree.write(file)

    这个是 ElementTree 特有的方法,将 ElementTree 写到 file 中

Element 属性 描述
.tag 元素名称
.attrib 元素属性的字典
.text 第一个子元素标签之前的文本。Element的文本均为直接自文本,不包含子元素中的文本
.tail Element 的关闭标签之后的文本,并且是在下一个标签之前的部分,没有则为None
Element 方法 描述
append(child) 添加一个子节点到当前 Element
insert(index,element) 插入子元素 element 到指定位置
clear() 移除 element 所有内容
remove(child) 将子节点从 Element 中移除
getchildren() 返回 Element 子元素的列表
iterchildren(tag=etree.Element, reversed=False) 通过设置 reversed 可以反向顺序迭代子元素
getiterator(tag=None,*tags) 递归返回 Element 子元素的生成器,可使用 tag 参数过滤返回的元素,深度优先,先根遍历
getroottree() 返回元素的 Elementree
iter(tag=None,*tags) 过滤特定标签,生成迭代器。默认情况下,iter() 迭代所有的节点,包括PI(处理指令) 、Comment(注释) 等。如果只想迭代标签元素,可以使用 Element factory 做参数 e.iter(tag = etree.Element)
itertext(tag=None, *tags, with_tail=True) 迭代 Element 元素的文本内容,with_tail 参数决定是否迭代子元素的 tail 。Element 的tail 不会进行迭代
iterancestors( tag=None ) 迭代所有 Element 先辈,可以使用 tag 过滤
itersiblings( preceding=False ) 迭代 Element 之后的兄弟元素,可以通过设置 preceding=True 仅迭代 Element 之前的兄弟元素
items() 返回 Element 属性的键值所构成的(name,value)元组的列表
keys() 返回元素属性名的列表
get(key,default=None) 返回字符串形式的 属性 key 的值,没有返回 None
set(A,V) 创建或者改变属性 A 的值为 V

什么是Xpath

Xpath,全称 XML Path Language,即 XML路径语言,是一门在 XML文档中查找信息的语言,最初用来搜寻 XML文档,但是它同样适用于 HTML文档的搜索

Xpath中,有七种类型的节点:元素,属性,文本,命名空间,处理指令,注释以及文档节点(根节点)

Xpath 使用路径表达式选取节点。节点是通过沿着路径或者 step 来选取的

节点关系

<bookstore>

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

</bookstore>
节点关系 描述
父(Parent) book元素是 titleauthor 等元素的父
子(Children) titleauthorbook 元素的子
同胞(Sibling) 拥有相同的父节点,titleauthoryear都是同胞
先辈(Ancestor) 某节点的父,父的父等。title 元素的先辈是 bookbookstore
后代(Descendant) 某节点的子,子的子等。bookstore 的后代是 booktitle

常用规则

选取节点,常用路径表达式

表达式 描述
/ 从根节点选取
// 选取所有节点,不考虑它们的位置
. 选取当前节点
选取当前节点的父节点
@ 选取属性
nodename 选取此节点的所有子节点
* 匹配任何元素节点
@* 匹配任何属性节点
text() 取标签当中的值

谓语

谓语的作用就是做过滤的,过滤条件写在 []

表达式 描述
/bookstore/book[1] 选取属于 bookstore 子元素的第一个 book 元素
/bookstore/book[last()] 选取属于 bookstore 子元素的最后一个 book 元素
/bookstore/book[last()] 选取属于 bookstore 子元素的倒数第二个 book 元素
/bookstore/book[position()< 3] 选取最前面的两个属于 bookstore 元素的子元素的 book 元素
//book[@lang] 选取所有拥有名为 lang 的属性的 title 元素
//book[@lang=‘eng’] 选取所有拥有 lang 属性值为 eng 的 book 元素
//book[price>35] 选取所有拥有 price元素且值大于35 的 book 元素

运算符

运算符 描述
| 返回两个节点集
+ 加法。6+4=10
- 减法。6-4=2
* 乘法。3*2=6
div 除法。4 div 2 = 2
mod 余数。5 mod 2 = 1
= 等于。1=1 返回 true
!= 不等于。9 != 9 返回 false
< 小于
<= 小等于
> 大于
>= 大等于
or
and

通过 轴 可以获取 祖先节点,属性值,兄弟节点等

使用方式:轴::[谓语]

  • child::* :所有子节点
  • child::div :所有 div 子节点
  • child::div[@class='item']:所有 class=item 的 div 子节点
描述
ancestor 选取当前节点的所有先辈(父、祖父等)。
ancestor-or-self 选取当前节点的所有先辈(父、祖父等)以及当前节点本身。
attribute 选取当前节点的所有属性。
child 选取当前节点的所有子元素。
descendant 选取当前节点的所有后代元素(子、孙等)。
descendant-or-self 选取当前节点的所有后代元素(子、孙等)以及当前节点本身。
following 选取文档中当前节点的结束标签之后的所有节点。
following-sibling 选取当前节点之后的所有兄弟节点
preceding 选取文档中当前节点的开始标签之前的所有节点。
preceding-sibling 选取当前节点之前的所有同级节点。
namespace 选取当前节点的所有命名空间节点。
parent 选取当前节点的父节点。
self 选取当前节点。

函数

函数 描述
position() 返回节点的 index 的位置
last() 返回节点的个数
contains(string1, string2) string1 是否包含 string2
text() 返回文本节点
comment() 返回注释节点
normalize-space(string) 去除首位空格,中间多个空格用一个空格代替
substring(string, start, len) 返回从 start 位置开始的指定长度的子字符串,第一个字符下标为1
substring-before(string1, string2) 返回 string1中位于第一个 string2 之前的部分
substring-after(string1, string2) 返回string1中位于第一个string2之后的部分

使用示例

# 获取父节点 获取 id=title 的 a 标签 父元素的 class 属性
xpath('//a[id="title"]/../@class')

# 模糊查询 获取 class 以 it 开头的 a 标签
xpath('//a[contains(@class, "it")]')

# 多条件匹配
xpath('//a[@class="title" and @name="t"]')

# 指定位置匹配
# ul 中最后一个 li 标签
xpath('//ul/li[last()]')

# ul 中倒数第二个 li 标签
xpath('//ul/li[last()-1]')

# ul 中前三个 li 标签
xpath('ul/li[position<4]')