设为首页 收藏本站
查看: 1430|回复: 0

[经验分享] lmth1 一个用Python编写的便捷网页信息提取工具

[复制链接]

尚未签到

发表于 2015-4-20 12:33:13 | 显示全部楼层 |阅读模式
lmth1 一个便捷的网页信息提取工具
0, Why lmth1?
  玩Python的人十有八九用过urllib,扒数据的十有八九用过BeautifulSoup。我也不例外,平时抓数据几乎全用BeautifulSoup。
BeautifulSoup的功能挺不错,但就是API挫了点,用起来不顺。相对于中规中矩的API,我更中意jQuery的Fluent API。所以,花了两个晚上,以BeautifulSoup作为基础,搞了两个库lmth和lmth1:lmth提供基本功能,并负责Hpath解析;lmth1提供Fluent API,进行数据抓取。
  lmth1的接口非常简单,它的实现更简单——不超过300行代码。但它的功能很强大,你很快就会看到,lmth1是如何用一行代码实现BeautifulSoup十行代码的功能的,而且,更易读。
1, 简介
  如题。
  使用前请将lmth.py, lmth1.py以及beautifulsoup.py放至Python的环境目录下。
2, Hpath
  Hpath是一种我定义的一种类似于Xpath的HTML路径查询表达式,它的语法非常简单——几个例子就能说明白。如果需要严格的定义,请参考2.2的BNF定义。
2.1 实例阐述
  注意,这里的例子所提到的获取元素,均为在目标节点下所获得的元素。
  采用的实例HTML:
  


1
2
3
4     Untitled Page
5
6
7 Page list
8
9     Google
10     Yahoo
11     Baidu
12     Bing
13
14
15     
16     1
17     2
18     3
19     4
20     5
21     6
22     
23
24
25   
  
2.1.1 基本表达式
  


li          作用:获取所有li元素
结果:


[
     1,
     2,
     3,
     4,
     5,
     6
]  
  


div[id=tbl]  作用:获取所有id属性为tbl的div元素
提示:通过属性过滤来进行更精准的查找
结果:




1
2
3
4
5
6

  
  


div[id=content, class=sites]  作用:获取所有id属性为name且class属性为grey的div元素
提示:你可以同时设定多个属性值,属性对之间用逗号分隔
结果:



Google
Yahoo
Baidu
Bing
  
  


div[@id]      作用:获取所有div元素的id属性值
提示:你需要在需获取的属性值前加一个@符
结果:


[
     'content',
    'tbl'
]  
  


div[id=content]/a[@href]  作用:获取所有id属性为name的元素下面的p元素的href属性值
结果:


[
     'http://www.google.com',
     'http://www.yahoo.com',
     'http://www.baidu.com',
     'http://www.bing.com'
]  
2.1.2 高级表达式
  


a[class={excellent|good}, @class, @href]  作用:获取所有class属性为excellent或good的元素下面的a元素的class属性和href属性
提示:大括号里面的是正则表达式,利用它可以实现或操作
结果:


[
     {
         'href': 'http://www.google.com',
         'class': 'good'
     },
     {
         'href': 'http://www.yahoo.com',
         'class': 'good'
     },
     {
         'href': 'http://www.bing.com',
         'class': 'excellent'
     }
]  
  


