在上一篇中,抓取了百度貼吧中的圖片並保存在了本地文件夾中,不過可以發現裏面是利用正則表達式來過濾出圖片地址和頁面數字,於是在本節中,考慮直接利用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下載的地址:
- date="上面那一堆…………"
- lister=URLLister()
- lister.feed(date)
用feed()把要處理的html傳遞給對象實體,然後我們來看看處理結果:
- 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)
看看結果:
- listname=ListName()
- listname.feed(date)
- 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文件夾下,保存了所有圖集的圖片