解析html与xhtml的神器——HTMLParser与SGMLParser
有时候你要把抓回来的数据进行提取,过大篇幅的html标签,你若使用正则表达式进行匹配的话,显然是低效的,这时使用python的HTMLParser模块会显得非常方便。据说还有个比较好用的解析器叫:Beautiful Soup,这个以后有机会再说吧,现在本渣连实习都找不到,再搞这个东西估计没法生活了。。。。。。
事先说明:我们要解析的html和xhtml的语法是规范的那一种,如果遇到不规范的就gg了,得自己手写正则提取。还有,对于那些转义字符没转义就先不考虑了。。。。。。。
关于HTMLParser与SGMLParser:
网上看很多大牛说HTMLParser对中文字符的提取很不好,推荐使用SGMLParser,但是python的官方文档的Demo是用HTMLParser写的。那就学HTMLParser,反正据说两者是继承关系。
先上文档的Demo:
1 #!/usr/bin/env python 2 #-*-coding:utf-8-*- 3 4 from HTMLParser import HTMLParser 5 from htmlentitydefs import name2codepoint 6 7 class MyHTMLParser(HTMLParser): 8 #检索开头标签 9 def handle_starttag(self,tag,attrs): 10 print "Start tag:",tag 11 #匹配里面的项 12 for attr in attrs: 13 print " attr:",attr 14 #匹配结束标签 15 def handle_endtag(self,tag): 16 print "End tag :",tag 17 #处理数据 18 def handle_data(self,data): 19 print "Data :",data 20 #检索注释内容 21 def handle_comment(self,data): 22 print "Comment :",data 23 #处理转义字符 24 def handle_entityref(self,name): 25 c = unichr(name2codepoint[name]) 26 print "Named ent:",c 27 #处理转义的数字字符(ACSII) 28 def handle_charref(self,name): 29 if name.startswith('x'): 30 c = unichr(int(name[1:],16)) #十六进制 31 else: 32 c = unichr(int(name)) 33 print "Num ent :",c 34 #匹配HTML头 35 def handle_decl(self,data): 36 print "Decl :",data 37 38 parser = MyHTMLParser() 39 40 parser.feed('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML4.01//EN"''"http://www.w3.org/TR/html4/strict.dtd">') 41 #Decl : DOCTYPE HTML PUBLIC "-//W3C//DTD HTML4.01//EN""http://www.w3.org/TR/html4/strict.dtd" 42 43 parser.feed('<img src="python-logo.png" alt="The Python logo">') 44 #Start tag: img 45 # attr: ('src', 'python-logo.png') 46 # attr: ('alt', 'The Python logo') 47 48 parser.feed('<style type="text/css">#python { color: green }</style>') 49 #Start tag: style 50 # attr: ('type', 'text/css') 51 #Data : #python { color: green } 52 #End tag : style 53 54 parser.feed('<script type="text/javascript"> alert("<strong>hello!</strong>");</script>') 55 #Start tag: script 56 # attr: ('type', 'text/javascript') 57 #Data : alert("<strong>hello!</strong>"); 58 #End tag : script 59 60 parser.feed('<!-- a comment --><!--[if IE 9]>IE-specific content<![endif]-->') 61 #Comment : a comment 62 #Comment : [if IE 9]>IE-specific content<![endif] 63 64 parser.feed('>>>') 65 #Named ent: > 66 #Num ent : > 67 #Num ent : > 68 69 parser.feed('<p><a class=link href=#main>tag soup</p ></a>') 70 #Start tag: p 71 #Start tag: a 72 # attr: ('class', 'link') 73 # attr: ('href', '#main') 74 #Data : tag soup 75 #End tag : p 76 #End tag : a
一般来说要提取HTML文件中的信息只有3个主要的用途:
- 提取标题和段落;
- 提取img的图片,另存为文件;
- 提取href中的链接
1.提取标题和段落:
1 #!/usr/bin/env python 2 #-*-coding:utf-8-*- 3 4 from htmlentitydefs import entitydefs 5 from HTMLParser import HTMLParser 6 7 class TitleParser(HTMLParser): 8 9 def __init__(self): 10 #定义要搜寻的标签 11 self.handledtags = ['title','p'] #提出标签,理论上可以提取所有标签的内容 12 self.processing = None 13 HTMLParser.__init__(self) #继承父类的构造函数 14 15 def handle_starttag(self,tag,attrs): 16 #判断是否在要搜寻的标签内 17 if tag in self.handledtags: 18 self.data = '' 19 self.processing = tag 20 21 def handle_data(self,data): 22 if self.processing: 23 self.data += data 24 25 def handle_endtag(self,tag): 26 if tag == self.processing: 27 print str(tag)+' : '+str(self.data) 28 self.processing = None 29 30 #下面两个函数都是对html实体做的转码,没有深究 31 def handle_entityref(self,name): 32 if entitydefs.has_key(name): 33 self.handle_data(entitydefs[name]) 34 else: 35 self.handle_data('&'+name+';') 36 37 def handle_charref(self,name): 38 try: 39 charnum=int(name) 40 except ValueError: 41 return 42 if charnum<1 or charnum>255: 43 return 44 self.handle_data(chr(charnum)) 45 46 parser = TitleParser() 47 html1 = """ 48 <html> 49 <head> 50 <title> XHTML 与 HTML 4.01 标准没有太多的不同</title> 51 </head> 52 <body> 53 <p>i love you</p> 54 </body> 55 </html> 56 """ 57 58 html2 = """ 59 <html> 60 <head> 61 <title> XHTML 与" HTML 4.01 "标准没有太多的不同</title> 62 </head> 63 <body> 64 <p>i love÷ you×</p> 65 </body> 66 </html> 67 """ 68 parser.feed(html2)
2.提取img的图片,另存为文件
1 #!/usr/bin/env python 2 #-*-coding:utf-8-*- 3 4 from htmlentitydefs import entitydefs 5 from HTMLParser import HTMLParser 6 import urllib 7 import time 8 9 class ImgParser(HTMLParser): 10 11 num = 1 12 13 def __init__(self): 14 #定义要搜寻的标签 15 self.processing = None 16 HTMLParser.__init__(self) #继承父类的构造函数 17 self.addr='' 18 19 def handle_starttag(self,tag,attrs): 20 #判断是否在要搜寻的标签内 21 if tag == 'img': 22 print 'pic'+str(self.num) + " : " + tag 23 self.num += 1 24 for key,value in attrs: 25 if key == 'src': 26 self.addr = value 27 #在类的成员函数中,使用类中的另一个成员函数,前面必须要指定类名 28 ImgParser.getImage(self) #合法 29 print key + " : " + value 30 if key == 'alt': 31 print key + " : " + value 32 33 def getImage(self): 34 u = urllib.urlopen(self.addr) 35 data = u.read() 36 filename = self.addr.split('/')[-1] 37 timestr = time.strftime('%Y%m%d%S',time.localtime(time.time())) 38 f = open('/home/dzhwen/python文件/Homework/urllib/pic/'+timestr+filename,'wb') 39 f.write(data) 40 f.close() 41 42 parser = ImgParser() 43 f = urllib.urlopen('http://www.sina.com.cn/') #抓取新浪网上以img标签开头的图片 44 parser.feed(f.read())
3.提取href中的链接
1 #!/usr/bin/env python 2 #-*-coding:utf-8-*- 3 4 from htmlentitydefs import entitydefs 5 from HTMLParser import HTMLParser 6 import urllib 7 8 class LinkParser(HTMLParser): 9 10 def __init__(self): 11 #定义要搜寻的标签 12 self.handledtags = ['a'] #提出标签,理论上可以提取所有标签的内容 13 self.processing = None 14 self.linkstr = '' #定义链接的标题 15 self.linkaddr = '' #定义链接的地址 16 HTMLParser.__init__(self) #继承父类的构造函数 17 18 def handle_starttag(self,tag,attrs): 19 #判断是否在要搜寻的标签内 20 if tag in self.handledtags: 21 for name,value in attrs: 22 if name == 'href': 23 self.linkaddr = value 24 self.processing = tag 25 self.linkstr = '' 26 27 def handle_data(self,data): 28 if self.processing: 29 self.linkstr += data 30 31 def handle_endtag(self,tag): 32 if tag == self.processing: 33 print self.linkstr.decode('utf-8') + ' : ' + self.linkaddr.decode('utf-8') 34 self.processing = None 35 36 #下面两个函数都是对html实体做的转码,没有深究 37 def handle_entityref(self,name): 38 if entitydefs.has_key(name): 39 self.handle_data(entitydefs[name]) 40 else: 41 self.handle_data('&'+name+';') 42 43 def handle_charref(self,name): 44 try: 45 charnum=int(name) 46 except ValueError: 47 return 48 if charnum<1 or charnum>255: 49 return 50 self.handle_data(chr(charnum)) 51 52 parser = LinkParser() 53 f = urllib.urlopen('http://www.csdn.net/') #解析csdn主页的链接 54 parser.feed(f.read())
不过很多网站的数据都用js来包装,这样就不能单纯用HTMLParser来解析了,需要用到别的工具,还是回去好好练级吧。。。。
搞到这里,基本的爬虫能掌握思路了,解析来就剩下Beautiful Soup ,Scrapy 和 异步编程还没学(用来进行大幅度的下载和抓取)。。。。。。迫于生活压力,估计没那么快更了,被迫进军java后台了(谁叫外面的世界都是java的天下呢?)唉。。。。。。