div[id={con.+}]/a[class={ass.+}, @class, @#]  作用:获取所有id属性以con做前缀的div元素下面的class属性以ass为前缀的元素的id属性以及内容
提示:@#代表要获取元素的内容(innertext)
结果:


{
     '#': 'Baidu',
     'class': 'asshole'
}  
  



ul/li[class={e.+}, @#]  作用:获取所有id属性以post做前缀的元素下面的以数字为id的p元素下面的a元素的href属性及内容
提示:也可以利用正则表达式进行模糊查询
结果:


[
     '2',
     '4',
     '6'
]  
2.2 Hpath的BNF定义
  没玩过编译的可以忽略这一节。
玩过编译的看了就明白。
  


hpath ::= hpart {"/" hpart}
hpart ::= ele_name [ "[" attrs "]" ]
attrs ::= pred_attrs [ "," get_attrs ]
pred_attrs ::= pred_attr { "," pred_attr }
get_attrs ::= get_attr { "," get_attr }
get_attr ::= "@"string [ "(" attr_alias ")"]
attr_alias ::= string
pred_attr ::= string "=" value
value ::= string | regex_value
regex_value ::= "{" string "}"  
3, 选择元素
  lmth1提供了非常简便的API来进行HTML元素的获取。
为了方便,请输入以下代码:


from lmth1 import Url  这样可以省去lmth1这个看起来有些诡异的前缀 :)
  这里以http://files.iyunv.com/figure9/test.xml这个链接上的文件为例(该文件内容和之前的实例HTML是一样的):
3.1 选择单个元素
  


Url('http://files.iyunv.com/figure9/test.xml').elem('div')  作用:从http://files.iyunv.com/figure9/test.xml链接上获取第一个div元素。
结果:



Google
Yahoo
Baidu
Bing
3.2 选择多个元素
  


Url('http://files.iyunv.com/figure9/test.xml').elems('li')  作用:从http://files.iyunv.com/figure9/test.xml链接上获取所有li元素。
结果:


[
     1,
     2,
     3,
     4,
     5,
     6
]  
3.3 链式选择
  


Url('http://files.iyunv.com/figure9/test.xml').elem('div').elem('a')  作用:从http://files.iyunv.com/figure9/test.xml链接上获取第一个div元素下面的a元素。
提示:这里只是为了演示链式选择,更好的选择是使用Url('http://files.iyunv.com/figure9/test.xml').elem('div/a'),效果等同。
结果:


Google  

  

Url('http://files.iyunv.com/figure9/test.xml').elems('div')[-1].elems('li[class=odd]')[-1]
  作用:从http://files.iyunv.com/figure9/test.xml链接上获取最后一个div元素的最后一个class属性为odd的li元素。
提示:结合elems和Python的列表操作,可以获得强大的表达能力。
结果:

5  
  


Url('http://files.iyunv.com/figure9/test.xml').elems('div')[-1].elems('li')[::2]  作用:从http://files.iyunv.com/figure9/test.xml链接上获取最后一个div元素的序数为奇数的li元素。
提示:Don't forget the slices!
结果:


[
     1,
     3,
     5
]  
4, 获取属性
  有时我们需要对本地的HTML文件进行操作,所以我在lmth引入了Path这个类,用来处理本地的文件。
  请把http://files.iyunv.com/figure9/test.xml的文件保存到本地,这里假定它被保存在d:\test.xml路径。
  同样,为了方便,请输入以下代码:


from lmth1 import Path  这样可以省去lmth1这个看起来有些诡异的前缀 :)
4.1 获取单个属性
  


attr = Path(r'd:\test.xml').attr('li[class=even, @#(content)]')  作用:从d:\test.xml文件获取第一个class属性为even的li元素的内容,然后为这个属性取名为content。
提示:@开头表示要取的属性,()里代表属性的别名,#表示元素的内容,可以直接用名字获取属性值。
结果:


attr =>
{
     content:'2'
}

attr.content =>
'2'  
4.2 获取多个属性
  


attrs = Path(r'd:\test.xml').attrs('a[@href(link), @class(category), @#(title)]')  作用:从d:\test.xml文件获取所有a元素的href(设置别名为link)、class属性(设置别名为category)和内容(设置别名为title)。
提示:@开头表示要取的属性,()里代表属性的别名,#表示元素的内容,可以直接用名字获取属性值。
结果:
  


