http://www.cnblogs.com/binye-typing/p/6656595.html

  读者可能会奇怪我标题怎么理成这个鬼样子,主要是单单写 lxml 与 bs4 这两个 py 模块名可能并不能一下引起大众的注意,一般讲到网页解析技术,提到的关键词更多的是 BeautifulSoup 和 xpath ,而它们各自所在的模块(python 中是叫做模块,但其他平台下更多地是称作库),很少被拿到明面上来谈论。下面我将从效率、复杂度等多个角度来对比 xpath 与 beautifulsoup 的区别。

效率

从效率上来讲,xpath 确实比 BeautifulSoup 高效得多,每次分步调试时,soup 对象的生成有很明显的延迟,而 lxml.etree.HTML(html) 方式则在 step over 的一瞬间便构建成功了一个可执行 xpath 操作的对象,速度惊人。原理上来讲,bs4 是用 python 写的,lxml 是 c 语言实现的,而且 BeautifulSoup 是基于 DOM 的,会载入整个文档,解析整个DOM树,因此时间和内存开销都会大很多。而lxml只会进行局部遍历。

使用复杂度

从使用复杂度来讲,beautifulsoup 的 find 方法要比 xpath 简单,后者不仅要求通晓 xpath 语法,而且 xpath 方法的返回对象始终是一个 list,这使得对于页面中一些唯一元素的处理有些尴尬,比如根据 id 获取页面某一标签,下面我用两种方式实现一个获取网页导航栏的方法 (注释部分为 bs4 的实现):
1     def get_nav(self,response):
2         # soup = BeautifulSoup(response.body_as_unicode(), 'lxml')
3         # nav_list = soup.find('ul', id='nav').find_all('li')
4         model = etree.HTML(response.body_as_unicode())
5         nav_list = model.xpath('//ul[@id="nav"]/li')
6         for nav in nav_list[1:]:
7             # href = nav.find('a').get('href')
8             href = nav.xpath('./a/@href')[0]
9 yield Request(href, callback=self.get_url)

 

  可以看到 xpath 除了其特殊的语法看上去有些别扭(跟正则表达式似的)以外,它在代码简洁度上还是可观的,只是所有 xpath 方法的返回结果都是一个 list ,如果匹配目标是单个元素,对于无脑下标取0的操作,强迫症患者可能有些难受。相比之下,BeautifulSoup 这一长串的 find 与 find_all 方法显得有些呆板,如果碰到搜索路线比较曲折的,比如:

# href = article.find('div', class_='txt').find('p', class_='tit blue').find('span').find('em').find('a').get('href')
href = article.xpath('./div[@class="txt"]//p[@class="tit blue"]/span/em/a/@href')[0]

 

  这种情况下,BeautifulSoup 的写法就显得有些让人反胃了,当然一般情况下不会出现这么长的路径定位。

 

功能缺陷总结——BeautifulSoup

   BeautifulSoup 在使用上的一个短板,就是在嵌套列表中去匹配元素的时候会显得很无力,下面是一个例子(具体网页结构可根据 index_page 在浏览器打开进行审查):

 1 class RankSpider(spider):
 2     name = 'PCauto_rank'
 3     index_page = 'http://price.pcauto.com.cn/top/hot/s1-t1.html'
 4     api_url = 'http://price.pcauto.com.cn%s'
 5 
 6     def start_requests(self):
 7         yield Request(self.index_page, callback=self.get_left_nav)
 8 
 9     # 测试 BeautifulSoup 是否能连续使用两个 find_all 方法
10     def get_left_nav(self,response):
11         # model = etree.HTML(response.body_as_unicode())
12         # nav_list = model.xpath('//div[@id="leftNav"]/ul[@class="pb200"]/li//a[@class="dd "]')
13         soup = BeautifulSoup(response.body_as_unicode(), 'lxml')
14         nav_list = soup.find('div', id='leftNav').find('ul', class_='pb200').find_all('li').find_all('a', class_='dd')
15         for sub_nav in nav_list:
16             href = self.api_url % sub_nav.xpath('./@href')[0]
17             yield Request(href, callback=self.get_url)
18 
19     def get_url(self):
20         pass

 

 

   使用注释部分的 xpath 写法没什么问题,可实现准确定位,但用到 BeautifulSoup 去实现相应逻辑的时候,就要连续使用两个 find_all 方法 ,显然这种写法不符合规范,运行的时候会报 AttributeError: 'ResultSet' object has no attribute 'find_all' 错误,这时候我们要实现这种匹配,只能先去遍历各个 li ,然后调 find_all 方法找到 li 下的各个 a 标签,实在繁琐,所以这种场景用 xpath 来解决会省下不少麻烦。

  当然这里我只是单单为了诠释这么个问题才在故意在拿目标 url 时分这么多级的,实际开发中我这里用的是:

1         # nav_list = model.xpath('//div[@id="leftNav"]///a[@class="dd "]')
2         nav_list = soup.find('div', id='leftNav').find_all('a', class_='dd')

 

  但如果说我们的目标不是所有的 li 下面的 a 标签,而是部分 class="*" 的 li 下面的 a 标签,这时候我们就只能选择使用 xpath 来达到目的,当然如果你喜欢写遍历,觉得这样写出来逻辑展示更清晰,那你可以跳过这一节。

 

功能缺陷总结——xpath

xpath 的类选择器在做公共类名选择时有短板,也勉强把它算作功能缺陷吧,比如:     
1  model = etree.HTML(response.body_as_unicode())
2  model.xpath('//div[@class="box box-2 box-4"]')
无法定位 html 中 class 为 box box-2 box-4 mt25 与 box box-2 box-4 mt17 的两个 div,必须分别以: 
model.xpath('//div[@class="box box-2 box-4 mt25"]') 
model.xpath('//div[@class="box box-2 box-4 mt17"]')

 

  来匹配目标,这可能要归结于 xpath 在设计的时候本身就是以类名的完全匹配来确定目标的,哪怕多一个空格:

页面中一个 a 标签是这样写的:  <a href="/top/hot/s1-t1.html" class="dd ">5万以下</a> 用 xpath 去选择,写作:

      model.xpath('//a[@class="dd"]')

死活匹配不到(当时真的是蛮懵逼的),必须要在后面加空格,但在通过 js 控制台 a.dd 这个类选择器又可以定位到目标,而且 BeautifulSoup 调 find_all('a', class_='dd') 也是没有问题的,这种应用场景下的 xpath 就略显死板。

 

文本获取

  xpath 目标结点的 text 属性值对应的只是当前匹配元素下面的文本信息,如要获取该结点下面包括子结点在内的所有文本内容要使用 .xpath('string()') 的方式:

1        model = etree.HTML(response.body_as_unicode())
2         place = model.xpath('//div[@class="guide"]')
3 # nav and aiticle
4         if place:
5             mark = place[0].xpath('./span[@class="mark"]')
6             if mark:
7                 # text = mark[0].text.strip().replace('\n','').replace('\r','')  # false
8                 text = mark[0].xpath('string()')
9                 result['address'] = text

 

 

其他方面比较

从参考资料上来讲,bs4 有详细中/英文版官方帮助文档,lxml 好像 document 相对少。另外 BeautifulSoup 的结点对象在 Debugger 下面对于变量内容的监视更友好,它直接显示匹配的 html 字符串,而 lxml 就是一个类似这种表示的对象: <Element html at 0x####>,不是很友好,但这都不重要,笔者还是更喜欢 xpath 的高效,简洁,一步到位。
  
 

转载于:https://www.cnblogs.com/mapu/p/8337407.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/452908.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/452908.shtml
英文地址,请注明出处:http://en.pswp.cn/news/452908.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java如何去掉html标签_Java后端去掉HTML标签获取纯文本-Fun言

今天又对我的博客首页进行了一次版本的更新&#xff0c;使其自适应屏幕&#xff0c;获得更好的用户体验&#xff0c;然后就出现点小问题&#xff0c;那就是原来的摘要是人为添加的&#xff0c;有长有短&#xff0c;对自适应屏幕有影响&#xff0c;所以我们现在是截取文章的前20…

单/双中括号与测试条件

测试命令 tesst[]内置命令[[]]bash中的关键字 单中括号 格式[#express1#op#express2#] 注意&#xff1a;   其中#代表括号不能省略   不能匹配模式   变量引用应用双引号括起&#xff0c;尤其当变量引用有空格时   与或非形式-a –o -not   常量应用单/双引号括起  …

暗时间--平凡与优秀间的距离

每个人都希望&#xff0c;在他所从事的领域很优秀&#xff0c;那么如何才能优秀呢&#xff1f;有人做过一个研究&#xff0c;说那些优秀的音乐家&#xff0c;在他们成名之前&#xff0c;已经训练过10000小时。有人可能成功得早&#xff0c;如莫扎特16岁&#xff0c;有些可能需要…

IP分组

IP分组就是根据Ip地址来进行分组&#xff0c;目的可以是为了对不同 的地址组分配不同的带宽&#xff08;限速&#xff09;配置地址组时&#xff0c;其输入格式为A.B.C.D-A.B.C.E&#xff0c;例如&#xff1a;192.168.1.1-192.168.1.250

python3基础3--数据类型--数据运算--表达式if -else-while-for

一、python3 数据类型 1.1 数字例如&#xff1a;1,2,3,4等1.2 int&#xff08;整型&#xff09; 在32位机器上&#xff0c;整数的位数为32位&#xff0c;取值范围为-2**31&#xff5e;2**31-1&#xff0c;即-2147483648&#xff5e;2147483647在64位系统上&#xff0c;整数的位…

spark java教程_(Spark)学习进度十四(Spark之Java独立应用编程)

环境如下:(更新了林子雨教程中不可使用的部分) Hadoop 2.6.0以上 java JDK 1.7以上 Spark 3.0.0-preview2 二、java独立应用编程(在下载依赖jar包的过程中如遇到卡顿现象可以Ctrl+C停止下载,然后重新执行本条命令即可继续下载相应的依赖jar包) 1、安装maven ubuntu中没有自带…

nowcoderD Xieldy And His Password

题意:一个01序列,长度1e6,问有多少子串十进制是3的倍数 题解:DP[i][j]代表前i个并且以i为结尾,且十进制%3j的串的个数 #include<bits/stdc.h> #define maxn 1001000 using namespace std; char s[maxn]; long long dp[maxn][3], sum; int main(){while(~scanf("%s&q…

3D电视,你知道多少?

1.3D电视常见知识 系统概述篇 1、 什么是3D电视&#xff1f; 答&#xff1a;3D电视是一种能够模拟实际景物的真实空间关系的新型电视&#xff0c;它利用人眼的视觉特性产生立体感&#xff0c;让观众感受到观看的影像是具有深度特性的三维立体场景&#xff0c;观众对延伸于屏幕…

testng.xml文件配置

TestNG的DTD检查文件&#xff1a;http://testng.org/testng-1.0.dtd.php <?xml version"1.0" encoding"UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"> <!--suite&#xff08;测试套件&#xff09;为根…

什么是分组转发

分组转发(forwarding)是指在互联网络中路由器转发IP分组的物理传输过程与数据报转发机制。根据分组的目的Ip地址与源Ip地址是否属于同一个子网可分为直接转发和间接转发。 是直接转发还是间接转发&#xff0c;路由器需要根据分组的目的IP地址和源IP地址是否属于同一网络判断。目…

java 栈 先进后出_数据结构: 先进后出——堆栈

栈是一种常用的数据结构&#xff0c;在生活中经常遇到这样的例子&#xff0c;如铁路调度站。本节将详细介绍堆栈的实现过程。算法点拨(顺序栈)栈是一种重要的数据结构。从数据结构的角度看&#xff0c;栈也是线性表&#xff0c;其特殊性在于栈的基本操作是线性表操作的子集&…

Spring Boot—07应用application.properties中的配置

方法1Value("${test.msg}") private String msg;方法2Autowired private Environment env; String value env.getProperty("test.msg");方法3RequestMapping(path"/${query.all}.json", methodRequestMethod.GET) ResponseBody public List&…

skip与direct模式区别 ,他们与CBP的关系

1 CBP表示残差的编码状态,CBP一共6bit&#xff0c;低4位表示4个亮度8x8块,第4位表示U,第五位表示V,如果相应的位为"1", 表示此块有残差系数,反之没有残差,此宏块没有被编码.2 direct 是帧间宏块的一种预测模式&#xff0c;而不是宏块类型&#xff0c;而 S…

程序的装入和链接过程

从用户放入源程序进入操作系统到相应的装程序在机器上运行&#xff0c;所经历的主要阶段有编译阶段 链接阶段 装入阶段 和运行阶段

[零基础学JAVA]Java SE应用部分-34.Java常用API类库

本季目标1、StringBuffer类 2、Runtime 类 3、包装类与JDK 1.5的新特性——泛型 4、日期的操作类 5、Math类 6、Random类1、StringBuffer&#xff08;重点&#xff09; String 类的时候说过&#xff1a;String 类的内容一旦声明则不可改变&#xff0c;改变的只是其地址。…

我所理解的机器学习

各位请移步到【http://www.cnblogs.com/cchHers/p/8945908.html】转载于:https://www.cnblogs.com/cchHers/p/8933042.html

protobuf java文档_Java中使用Protobuf

gradle依赖库&#xff1a;implementation com.google.protobuf:protobuf-java:3.4.0implementation com.google.protobuf:protobuf-java-util:3.4.00.编写.proto文件&#xff0c;编译生成对应Java源文件&#xff1a;syntax "proto2";option java_generic_services …

python 数组和列表的区别

Python没有数组&#xff1a; 只有元组(tuple)和列表(list)&#xff1b;元组一旦创建不可改变&#xff0c;例如&#xff1a;aatuple(1,2,3)&#xff1b;元组不能追加(append)元素&#xff0c;弹出(pop)元素等&#xff1b;只能对元组中的元素进行索引aa[0]&#xff0c;不能对其中…

内存空间 逻辑地址空间 相对地址 绝对地址

内存空间&#xff08;物理空间或绝对空间&#xff09;&#xff1a;由一系列存储单元所限定 的地址范围。 逻辑地址空间&#xff08;地址空间&#xff09;&#xff1a;由程序中逻辑地址组成的地址范围。 相对地址&#xff08;逻辑地址&#xff09;&#xff1a;用户程序经编译后…

多租户表设计

2019独角兽企业重金招聘Python工程师标准>>> multi-tenant-databases-in-the-cloudtips-amp-tricks-to-build-multi-tenant-databases-with-sql-databases团队开发框架实战—多租户支持转载于:https://my.oschina.net/yangjiandong/blog/1612626