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文件夹下,保存了所有图集的图片


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值