attrs =>
[
     {
         category:u'good',
         link:u'http://www.google.com',
         title:u'Google'
     },
    {
         category:u'good',
         link:u'http://www.yahoo.com',
         title:u'Yahoo'
     },
    {
         category:u'asshole',
         link:u'http://www.baidu.com',
         title:u'Baidu'
     },
     {
         category:u'excellent',
         link:u'http://www.bing.com',
         title:u'Bing'
     }
]

attrs[-1] =>

{
     category:u'excellent',
     link:u'http://www.bing.com',
     title:u'Bing'
}

print attrs[2].title, 'is an', attrs[2].category =>

Baidu is an asshole  
4.3 链式选择
  可以在elem和elems选择器之后应用attr和attrs选择器。注意,你不能在attr和attrs选择器之后应用其它的选择器。
  


Path(r'd:\test.xml').elems('div[id=content]/a')[::2].attrs('[@href(link), @class(category)]')  作用:从d:\test.xml文件获取id属性为content的div元素下面的所有序数为奇数的a元素的href(别名为link)和class属性(别名为category)。
提示:当Hpath没有元素名,仅由要获取的属性名组成时,其获取的属性为当前元素的属性。
  结果:


[
     {
         category:u'good',
         link:u'http://www.google.com'
     },
     {
         category:u'asshole',
         link:u'http://www.baidu.com'
     }
]  
5, 其它功能
5.1 URL生成
  在日常的html获取中,经常需要生成大量的URL,尽管这样的工作在Python中一两行就可以搞定,但为了避免不必要的重复,我在lmth1中提供了Urls类,并提供了两个基础方法,用来生成Urls对象。
Urls对象保存了若干个Url实例,其中每一个实例都可以直接进行选择操作。
  请先执行下面的代码:   


from lmth1 import Urls  
  通过数字批量生成Url:
  


lmth.Urls.from_indice('http://www.bing.com/page/', 1, 5)  作用:以为前缀,生成后缀从1到5的Url
结果:


http://www.bing.com/1
http://www.bing.com/2
http://www.bing.com/3
http://www.bing.com/4
http://www.bing.com/5  
  


lmth.Urls.from_indice('http://www.bing.com/page/', 1, 4, 3)  作用:以http://www.bing.com/page/作为前缀,生成后缀从1到5的Url,其中默认宽度为3,用0填充
提示:第四个参数用来设置数字宽度,对于一些网站,这是很必要的
结果:


http://www.bing.com/001
http://www.bing.com/002
http://www.bing.com/003
http://www.bing.com/004
http://www.bing.com/005  
  通过后缀批量生成Url:
  


Urls.from_postfixes('http://www.baidu.com/', ['isfool', 'isasshole', 'ismoron'])  作用:以http://www.baidu.com/作为前缀,迭代后面的后缀列表,批量生成Url
结果:
  


http://www.baidu.com/isfool
http://www.baidu.com/isasshole
http://www.baidu.com/ismoron  
5.2 字符编码
  lmth1的默认编码是UTF-8,可以满足绝大多数网站的需求。然而,在读取某些中文的网站时,仍然会出现乱码,因此,lmth1允许手动设置编码。对于一些乱码的中文网站,将编码换为gb18030可以解决问题。
  


Url('http://www.bing.com/', 'gb18030')  作用:生成编码为gb18030的Url对象
提示:默认的编码为UTF-8
  


lmth.Urls.from_indice('http://www.bing.com/page/', 1, 5, code_str='gb18030')  作用:批量生成编码为gb18030的Url对象
提示:默认的编码为UTF-8
6, 参考
  1, BeautifulSoup: http://www.crummy.com/software/BeautifulSoup/
2, Martin Fowler: Domain-Specific Languages.
3, Internal-DSL: http://en.wikipedia.org/wiki/Domain-specific_language
4, Fluent Interface: http://en.wikipedia.org/wiki/Fluent_interface
  
源代码下载:
http://files.iyunv.com/figure9/lmth1withBS.7z

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-58861-1-1.html 上篇帖子: python 列表函数 下篇帖子: python之使用字符串
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表