解析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('&gt;&#62;&#x3E;')
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 與&quot; HTML 4.01 &quot;標准沒有太多的不同</title> 
62 </head> 
63 <body> 
64 <p>i love&#247; you&times;</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的天下呢?)唉。。。。。。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM