网络爬虫笔记—解析库Xpath

网络爬虫笔记—解析库Xpath

Xpath可以用来定位网页源码的节点,并可以返回节点的内容。

1、Xpath的常用规则

表达式描述
nodename选取此节点的所有子节点
/从当前节点选取直接子节点
//从当前节点选取子孙节点,既选取子节点,又选取子节点的子节点(孙节点)
.选取当前节点
选取当前节点的父节点
@选取属性

2、实例引入

为了方便大家理解Xpath内容,原书作者编写了一个简单的网页源码,内容如下:

<div>
    <ul>
         <li class="item-0"><a href="link1.html">first item</a></li>
         <li class="item-1"><a href="link2.html">second item</a></li>
         <li class="item-inactive"><a href="link3.html">third item</a></li>
         <li class="item-1"><a href="link4.html">fourth item</a></li>
         <li class="item-0"><a href="link5.html">fifth item</a>
     </ul>
 </div>

我们可以将上面的网页源码,粘贴到文本文档中,然后将文本文档的格式修改为html,再用浏览器打开该文件,便会显示成下面的样式:

这便是一个简单的网页。然后单击右键选择检查(或点击F12)便会打开开发者选项,选择元素(element),便会出现我们上面编写的网页源码,关于网页源码的各个节点可参考下图。

from lxml import etree
text = """
<div>
    <ul>
         <li class="item-0"><a href="link1.html">first item</a></li>
         <li class="item-1"><a href="link2.html">second item</a></li>
         <li class="item-inactive"><a href="link3.html">third item</a></li>
         <li class="item-1"><a href="link4.html">fourth item</a></li>
         <li class="item-0"><a href="link5.html">fifth item</a>
     </ul>
 </div>
"""
html = etree.HTML(text)#解析文本,构建一个Xpath解析对象
result = etree.tostring(html)#修正网页文本
print(result.decode("utf-8"))#tostring转化的格式为bytes类型,这里将其转化为字符串类型

输出结果:

<html><body><div>
    <ul>
         <li class="item-0"><a href="link1.html">first item</a></li>
         <li class="item-1"><a href="link2.html">second item</a></li>
         <li class="item-inactive"><a href="link3.html">third item</a></li>
         <li class="item-1"><a href="link4.html">fourth item</a></li>
         <li class="item-0"><a href="link5.html">fifth item</a>
     </li></ul>
 </div>
</body></html>

从输出结果可以看出,网页源码多了<html><body>等字符,这是因为使用etree系统会自动补全网页所需元素。

  • 读取本地网页源码
from lxml import etree
html = etree.parse("实际案例.html",etree.HTMLParser())#实际案例.html为本地网页文件,其内容就是上面代码的text文本中的内容
result = etree.tostring(html)#修正网页文本
print(result.decode("utf-8"))#tostring转化的格式为bytes类型,这里将其转化为字符串类型

输出结果:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><div>&#13;
    <ul>&#13;
         <li class="item-0"><a href="link1.html">first item</a></li>&#13;
         <li class="item-1"><a href="link2.html">second item</a></li>&#13;
         <li class="item-inactive"><a href="link3.html">third item</a></li>&#13;
         <li class="item-1"><a href="link4.html">fourth item</a></li>&#13;
         <li class="item-0"><a href="link5.html">fifth item</a>&#13;
     </li></ul>&#13;
 </div></body></html>

从本地读取和直接写文本对比来看,从本地读取会多一些元素,但其不影响后续对文本的分析。

3、获取网页的所有节点

//代表获取子节点或孙节点,所以一般用//开头的Xpath规则来获取所有符合要求的节点。如果用/的话,则只能获取直接子节点,而不能获取孙节点,会有遗漏的情况。

from lxml import etree
html = etree.parse("实际案例.html",etree.HTMLParser())#实际案例.html为本地网页文件,其内容就是上面代码的text文本中的内容
result = html.xpath("//*")#表示获取所有的节点
result_li = html.xpath("//li")#表示获取所有的li节点
print("获取的所有节点:",result)
print("获取的所有的li节点:",result_li)

输出结果:

获取的所有节点: [<Element html at 0xb093594940>, <Element body at 0xb0946cc3c0>, <Element div at 0xb0935a20c0>, <Element ul at 0xb0935a2080>, <Element li at 0xb0935a2040>, <Element a at 0xb0935a2140>, <Element li at 0xb0935a2180>, <Element a at 0xb0935a21c0>, <Element li at 0xb0935a2200>, <Element a at 0xb0935a2100>, <Element li at 0xb0935a2240>, <Element a at 0xb0935a2280>, <Element li at 0xb0935a22c0>, <Element a at 0xb0935a2300>]
获取的所有的li节点: [<Element li at 0xb0935a2040>, <Element li at 0xb0935a2180>, <Element li at 0xb0935a2200>, <Element li at 0xb0935a2240>, <Element li at 0xb0935a22c0>]

4、获取网页的子节点

from lxml import etree
html = etree.parse("实际案例.html",etree.HTMLParser())#实际案例.html为本地网页文件,其内容就是上面代码的text文本中的内容
result = html.xpath("//li/a")#表示获取所有的li节点下的直接a节点,如果要想获取li节点下的所有a节点可这样写//li//a
#因为//代表获取子孙节点,而/获取直接子节点
print(result)

输出结果:

[<Element a at 0xf08310cac0>, <Element a at 0xf08310c100>, <Element a at 0xf08310c200>, <Element a at 0xf08310c3c0>, <Element a at 0xf08310c400>]

5、获取网页的父节点

from lxml import etree
html = etree.parse("实际案例.html",etree.HTMLParser())#实际案例.html为本地网页文件,其内容就是上面代码的text文本中的内容
result = html.xpath('//a[@href="link4.html"]/../@class')
print(result)
#输出结果为:['item-1']

'//a[@href=“link4.html”]/…/@class’表示的是:先获取href属性等于“link4.html”的a节点,再获取其父节点的class属性的值。

6、属性匹配

在选择网页节点时,根据某个属性值来筛选具有该属性的节点。

from lxml import etree
html = etree.parse("实际案例.html",etree.HTMLParser())
result = html.xpath('//li[@class="item-0"]')#获取class="item-0"的li节点
print(result)

输出结果:

[<Element li at 0xb0935a2400>, <Element li at 0xb0935a2580>]

7、文本获取

获取网页节点中的文本。

from lxml import etree
html = etree.parse("实际案例.html",etree.HTMLParser())
result_1 = html.xpath('//li[@class="item-0"]/text()')#获取class="item-0"的li节点的直接文本
result_2 = html.xpath('//li[@class="item-0"]//text()')#获取class="item-0"的li节点的其本身节点和子孙节点的文本
print("单斜杠的输出结果:",result_1)
print("双斜杠的输出结果:",result_2)

输出结果:

单斜杠的输出结果: ['\r\n     ']
双斜杠的输出结果: ['first item', 'fifth item', '\r\n     ']

如果想获取网页节点内的文本,可以在节点后面使用text()来获取,当text()前面是单斜杠时,表示获取该节点的文本;当text()前面是双斜杠时,表示获取该节点和其子孙节点的文本。

8、属性获取

网页节点除了有文本外,还会有许多属性,例如class属性、href属性等,这些属性的值同样也可以获取。

from lxml import etree
html = etree.parse("实际案例.html",etree.HTMLParser())
result = html.xpath('//li/a/@href')#li节点的直接子节点a的href属性值
print("li节点的直接子节点a的href属性值为:",result)

输出结果:

li节点的直接子节点a的href属性值为: ['link1.html', 'link2.html', 'link3.html', 'link4.html', 'link5.html']

一般节点的属性前面会加@符号

9、属性多值匹配

有时候,某些节点的属性值会是两个字符,这时候就需要使用contains来选取属性值。

from lxml import etree
text = '''
<li class="li li-first"><a href="link.html">first item</a></li> 
<li class="li li-secend"><a href="link.html">secend item</a></li> 
'''
html = etree.HTML(text)
result_1 = html.xpath('//li[@class="li li-first"]/a/text()')#根据属性值直接取值
result_2 = html.xpath('//li[contains(@class, "li")]/a/text()')#使用contains获取结果
print("按照属性直接取值的结果:",result_1)
print("使用contains取值的结果:",result_2)

输出结果:

按照属性直接取值的结果: ['first item']
使用contains取值的结果: ['first item', 'secend item']

当属性具有多个值得时候,也可以直接根据属性值获取文本。从上面的例子可以看出,使用contains获取了两个字,这是因为只要class属性值中含有li都会被提取出来,适合多值的提取。

10、多属性匹配

有时根据单个属性值无法锁定某个节点,这是就需要根据多个属性来锁定这一个节点。这时可以使用and函数来链接多个属性。如果是或的关系的话可以用or。

from lxml import etree
text = '''
<li class="li li-first" name="item"><a href="link.html">first item</a></li>
'''
html = etree.HTML(text)
result = html.xpath('//li[@class="li li-first" and @name="item"]/a/text()')#使用and函数来链接两个属性
print(result)
#输出结果:['first item']

11、按序选择

有时根据节点值会获取多个节点,而我们只需要第一个或第二个节点,这时候就需要按照序列选择节点。

from lxml import etree
text = '''
<div>
    <ul>
         <li class="item-0"><a href="link1.html">first item</a></li>
         <li class="item-1"><a href="link2.html">second item</a></li>
         <li class="item-inactive"><a href="link3.html">third item</a></li>
         <li class="item-1"><a href="link4.html">fourth item</a></li>
         <li class="item-0"><a href="link5.html">fifth item</a>
     </ul>
 </div>
'''
html = etree.HTML(text)
result = html.xpath('//li[1]/a/text()')
print("第一个li节点的直接子节点a的文本值:",result)
result = html.xpath('//li[last()]/a/text()')
print("最后一个li节点的直接子节点a的文本值:",result)
result = html.xpath('//li[position()<3]/a/text()')
print("位置序号小于3的li节点的直接子节点a的文本值:",result)
result = html.xpath('//li[last()-2]/a/text()')
print("倒数第三个的li节点的直接子节点a的文本值:",result)

输出结果:

第一个li节点的直接子节点a的文本值: ['first item']
最后一个li节点的直接子节点a的文本值: ['fifth item']
位置序号小于3的li节点的直接子节点a的文本值: ['first item', 'second item']
倒数第三个的li节点的直接子节点a的文本值: ['third item']

—End—

转载内容:

《Python3网络爬虫开发实战》_崔庆才

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
爬虫(Web Crawler)是一种自动化程序,用于从互联网上收集信息。其主要功能是访问网页、提取数据并存储,以便后续分析或展示。爬虫通常由搜索引擎、数据挖掘工具、监测系统等应用于网络数据抓取的场景。 爬虫的工作流程包括以下几个关键步骤: URL收集: 爬虫从一个或多个初始URL开始,递归或迭代地发现新的URL,构建一个URL队列。这些URL可以通过链接分析、站点地图、搜索引擎等方式获取。 请求网页: 爬虫使用HTTP或其他协议向目标URL发起请求,获取网页的HTML内容。这通常通过HTTP请求实现,如Python中的Requests解析内容: 爬虫对获取的HTML进行解析,提取有用的信息。常用的解析工具有正则表达式、XPath、Beautiful Soup等。这些工具帮助爬虫定位和提取目标数据,如文本、图片、链接等。 数据存储: 爬虫将提取的数据存储到数据、文件或其他存储介质中,以备后续分析或展示。常用的存储形式包括关系型数据、NoSQL数据、JSON文件等。 遵守规则: 为避免对网站造成过大负担或触发反爬虫机制,爬虫需要遵守网站的robots.txt协议,限制访问频率和深度,并模拟人类访问行为,如设置User-Agent。 反爬虫应对: 由于爬虫的存在,一些网站采取了反爬虫措施,如验证码、IP封锁等。爬虫工程师需要设计相应的策略来应对这些挑战。 爬虫在各个领域都有广泛的应用,包括搜索引擎索引、数据挖掘、价格监测、新闻聚合等。然而,使用爬虫需要遵守法律和伦理规范,尊重网站的使用政策,并确保对被访问网站的服务器负责。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值