python學習筆記三:解析html(HTMLParser、SGMLParser),並抓取圖片

在上一篇中,抓取了百度貼吧中的圖片並保存在了本地文件夾中,不過可以發現裏面是利用正則表達式來過濾出圖片地址和頁面數字,於是在本節中,考慮直接利用python中自帶的解析html的解析器HTMLParser和SGMLParser來分析html,另外由於SGMLParser比HTMLParser好用,於是這裏用了SGMLParser。

sgmllib.py 包含一個重要的類: SGMLParser。SGMLParser 將 HTML 分解成有用的片段, 比如開始標記和結束標記。一旦它成功地分解出某個數據爲一個有用的片段,它會根據 所發現的數據,調用一個自身內部的方法。爲了使用這個分析器,您需要子類化 SGML- Parser類,並且覆蓋這些方法。

SGMLParser類裏面包含了很多內部方法,開始讀取html後,遇到相應的數據就會調用其對應的方法,最重要的方法有三個:

  • start_tagname(self, attrs)
  • end_tagname(self)
  • handle_data(self, text)

tagname就是標籤名稱,比如當遇到<pre>,就會調用start_pre,遇到</pre>,就會調用 end_pre,attrs即爲標籤的參數,以[(attribute, value), (attribute, value), ...]的形式傳回,我們要做的就是在其子類重載自己感興趣標籤對應的函數。

一個經典的例子:


from sgmllib import SGMLParser

class URLLister(SGMLParser):
    def __init__(self):
        SGMLParser.__init__(self)
        self.urls = []
    def start_a(self, attrs):
        href = [v for k, v in attrs if k=='href']
        if href:
            self.urls.extend(href)

顧名思義,這個類的作用就是把html中的所有連接(<a>標籤)中的地址(href屬性的值)提取出來,放到一個list裏面,很實用的功能。^^

比如處理下面的html:

<tr>
<td height="207" colspan="2" align="left" valign="top" class="normal">
<p>Damien Rice - 《0》 </p>
<a href="http://galeki.xy568.net/music/Delicate.mp3">1. Delicate</a><br />
<a href="http://galeki.xy568.net/music/Volcano.mp3">2. Volcano</a><br />
<a href="http://galeki.xy568.net/music/The Blower's Daughter.mp3">3. The Blower's Daughter</a><br/>
<a href="http://galeki.xy568.net/music/Cannonball.mp3">4. Cannonball </a><br />
<a href="http://galeki.xy568.net/music/Older Chests.mp3">5. Order Chests</a><br />
<a href="http://galeki.xy568.net/music/Amie.mp3">6. Amie</a><br />
<a href="http://galeki.xy568.net/music/Cheers Darlin'.mp3">7. Cheers Darling</a><br />
<a href="http://galeki.xy568.net/music/Cold Water.mp3">8. Cold water</a><br />
<a href="http://galeki.xy568.net/music/I Remember.mp3">9. I remember</a><br />
<a href="http://galeki.xy568.net/music/Eskimo.mp3">10. Eskimo</a></p>
</td>
</tr>

很亂對吧?下面讓舉個例子利用URLLister提取出上面mp3下載的地址:

  1. date="上面那一堆…………"
  2. lister=URLLister()
  3. lister.feed(date)

用feed()把要處理的html傳遞給對象實體,然後我們來看看處理結果:

  1. print lister.urls

顯示:

['http://galeki.xy568.net/music/Delicate.mp3', 
'http://galeki.xy568.net/music/Volcano.mp3',
"http://galeki.xy568.net/music/The Blower's Daughter.mp3",
'http://galeki.xy568.net/music/Cannonball.mp3', 
'http://galeki.xy568.net/music/Older Chests.mp3', 
'http://galeki.xy568.net/music/Amie.mp3', 
"http://galeki.xy568.net/music/Cheers Darlin'.mp3",
'http://galeki.xy568.net/music/Cold Water.mp3', 
'http://galeki.xy568.net/music/I Remember.mp3', 
'http://galeki.xy568.net/music/Eskimo.mp3']

好了,是不是很方便?現在我們知道了如何處理標籤中的屬性,那麼如何處理標籤包含的文字呢?就是上面列出的handle_data(self, text),當遇到標籤內的內容,就會調用這個函數,傳入的text自然就是標籤內的內容了,不過,如何篩選出感興趣標籤內的內容呢?比如上面歌曲的列 表,這時候就要配合start_tagname、end_tagname,用做標記的方法來達到這個目的:

