這是從用Python開發開始到現在第二次使用HTMLParser模塊進行html解析了,第一次用的時候,由於是剛剛接觸Python,對其中的一些用法不是很理解,因爲趕進度,雖然照着參考資料也寫出來了,但是其中的原理還是不怎麼了解。第二次用的時候,有一定的經驗了,對Python的理解也更加深刻了,所以第二次用的時候,對HTMLParser模塊的一些用法不像第一次用時那麼茫然。結合鄙人第二次用該模塊的經驗,來講講HTMLParser模塊的基本使用方法,希望對你有幫助。
HTMLParser是python用來解析html的模塊。它可以分析出html裏面的標籤、數據等等,是一種處理html的簡便途徑。HTMLParser採用的是一種事件驅動的模式,當HTMLParser找到一個特定的標記時,它會去調用一個用戶定義的函數,以此來通知程序處理。它主要的用戶回調函數的命名都是以handler_開頭的,都是HTMLParser的成員函數。當我們使用時,就從HTMLParser派生出新的類,然後重新定義這幾個以handler_開頭的函數即可。這幾個函數包括:
handle_startendtag(tag, attrs) 處理開始標籤和結束標籤
handle_starttag(tag, attrs) 處理開始標籤,比如<xx>
handle_endtag(tag) 處理結束標籤,比如</xx>
handle_charref(name) 處理特殊字符串,就是以&#開頭的,一般是內碼錶示的字符
handle_entityref(name) 處理一些特殊字符,以&開頭的,比如
handle_data(data) 處理數據,就是<xx>data</xx>中間的那些數據
handle_comment(data) 處理註釋
handle_decl(decl) 處理<!開頭的,比如<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
handle_pi(data) 處理形如<?instruction>的東西
tag是的html標籤,attrs是 (屬性,值)元組(tuple)的列表(list).
HTMLParser自動將tag和attrs都轉爲小寫。
下面以從中國銀行官網上解析html文件以獲取實時匯率(這就是我第二此用該模塊時的解析任務)爲例,來定義一個HTMLParser派生類。
在構造這個派生類之前,肯定要分析網頁的源碼的,找出你要獲取的那一個值,在此不做介紹。直接上代碼了,要仔細理解
#coding=utf-8 import HTMLParser import requests class MyHTMLParser(HTMLParser.HTMLParser): """ """ def __init__(self): HTMLParser.HTMLParser.__init__(self) self.result = []#記錄結果 self.flag = False#標記 def handle_starttag(self,tag,attrs): if tag == "td" and self.flag == False and len(attrs) == 0: #attrs(屬性,值)元組(tuple)的列表(list),由於我要獲取的值的html標籤的沒有屬性 #所以要求它長度0以排除其它有屬性的標籤,縮小獲取結果的範圍。 #如果有屬性的話,attrs不會爲空,它是元組列表可以用條件語句來判斷 self.flag = True def handle_end(self,tag): if tag == "td" and self.flag == True: self.flag = False def handle_data(self, data): if self.flag == True: self.result.append(data)
上面代碼不難理解,都是重載HTMLParser類裏的方法。
再接着就是爬取網頁了,獲取數據,代碼如下:
def _filter_string(string): """ 除去字符串中的空格,換行等等 由於網頁源碼中空格、換行符等會使返回的結果中帶有換行符(\t)等,所以在此除去, 其實也可以得到結果(返回的結果是個列表)後用列表的內建方法除去這些帶有換行符的元素, 其實這樣用‘+’拼接字符串的效率很低,這個問題我也是剛剛發現,過段時間在研究一下, 找到了好的方法,再跟大家分享下。 """ s = '' for one in string: if one != '\n' and one != '\r' and one != '\t' and one != ' ': s = s + one return s def get_rate(): """ 獲取匯率 Returns: """ url = "http://srh.bankofchina.com/search/whpj/search.jsp" headers = {'user-agent':'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:46.0) Gecko/20100101 Firefox/46.0'} usd_data = { 'pjname':1316,#美元在中國銀行官網的代碼 } hkd_data = { 'pjname':1315,#港幣在中國銀行官網的代碼 } usd_result = requests.post(url, data=usd_data, headers=headers) hkd_result = requests.post(url, data=hkd_data, headers=headers) usd_parser = MyHTMLParser() hkd_parser = MyHTMLParser() usd_text = usd_result.text hkd_text = hkd_result.text # text = _filter_string(text) usd_text = _filter_string(usd_text) hkd_text = _filter_string(hkd_text) usd_parser.feed(usd_text) usd_parser.close() # print usd_result.text hkd_parser.feed(hkd_text) hkd_parser.close() usd_start = usd_parser.result.index(u"美元") # 美元第一個出現的位置是最新值 usd_rate = usd_parser.result[usd_start + 3] usd_rate = float(usd_rate) / 100 # 最新美元匯率 usd_uptime = usd_parser.result[usd_start + 7] # 發佈時間 hkd_start = hkd_parser.result.index(u"港幣") hkd_rate = hkd_parser.result[hkd_start + 3] hkd_rate = float(hkd_rate) / 100 hkd_uptime = hkd_parser.result[hkd_start + 7] # 發佈時間 return usd_rate, usd_uptime, hkd_rate, hkd_uptime
HTMLParser類的其它一些方法:
HTMLParser.reset()
重置該實例。失去所有未處理的數據。這個在實例化對象時被隱含地調用。
HTMLParser.feed(data)
提供一些文本給解析器。在由完整元素組成的限度內進行處理,不完整的數據被緩衝直到更多的數據提供或者close()被調用。
HTMLParser.close()
強制將所有的緩衝數據按跟在結束標記的數據一樣進行處理。該方法可以通過派生類定義對輸入結尾的額外處理來進行重定義,但是重定義的版本應該總是調用HTMLParser基類方法close()
HTMLParser.getpos()
返回當前行數和位移值。
HTMLParser.get_starttag_text()
返回當前位置最近的開始標籤的內容
這是給公司內部財務人員記賬用的,所以做成了API供調用。隱約感覺上面代碼最後幾行獲取結果的方法不是最佳方法,如果你有更好的方法,歡迎您與我分享,如果我找到了更好的我會及時分享給大家。
當然,如果是個簡單的任務,用HTMLParser模塊抓取一些想要的數據還是綽綽有餘,如果是複雜的任務,有一定要求(比如性能要求)的任務,那就要用第三方庫,比如Beautiful Soup,不過這個庫我沒用過,有時間研究一下,然後再和大家分享一下我的學習習得。