Python如何使用XPath对HTMl内容解析
文章目录
- Python如何使用XPath对HTMl内容解析
- HTMl内容解析
- HTML基础:
- 什么是XPath:
- lxml的安装
- XPath语句格式
- XPath的特殊情况
- XPath常用表达式:
- XPath的使用步骤:
- 使用xpath时候的一些坑原因分析:
HTMl内容解析
HTML基础:
HTML也就是前面章节提到的网页源代码,是一种结构化的标记语言。HTML可以描述一个网页的结构信息。
HTML与CSS(Cascading Style Sheets,层叠样式表)、JavaScript一起构成了现代互联网的基石。
先以地名为例,来看HTML代码的结构关系:
<中国>
<北京>
<海淀区>
<五道口>
××面馆
</五道口>
</海淀区>
<东城区></东城区>
</北京>
<山东>
<青岛></青岛>
<烟台></烟台>
</山东>
</中国>
在这个以地名表示HTML结构的例子中,出现了很多用尖括号括起来的地名,而且这些地名都是成对出现的。有<北京>就有</北京>,有<山东>就有</山东>。在HTML中,这叫作标签。一个标签可以表示为:
<标签名>
文本
</标签名>
不加斜杠,表示标签开始;加上斜杠,表示标签结束。它们中间的部分,就是标签里面的元素。标签里面可以是另一个标签,也可以是一段文本。标签可以并列,也可以嵌套。例如<北京>与<山东>就属于并列关系。而<北京>与<海淀区>就是属于嵌套的关系。
不论谁在谁旁边,不论谁包含了谁,通过HTML 的这种表示方法,都可以轻易将不同标签的相对关系表现出来。
再来看一段真正的HTML代码的结构:
<html>
<head>
<title>测试</title>
</head>
<body>
<div class="useful">
<ul>
<li >我需要的信息1</li>
<li >我需要的信息2</li>
<li >我需要的信息3</li>
</ul>
</div>
<div class="useless">
<ul>
<li >垃圾1</li>
<li >垃圾2</li>
</ul>
</div>
</body>
</html>
对比这一段真实的HTML代码和上面地名的例子,可以看到,在结构上面,它们是完全一样的。只不过在真实的HTML代码里面,每个标签除了标签名以外,还有“属性”。一个标签可以有0个、1个或者多个属性,所以一个真正的HTML标签应该是下面这样的:
<标签名 属性1="属性1的值" 属性2="属性2的值">显示在网页上的文本</标签名>
它可以被表示成一个倒立的树形结构,如图1:
HTML就是通过这样一种一层套一层的结构来描述一个网页各个部分的相对关系的。这里的、
等都是HTML的标签。如果把HTML最外层的标签当作树根,从树根上面分出了两个树枝和,里面又分出了class分别为useful和useless的两个树枝
……正如北京在中国里面,清华大学在北京里面……因此,根据每个树枝独特的标志,一步一步找下去,就可以找到特定的信息。
什么是XPath:
XPath(XML Path)是一种查询语言,它能在XML(Extensible Markup Language,可扩展标记语言)和HTML的树状结构中寻找结点。
形象一点来说,XPath就是一种根据“地址”来“找人”的语言。
用正则表达式来提取信息,经常会出现不明原因的无法提取想要内容的情况。最后即便绞尽脑汁终于把想要的内容提取了出来,却发现浪费了太多的时间。
需要寻找的内容越复杂,构造正则表达式所需要花费的时间也就越多。
而XPath却不一样,熟练使用XPath以后,构造不同的XPath,所需要花费的时间几乎是一样的,所以用XPath从HTML源代码中提取信息可以大大提高效率。
在Python中,为了使用XPath,需要安装一个第三方库:lxml。
lxml的安装
通用安装:
pip install lxml
2.在Ubuntu下安装lxml
如果操作系统为Ubuntu,可以使用如下命令安装lxml:
sudo apt-get install python-lxml
3.在Windows下安装lxml
如果操作系统为Windows,那么安装lxml的过程比较麻烦。
pip install lxml
检查xpath安装是否成功:
如图2:
XPath语句格式
核心思想:写XPath就是写地址。
获取文本:
//标签1[@属性1="属性值1"]/标签2[@属性2="属性值2"]/..../text()
获取属性值:
//标签1[@属性1="属性值1"]/标签2[@属性2="属性值2"]/..../@属性n
其中,[@属性="属性值"]不是必需的。它的作用是帮助过滤相同的标签。在不需要过滤相同标签的情况下可以省略。
标签1的选取
标签1可以直接从html这个最外层的标签开始,一层一层往下找,这个时候,XPath语句是这样的:
/html/body/div[@class="useful"]/ul/li/text()
当以html开头的时候,它前面是单斜线。这样写虽然也可以达到目的,但是却多此一举。正如在淘宝买东西时,没有人会把收货地址的形式写为“地球,亚洲,中国,北京,海淀区,××路,××号”一样。地址前面的“地球,亚洲,中国”写了虽然也没错,但却没有必要。谁都知道全世界只有一个北京。而北京必定在中国,中国必定在亚洲,亚洲必定在地球上。所以,写收货地址的时候,直接写北京就可以了,前面的“地球,亚洲,中国”可以省略。XPath也是同样的道理。在XPath里面找到一个标志性的“地标”,然后从这个标志性的“地标”开始往下找就可以了。标志性的“地标”前面的标签都可以省略。
哪些属性可以省略
来细看下面这个代码片段:
<div class="useful">
<ul>
<li >我需要的信息1</li>
<li >我需要的信息2</li>
<li >我需要的信息3</li>
</ul>
</div>
- 标签本身就没有属性,则写XPath的时候,其属性可以省略。 标签有属性,但是如果这个标签的所有属性值都相同,则可以省略属性,例如
- ,所有的
- 标签都有一个class属性,值都为info,所以属性可以省略。
XPath的特殊情况
如图3:
XPath常用表达式:
路径定位:按照网页的树枝路径结构
绝对路径:/html/body/div/div/h1
相对路径://div//h1
属性谓语://div[@attributeName=" ]/h1
索引谓语://div/div/ul/li[3]
取文本内容:
取元素文本:/html/body/div/div/h1/text()
取所有子元素文本://div//text()
取属性://div/div/ul/a/@href
模糊查询://div[starts-with(@class,'stu ')] contains,ends-with
XPath的使用步骤:
第一步:导包
from lxml imort etree
pip install lxml
创建etree解析对象
tree=etree.HTML(html_content)
构建xpath表达式
xpath="/html/body/div[2]"
进行xpath解析,返回列表:
tree.xpath("xpath表达式")
步骤1:分析内容的结构
步骤2:构建数据解析对象
步骤3:对数据进行解析
步骤4:获取解析的数据
xpath的进阶用法:
用法 | 举例 |
匹配当前节点下的所有 | .// |
属性定位 | /@属性名 |
将对象还原为字符串 | etree.tostring(element) |
按轴(Axes)匹配)(==XPath表达式) | |
获取当前节点的所有子元素 | //div/ul/child::li==//div/ul/li |
获取当前节点的所有属性 | //div/ul/attribute:: ==(//div/ul/@) |
获取当前节点的父辈元素 | //div/ul/ancestor::div (通过子元素定位其他元素) |
获取当前节点的后代元素 | //div/ul/descendant::a |
常用函数 | |
统计符合要求节点的数量 | count(//div//li) |
根据指定的文本内容选择 | //div//li[contains(text(),“xx”)]/span |
字符串拼接 | concat(//il/text(), //span/text()) |
通过文件创建 XPATH 解析器:
创建 Parse 对象: parser=etree.HTMLParser ( encoding =” gbk " )
创建etree对象:
tree =etree.parse ( " feng.html " , parser= parser )
格式化输出解析对象的文本( HTML 代码) :
print ( et 「 ee . t 。 st 「 ing ( t 「 ee , en 。。 ding 二: g 匕、, · , prett 犯 p 「 int 二丁 rue , met 。。 d 二: html 二) . de 。。 de (二 g 匕、: ) ) 寻 XPath 连缀解析:狂已解析的 xpath 对象. xpath (表达式) . xpat 卜(表达式)网络 We 卜 C 1 .愉 1 el 爬 T 亡亡 h 虫技术 }
格式化输出解析对象的文本( HTML 代码) :
print ( ettree.tostring(tree,encoding="gbk",pretty_print=True,method="html").decode("gbk")
XPath连接解析:
已经解析的xpath对象.xpath(表达式).xpath(表达式)
使用xpath时候的一些坑原因分析:
眼见不一定为实
看到的结果极有可能是浏览器打开网页之后,进行多次加载的结果
目前,爬虫只爬到了第一次下载的结果,没有进行后续的JS,AJAX的数据加载
爬取的内容只是像HTML格式的文本
内容可能格式不正确
不会像浏览器一样进行加载解析
以爬取的文本为依据,进行数据解析
做爬虫如做人一样
留有余地:适当地扩大数据爬取范围,多使用//text()
抓大放小:个别数据爬取不到,就放了吧