from sgmllib import SGMLParser


class ListName(SGMLParser):
    def __init__(self):
        SGMLParser.__init__(self)
        self.is_a = ""
        self.name = []
    def start_a(self,attrs):
        self.is_a = 1
    def end_a(self):
        self.is_a = ""

    def handle_data(self,text):
        if self.is_a == 1:
            self.name.append(text)

看看結果:

  1. listname=ListName()
  2. listname.feed(date)
  3. print listname.name

或者使用:

for item in listname.name:
	print item.decode('gbk').encode('utf8')  #當文字是中文時,這樣不會亂碼

顯示:

['1.Delicate', '2.Volcano', "3.The Blower's Daughter",
'4.Cannonball ', '5.Order Chests', '6.Amie', 
'7.Cheers Darling', '8.Cold water', '9.I remember', 
'10.Eskimo']

OK,搞定~


很簡單,這裏定義了一個叫做ListName的類,繼承SGMLParser裏面的方法。使用一個變量is_a做標記判定html文件中的a標籤,如果遇到a標籤,則將標籤內的內容加入到List變量name中。解釋一下start_a()和end_a(),它們原型是SGMLParser中的

start_tagname(self, attrs)
end_tagname(self)

tagname就是標籤名稱,比如當遇到<pre>,就會調用start_pre,遇到</pre>,就會調用 end_pre。attrs爲標籤的參數,以[(attribute, value), (attribute, value), ...]的形式傳回。


SGMLParser內置的方法不僅僅只有這三個,還有處理註釋的handle_comment,還有處理聲明的handle_decl等等等等,不過使用方法和上面的基本相同,不再多寫了。

完整代碼:

#coding:utf-8  
  
from sgmllib import SGMLParser  
  
class ListName(SGMLParser):  
  
    def reset(self):  
        #變量標示是否是標籤a,或使用self.is_a=True  
        self.is_a=""  
        self.name=[]  
        #繼承父類reset方法  
        SGMLParser.reset(self)  
  
    def start_a(self,attrs):  
        #如果是標籤a,則設置is_a=1  
        self.is_a=1  
  
    def end_a(self):  
        self.is_a=""  
  
    def handle_data(self,data):  
        #如果是標籤a的數據,則追加  
        if self.is_a:  
            self.name.append(data)  
  
if __name__ == '__main__':  
    urls=''''' 
    <tr> 
<td height="207" colspan="2" align="left" valign="top" class="normal"> 
<p>Damien Rice - 《0》 </p> 
<a href="http://galeki.xy568.net/music/Delicate.mp3">1. Delicate</a><br /> 
<a href="http://galeki.xy568.net/music/Volcano.mp3">2. Volcano</a><br /> 
<a href="http://galeki.xy568.net/music/The Blower's Daughter.mp3">3. The Blower's Daughter</a><br /> 
<a href="http://galeki.xy568.net/music/Cannonball.mp3">4. Cannonball </a><br /> 
<a href="http://galeki.xy568.net/music/Older Chests.mp3">5. Order Chests</a><br /> 
<a href="http://galeki.xy568.net/music/Amie.mp3">6. Amie</a><br /> 
<a href="http://galeki.xy568.net/music/Cheers Darlin'.mp3">7. Cheers Darling</a><br /> 
<a href="http://galeki.xy568.net/music/Cold Water.mp3">8. Cold water</a><br /> 
<a href="http://galeki.xy568.net/music/I Remember.mp3">9. I remember</a><br /> 
<a href="http://galeki.xy568.net/music/Eskimo.mp3">10. Eskimo</a></p> 
</td> 
</tr> 
    '''  
    listname=ListName()  
    listname.feed(urls)  
    #輸出文本的列表結果  
    print listname.name  
    listname.close()

以上是使用SGMLParser的一個簡單的例子介紹,下面我將利用SGMLParser和urllib2庫來實現圖片抓取主要抓的是http://desk.zol.com.cn/meinv/這個鏈接下的圖片,通過得到第一個圖集的起始URL地址,得到第一張圖片,然後提取出該圖集中下一張圖片的URL地址,不斷的去獲取其下一個圖片的URL,並下載圖片,下載完第一個圖集的圖片後,接着得到第二個圖集的起始URL地址,然後獲取其下一個圖片的URL,繼而得到所有首頁的圖集的圖片。

