有時候你要把抓回來的數據進行提取,過大篇幅的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的天下呢?)唉。。。。。。
