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 方法显得有些呆板,如果碰到搜索路线比较曲折的,比如:

  1. # href = article.find('div', class_='txt').find('p', class_='tit blue').find('span').find('em').find('a').get('href')
  2. 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,必须分别以: 
  1. model.xpath('//div[@class="box box-2 box-4 mt25"]')
  2. 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 的高效,简洁,一步到位。
  
 

关于爬虫中常见的两个网页解析工具的分析 —— lxml / xpath 与 bs4 / BeautifulSoup的更多相关文章

  1. 【XPath Helper:chrome爬虫网页解析工具 Chrome插件】XPath Helper:chrome爬虫网页解析工具 Chrome插件下载_教程_安装 - 开发者插件 - Chrome插件网

    [XPath Helper:chrome爬虫网页解析工具 Chrome插件]XPath Helper:chrome爬虫网页解析工具 Chrome插件下载_教程_安装 - 开发者插件 - Chrome插 ...

  2. java基础71 XML解析中的【DOM和SAX解析工具】相关知识点(网页知识)

    本文知识点(目录):本文下面的“实例及附录”全是DOM解析的相关内容 1.xml解析的含义    2.XML的解析方式    3.xml的解析工具    4.XML的解析原理    5.实例    6 ...

  3. css布局 - 工作中常见的两栏布局案例及分析

    突然想到要整理这么一篇平时工作中相当常见但是我们又很忽视的布局的多种处理方法.临时就在我经常浏览的网站上抓的相对应的截图.(以后看到其他类型的我再补充) 既然截了图,咱们就直接看人家使用的布局方式,毕 ...

  4. MathType中常见的两种符号的运用

    想要让公式编辑得快速又高效,MathType数学公式编辑器这个神助攻是少不了的.MathType是一款专用的数学公式编辑器,用它来编辑公式非常方便实用,并且排版也非常简单.下面介绍两种常见符号的应用. ...

  5. C++中常见的两种二义性问题及其解决方式

    --------------------------------一.“倒三角”二义性问题------------------------------- 问题描述:卤煮之所以称之为“倒三角问题”,是因为 ...

  6. python接口测试中常见的两种接口依赖处理方式

    一.请求体的字段依赖 这种情况多数是在当前测试的接口,它的前置接口的请求体中的字段要拿来在当前的接口请求体中继续使用,比如修改用户信息的接口,该接口会使用到用户名的字段,该字段是由创建用户时的请求体中 ...

  7. 留学Essay写作中常见的两类要求词盘点

    写essay的时候,我们会常常因为各式各样的要求词而头疼:discuss,describing,evaluate,explain,等等,他们之间有何区别?如果你在思考这个问题,那么这篇文章就是为你写的 ...

  8. 简析--Java中常见的一些关键字的解析

    在Java开发中我们经常会用到一些关键字,关键字的定义很有意思"Java事先定义好的,具有特殊含义的单词",那么我们怎么来用好关键字呢?下面我们对一些常见的关键字进行分析和比较; ...

  9. Python网络爬虫笔记(一):网页抓取方式和LXML示例

    (一)   三种网页抓取方法 1.    正则表达式: 模块使用C语言编写,速度快,但是很脆弱,可能网页更新后就不能用了. 2.    Beautiful Soup 模块使用Python编写,速度慢. ...

随机推荐

  1. linux服务器系统负载监控-shell脚本

    一.监控服务器系统负载情况: 1.用uptime命令查看当前负载情况(1分钟,5分钟,15分钟平均负载情况) # uptime   15:43:59 up 186 days, 20:04,  1 us ...

  2. [leetcode]43. Multiply Strings高精度乘法

    Given two non-negative integers num1 and num2 represented as strings, return the product of num1 and ...

  3. input type="file"多图片上传 原生html传递的数组集合

    单个的input type="file"表单也是可以实现多图片上传的 代码如下: <form action="manypic.php" method=&q ...

  4. dos批处理(bat)运行exe

    @echo off SETLOCAL ENABLEDELAYEDEXPANSIONREM 延迟环境变量扩展 color E echo operate:1.start启动 2.stop停止 3.exit ...

  5. window 安装mysql

    常见错误:ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES) 密码输入错误:无法远程 ...

  6. ActiveMQ_1学习

    学习资源 官方文档 http://activemq.apache.org/features.html 下载ActiveMQ选择版本 http://activemq.apache.org/overvie ...

  7. 利用ONENET平台透传电脑截图

    这个仅供技术学习了 可以用在远程监控等行业,不用传统工具用的公网ip等比较坑爹的东西 还是比较方便的 需要的话请联系微信nbdx123

  8. 学习在dos下使用gcc来编译

    这两年里,断断续续的学习和使用c,平时都是在CodeBlocks里写代码,编译程序,点一下按钮就行了.对整个编译过程是一点儿都不了解.相比当年学习java,真的是选择了两个不同的路,当年学习java的 ...

  9. HTTPS抓包之Charles

    这里对HTTP请求的抓包操作不做讲解了,只讲解HTTPS的抓包要进行的操作. [说明]:下面以MAC电脑示例,Windows版本可参考:http://weibo.com/ttarticle/p/sho ...

  10. Android开发 - 掌握ConstraintLayout(二)介绍

    介绍 发布时间 ConstraintLayout是在2016的Google I/O大会上发布的,经过这么长时间的更新,现在已经非常稳定. 支持Android 2.3(API 9)+ 目前的Androi ...