代碼如下:

# -*- coding: utf-8 -*-
#feimengjuan
import urllib
import urllib2
from sgmllib import SGMLParser
import re

# 根url
host = "http://desk.zol.com.cn"

# 定義保存文件夾
localSavePath = "girls/"

# 根據首頁得到的圖片集遍歷每個圖片集,獲得該圖片集下的所有圖片的地址
def getImageUrlByHtmlUrl(htmlUrl):
    parser = MySGMLParser(False) # 傳入False,來控制解析的是某個圖片集網址
    try:
        print "正在解析的網頁是:",htmlUrl
        response = urllib2.urlopen(htmlUrl)
        content = response.read()
        parser.feed(content) # 解析該圖片集網址
    except urllib2.URLError, e:
        print e.reason

# 下載圖片
def downLoadImage(imageUrl):
    print "正在下載的圖片地址:",imageUrl
    data = urllib2.urlopen(imageUrl).read()
    # 由圖片的地址名中分析取出後面的數字作爲圖片文件名來保存
    pattern = '[0-9]*\.jpg'
    match = re.search(pattern,imageUrl)
    if match:
        print '正在下載文件:',match.group()
        fileName = localSavePath + match.group()
        f = open(fileName,'wb+')
        f.write(data)
        f.close
    else:
        print 'no match'


class MySGMLParser(SGMLParser):
    def __init__(self,isIndex):
        self.isIndex = isIndex  # 程序開始時isIndex = Ture,
        SGMLParser.__init__(self)
        self.urls = []

    # 用標籤'a'來查找網頁鏈接,開始時,
    # 是從首頁面查找所有圖片集的網址,由isIndex = True來控制
    def start_a(self,attrs):
        # 程序最開始時,isIndex = Ture,表示解析的是初始網頁,從中獲得每個圖片集的網址
        if(self.isIndex):
            if(len(attrs) == 4):
                if(attrs[0] == ('class','pic')):
                    #提取出href的屬性,即圖片集的網址
                    newUrl = [host + v for k,v in attrs if k == 'href']
                    print '找到一處圖片集的網頁鏈接:',newUrl[0]
                    getImageUrlByHtmlUrl(newUrl[0])
        # isIndex = False,表示解析每個圖片集的第一張圖片所在的網頁,
        # 從中獲得每個圖片集下所有圖片的網頁
        else:
            if(len(attrs) == 4):
                # 由該圖片集中當前的圖片所在網頁中獲取下一張圖片的網址
                if(attrs[0] == ('id','pageNext')):
                    #提取出href的屬性,即下一張圖片的網址
                    href = [ v for k,v in attrs if k == 'href']
                    # 在每個圖片集最後一張圖片中沒有下一張圖片的鏈接地址,
                    # 而是爲"javascript:;",所以要區分出來
                    if (href[0] != "javascript:;"):
                        nextUrl = host + href[0]
                        print '找到該圖片集中一處圖片的網頁鏈接:',nextUrl
                        getImageUrlByHtmlUrl(nextUrl)

    def start_img(self,attrs): # 用標籤img來查找圖片地址並下載
        if(self.isIndex == False):
            # 在每個圖片集的最後一張圖片的html中無法找到下一張圖片的網頁鏈接,
            # 因此會一直將這個html解析到最後,故需要加一個判斷,表示已經解析完整個html了,
            # 此時attrs爲空,表示已經下載完了該圖片集的最後一張圖片
            if attrs:
                if(attrs[0] == ('id','bigImg')):
                    imageUrl = attrs[1][1]  # 圖片的地址
                    print '找到一張圖片:',imageUrl
                    downLoadImage(imageUrl) # 下載保存圖片


if __name__ == '__main__':
    url = "http://desk.zol.com.cn/meinv/"
    html = urllib.urlopen(url).read()
    # 開始時,傳入參數True,表示解析的圖集首頁,即給的網址
    mySparser = MySGMLParser(True)
    mySparser.feed(html)

最後,會在本地girls文件夾下,保存了所有圖集的圖片


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章