1. 程式人生 > >python模塊之HTMLParser簡介

python模塊之HTMLParser簡介

end org str feed 放置 處理方式 Edito clas htm

html.parser是一個非常簡單和實用的庫,它的核心是HTMLParser類。

工作的流程是:當你feed給它一個類似HTML格式的字符串時,它會調用goahead方法向前叠代各個標簽,並調用對應的parse_xxxx方法提取start_tag, tag, attrs data comment和end_tag等等標簽信息和數據,然後調用對應的方法對這些抽取出來的內容進行處理。整個HTMLParser的大致結構如下圖所示:

技術分享圖片

可以發現,處理開始標簽(handle_starttag)、結束標簽(handle_endtag)和處理數據(handle_data)等處理函數在HTMLParser裏是沒有實現的(pass),這需要我們繼承HTMLParser這個類的並覆蓋這些方法。詳細可以參閱python文檔,https://docs.python.org/3/library/html.parser.html?highlight=htmlparser

一、常用方法介紹

l feed(data):主要用於接受帶html標簽的str,當調用這個方法時並提供相應的data時,整個實例(instance)開始執行,結束執行close()。

l handle_starttag(tag, attrs): 這個方法接收Parse_starttag返回的tag和attrs,並進行處理,處理方式通常由使用者進行覆蓋,本身為空。

例如,連接的start tag是<a>,那麽對應的參數tag=’a’(小寫)。attrs是start tag <>中的屬性,以元組形式(name, value)返回(所有這些內容都是小寫)。

例如,對於<A HREF="http://www.baidu.com“>,那麽內部調用形式為:handle_starttag(’a’,[(‘href’,’http://www.baidu.com)]).

l handle_endtag(tag):跟上述一樣,只是處理的是結束標簽,也就是以</開頭的標簽。

l handle_data(data):處理的是網頁的數據,也就是開始標簽和結束標簽之間的內容。例如:<script>...</script>的省略號內容

l handle_comment(data) ,處理註釋,<!-- -->之間的文本

l reset():將實例重置,包括作為參數輸入的數據進行清空。

二、基本使用

from html.parser import
HTMLParser class MyHTMLParser(HTMLParser): def handle_starttag(self, tag, attrs): """ recognize start tag, like <div> :param tag: :param attrs: :return: """ print("Encountered a start tag:", tag) def handle_endtag(self, tag): "
"" recognize end tag, like </div> :param tag: :return: """ print("Encountered an end tag :", tag) def handle_data(self, data): """ recognize data, html content string :param data: :return: """ print("Encountered some data :", data) def handle_startendtag(self, tag, attrs): """ recognize tag that without endtag, like <img /> :param tag: :param attrs: :return: """ print("Encountered startendtag :", tag) def handle_comment(self,data): """ :param data: :return: """ print("Encountered comment :", data) parser = MyHTMLParser() parser.feed(‘<html><head><title>Test</title></head>‘ ‘<body><h1>Parse me!</h1><img src = "" />‘ ‘<!-- comment --></body></html>‘)

以上是根據python手冊寫的基本使用,解析了一個簡單的html。可以運行看看,主要用於了解各個函數負責解析的部分,以及解析順序

Encountered a start tag: html
Encountered a start tag: head
Encountered a start tag: title
Encountered some data  : Test
Encountered an end tag : title
Encountered an end tag : head
Encountered a start tag: body
Encountered a start tag: h1
Encountered some data  : Parse me!
Encountered an end tag : h1
Encountered startendtag : img
Encountered comment :  comment
Encountered an end tag : body
Encountered an end tag : html

三、實用案例

以下的實用案例均在上面的代碼中修改對應函數,每個實例都是單獨的。

解析的html如下

<html>
    <head>
        <title>Test</title>
    </head>
    <body>
        <h1>Parse me!</h1>
        <img src = "" />
        <p class=‘123‘>A paragraph.</p>
                <p class = "p_font">A paragraph with class.</p>
                <!-- comment -->
        <div>
            <p>A paragraph in div.</p>
        </div>
    </body>
</html>

1.獲取所有p標簽的文本,最簡單方法只修改handle_data

def handle_data(self, data):
    if self.lasttag == ‘p‘:
        print("Encountered p data  :", data)

2.獲取css樣式(class)為p_font的p標簽的文本,使用了案例1,增加一個實例屬性作為標誌,選取需要的標簽

def __init__(self):
    HTMLParser.__init__(self)
    self.flag = False

def handle_starttag(self, tag, attrs):
for attr in attrs:
if tag == ‘p‘ and attr[1]==‘"p_font":
self.flag = True

def handle_data(self, data):
    if self.flag == True:
        print("Encountered p data  :", data)

3.獲取p標簽的屬性列表

def handle_starttag(self, tag, attrs):
    if tag == ‘p‘:
        print("Encountered p attrs  :", attrs)

4.獲取p標簽的class屬性

def handle_starttag(self, tag, attrs):
for attr in attrs:
    	If tag == ‘p‘ and attr[0]= ‘class‘):
        	print("Encountered p class  :", attr[1])

5.獲取div下的p標簽的文本

def __init__(self):
    HTMLParser.__init__(self)
    self.in_div = False

def handle_starttag(self, tag, attrs):
    if tag == ‘div‘:
        self.in_div = True

def handle_data(self, data):
    if self.in_div == True and self.lasttag == ‘p‘:
        print("Encountered p data  :", data)
self.in_div = False

6.處理註釋中的標簽,若需要的數據在註釋中,使用一般函數解析不到

處理方法為,寫兩個類,繼承HTMLParser。在其中一個類的handle_comment裏實例化解析類,和其他標簽一樣解析

def __init__(self):
    HTMLParser.__init__(self)

def handle_comment(self,data):
    print(("Encountered  comment:",data)

完整實例

例如:我們有以下一堆帶HTML標簽的數據

html = ‘‘‘<h3 class="tb-main-title" data-title="Xiaomi/小米">
     【金冠現貨/全色/頂配版】Xiaomi/小米 小米note移動聯通4G手機
   </h3>
   <p class="tb-subtitle">
 【購機即送布丁套+高清貼膜+線控耳機+剪卡器+電影支架等等,套餐更多豪禮更優惠】
 </p>
   <div id="J_TEditItem" class="tb-editor-menu"></div>
 </div>
<h3 class="tb-main-title" data-title="MIUI/小米">
     【現貨增強/標準】MIUI/小米 紅米手機2紅米2移動聯通電信4G雙卡
   </h3>
   <p class="tb-subtitle">
 [紅米手機2代顏色版本較多,請親們閱讀購買說明按需選購---感謝光臨]
   <div id="J_TEditItem" class="tb-editor-menu"></div>
 </div>‘‘‘

很明顯,這裏面包含了兩臺手機,我們的目標是提取兩個手機的名字出來。

由於當我們feed這個html到HTMLParser中後,他們所有的標簽都叠代,如果需要它只提取我們需要的數據時,我們需要設置當handle_starttag遇到那個標簽和屬性時,才調用handle_data並print出我們的結果,這個時候我們可以使用一個flag作為判定,代碼如下:

#定義一個MyParser繼承自HTMLParser
class MyParser(HTMLParser):
    re=[]#放置結果
    flag=0#標誌,用以標記是否找到我們需要的標簽
    def handle_starttag(self, tag, attrs):
        if tag==‘p‘:#目標標簽
            for attr in attrs:
                if attr[0]==‘class‘ and attr[1]==‘tb-subtitle‘:#目標標簽具有的屬性
                    self.flag=1#符合條件則將標誌設置為1
                    break
        else:
            pass

    def handle_data(self, data):
        if self.flag==1:
            self.re.append(data.strip())#如果標誌為我們設置的,則將數據添加到列表中
            self.flag=0#重置標誌,進行下次叠代
        else:
            pass


my=MyParser()
my.feed(html)
my.re

運行結果如下,達到了我們的預期:

[‘【購機即送布丁套+高清貼膜+線控耳機+剪卡器+電影支架等等,套餐更多豪禮更優惠】‘,

‘[紅米手機2代顏色版本較多,請親們閱讀購買說明按需選購---感謝光臨]‘]

python模塊之HTMLParser簡介