• python网络编程学习笔记(7):HTML和XHTML解析(HTMLParser、BeautifulSoup)


    转载请注明:@小五义http://www.cnblogs.com/xiaowuyi

    在python中能够进行html和xhtml的库有很多,如HTMLParser、sgmllib、htmllib、BeautifulSoup、mxTidy、uTidylib等,这里介绍一下HTMLParser、BeautifulSoup等模块。

    一、利用HTMLParser进行网页解析
    具体HTMLParser官方文档可参考http://docs.python.org/library/htmlparser.html#HTMLParser.HTMLParser

    1、从一个简单的解析例子开始
    例1:
    test1.html文件内容如下:

    <html> 
    <head> 
    <title> XHTML 与 HTML 4.01 标准没有太多的不同</title> 
    </head> 
    <body> 
    i love you 
    </body> 
    </html> 

    下面是能够列出title和body的程序示例:

    ##@小五义:http://www.cnblogs.com/xiaowuyi 
    ##HTMLParser示例 
    import HTMLParser 
    class TitleParser(HTMLParser.HTMLParser): 
        def __init__(self): 
            self.taglevels=[] 
            self.handledtags=['title','body'] #提出标签 
            self.processing=None 
            HTMLParser.HTMLParser.__init__(self) 
        def handle_starttag(self,tag,attrs): 
            if tag in self.handledtags: 
                self.data='' 
                self.processing=tag 
        def handle_data(self,data): 
            if self.processing: 
                self.data +=data 
        def handle_endtag(self,tag): 
            if tag==self.processing: 
                print str(tag)+':'+str(tp.gettitle()) 
                self.processing=None 
        def gettitle(self): 
            return self.data 
    fd=open('test1.html') 
    tp=TitleParser() 
    tp.feed(fd.read()) 

    运行结果如下:
    title: XHTML 与 HTML 4.01 标准没有太多的不同
    body:
    i love you
    程序定义了一个TitleParser类,它是HTMLParser类的子孙。HTMLParser的feed方法将接收数据,并通过定义的HTMLParser对象对数据进行相应的解析。其中handle_starttag、handle_endtag判断起始和终止tag,handle_data检查是否取得数据,如果self.processing不为None,那么就取得数据。

    2、解决html实体问题
    (HTML 中有用的字符实体)
    (1)实体名称
    当与到HTML中的实体问题时,上面的例子就无法实现,如这里将test1.html的代码改为:
    例2:

    <html> 
    <head> 
    <title> XHTML 与&quot; HTML 4.01 &quot;标准没有太多的不同</title> 
    </head> 
    <body> 
    i love you&times; 
    </body> 
    </html> 

    利用上面的例子进行分析,其结果是:
    title: XHTML 与 HTML 4.01 标准没有太多的不同
    body:
    i love you
    实体完全消失了。这是因为当出现实体的时候,HTMLParser调用了handle_entityref()方法,因为代码中没有定义这个方法,所以就什么都没有做。经过修改后,如下:

    ##@小五义:http://www.cnblogs.com/xiaowuyi 
    ##HTMLParser示例:解决实体问题 
    from htmlentitydefs import entitydefs 
    import HTMLParser 
    class TitleParser(HTMLParser.HTMLParser): 
        def __init__(self): 
            self.taglevels=[] 
            self.handledtags=['title','body'] 
            self.processing=None 
            HTMLParser.HTMLParser.__init__(self) 
        def handle_starttag(self,tag,attrs): 
            if tag in self.handledtags: 
                self.data='' 
                self.processing=tag 
        def handle_data(self,data): 
            if self.processing: 
                self.data +=data 
        def handle_endtag(self,tag): 
            if tag==self.processing: 
                print str(tag)+':'+str(tp.gettitle()) 
                self.processing=None 
        def handle_entityref(self,name): 
            if entitydefs.has_key(name): 
                self.handle_data(entitydefs[name]) 
            else: 
                self.handle_data('&'+name+';') 
        def gettitle(self): 
            return self.data 
    fd=open('test1.html') 
    tp=TitleParser() 
    tp.feed(fd.read()) 

    运行结果为:
    title: XHTML 与" HTML 4.01 "标准没有太多的不同
    body:
    i love you×
    这里就把所有的实体显示出来了。

    (2)实体编码
    例3:

    <html> 
    <head> 
    <title> XHTML 与&quot; HTML 4.01 &quot;标准没有太多的不同</title> 
    </head> 
    <body> 
    i love&#247; you&times; 
    </body> 
    </html> 

    如果利用例2的代码执行后结果为:
    title: XHTML 与" HTML 4.01 "标准没有太多的不同
    body:
    i love you×
    结果中&#247; 对应的÷没有显示出来。
    添加handle_charref()进行处理,具体代码如下:

    ##@小五义:http://www.cnblogs.com/xiaowuyi 
    ##HTMLParser示例:解决实体问题 
    from htmlentitydefs import entitydefs 
    import HTMLParser 
    class TitleParser(HTMLParser.HTMLParser): 
        def __init__(self): 
            self.taglevels=[] 
            self.handledtags=['title','body'] 
            self.processing=None 
            HTMLParser.HTMLParser.__init__(self) 
        def handle_starttag(self,tag,attrs): 
            if tag in self.handledtags: 
                self.data='' 
                self.processing=tag 
        def handle_data(self,data): 
            if self.processing: 
                self.data +=data 
        def handle_endtag(self,tag): 
            if tag==self.processing: 
                print str(tag)+':'+str(tp.gettitle()) 
                self.processing=None 
        def handle_entityref(self,name): 
            if entitydefs.has_key(name): 
                self.handle_data(entitydefs[name]) 
            else: 
                self.handle_data('&'+name+';') 
    
        def handle_charref(self,name): 
            try: 
                charnum=int(name) 
            except ValueError: 
                return 
            if charnum<1 or charnum>255: 
                return 
            self.handle_data(chr(charnum)) 
    
        def gettitle(self): 
            return self.data 
    fd=open('test1.html') 
    tp=TitleParser() 
    tp.feed(fd.read()) 

    运行结果为:
    title: XHTML 与" HTML 4.01 "标准没有太多的不同
    body:
    i love÷ you×

    3、提取链接
    例4: 

    <html> 
    <head> 
    <title> XHTML 与&quot; HTML 4.01 &quot;标准没有太多的不同</title> 
    </head> 
    <body> 
    
    <a href="http://pypi.python.org/pypi" title="link1">i love&#247; you&times;</a> 
    </body> 
    </html>


    这里在handle_starttag(self,tag,attrs)中,tag=a时,attrs记录了属性值,因此只需要将attrs中name=href的value提出即可。具体如下:

    ##@小五义:http://www.cnblogs.com/xiaowuyi 
    ##HTMLParser示例:提取链接 
    # -*- coding: cp936 -*- 
    from htmlentitydefs import entitydefs 
    import HTMLParser 
    class TitleParser(HTMLParser.HTMLParser): 
        def __init__(self): 
            self.taglevels=[] 
            self.handledtags=['title','body'] 
            self.processing=None 
            HTMLParser.HTMLParser.__init__(self)        
        def handle_starttag(self,tag,attrs): 
            if tag in self.handledtags: 
                self.data='' 
                self.processing=tag 
            if tag =='a': 
                for name,value in attrs: 
                    if name=='href': 
                        print '连接地址:'+value 
        def handle_data(self,data): 
            if self.processing: 
                self.data +=data 
        def handle_endtag(self,tag): 
            if tag==self.processing: 
                print str(tag)+':'+str(tp.gettitle()) 
                self.processing=None 
        def handle_entityref(self,name): 
            if entitydefs.has_key(name): 
                self.handle_data(entitydefs[name]) 
            else: 
                self.handle_data('&'+name+';') 
    
        def handle_charref(self,name): 
            try: 
                charnum=int(name) 
            except ValueError: 
                return 
            if charnum<1 or charnum>255: 
                return 
            self.handle_data(chr(charnum)) 
    
        def gettitle(self): 
            return self.data 
    fd=open('test1.html') 
    tp=TitleParser() 
    tp.feed(fd.read()) 

    运行结果为:
    title: XHTML 与" HTML 4.01 "标准没有太多的不同
    连接地址:http://pypi.python.org/pypi
    body:

    i love÷ you×

    4、提取图片
    如果网页中有一个图片文件,将其提取出来,并存为一个单独的文件。
    例5:

    <html> 
    <head> 
    <title> XHTML 与&quot; HTML 4.01 &quot;标准没有太多的不同</title> 
    </head> 
    <body> 
    i love&#247; you&times; 
    <a href="http://pypi.python.org/pypi" title="link1">我想你</a> 
    <div id="m"><img src="http://www.baidu.com/img/baidu_sylogo1.gif" width="270" height="129" ></div> 
    </body> 
    </html> 


    将baidu_sylogo1.gif存取出来,具体代码如下:

    ##@小五义:http://www.cnblogs.com/xiaowuyi 
    ##HTMLParser示例:提取图片 
    # -*- coding: cp936 -*- 
    from htmlentitydefs import entitydefs 
    import HTMLParser,urllib 
    def getimage(addr):#提取图片并存在当前目录下 
        u = urllib.urlopen(addr) 
        data = u.read() 
        filename=addr.split('/')[-1] 
        f=open(filename,'wb') 
        f.write(data) 
        f.close() 
        print filename+'已经生成!' 
    
    class TitleParser(HTMLParser.HTMLParser): 
        def __init__(self): 
            self.taglevels=[] 
            self.handledtags=['title','body'] 
            self.processing=None 
            HTMLParser.HTMLParser.__init__(self)        
        def handle_starttag(self,tag,attrs): 
            if tag in self.handledtags: 
                self.data='' 
                self.processing=tag 
            if tag =='a': 
                for name,value in attrs: 
                    if name=='href': 
                        print '连接地址:'+value 
            if tag=='img': 
                for name,value in attrs: 
                    if name=='src': 
                        getimage(value) 
        def handle_data(self,data): 
            if self.processing: 
                self.data +=data 
        def handle_endtag(self,tag): 
            if tag==self.processing: 
                print str(tag)+':'+str(tp.gettitle()) 
                self.processing=None 
        def handle_entityref(self,name): 
            if entitydefs.has_key(name): 
                self.handle_data(entitydefs[name]) 
            else: 
                self.handle_data('&'+name+';') 
    
        def handle_charref(self,name): 
            try: 
                charnum=int(name) 
            except ValueError: 
                return 
            if charnum<1 or charnum>255: 
                return 
            self.handle_data(chr(charnum)) 
    
        def gettitle(self): 
            return self.data 
    fd=open('test1.html') 
    tp=TitleParser() 
    tp.feed(fd.read()) 

    运动结果为:
    title: XHTML 与" HTML 4.01 "标准没有太多的不同
    连接地址:http://pypi.python.org/pypi
    baidu_sylogo1.gif已经生成!
    body:
    i love÷ you×
    ?ò????

    5、实际例子:
    例6、获取人人网首页上的各各链接地址,代码如下:

    ##@小五义:http://www.cnblogs.com/xiaowuyi 
    ##HTMLParser示例:获取人人网首页上的各各链接地址 
    #coding: utf-8 
    from htmlentitydefs import entitydefs 
    import HTMLParser,urllib 
    def getimage(addr): 
        u = urllib.urlopen(addr) 
        data = u.read() 
        filename=addr.split('/')[-1] 
        f=open(filename,'wb') 
        f.write(data) 
        f.close() 
        print filename+'已经生成!' 
    class TitleParser(HTMLParser.HTMLParser): 
        def __init__(self): 
            self.taglevels=[] 
            self.handledtags=['a'] 
            self.processing=None 
            self.linkstring='' 
            self.linkaddr='' 
            HTMLParser.HTMLParser.__init__(self)        
        def handle_starttag(self,tag,attrs): 
            if tag in self.handledtags: 
                for name,value in attrs: 
                    if name=='href': 
                        self.linkaddr=value 
                self.processing=tag 
    
        def handle_data(self,data): 
            if self.processing: 
                self.linkstring +=data 
                #print data.decode('utf-8')+':'+self.linkaddr 
        def handle_endtag(self,tag): 
            if tag==self.processing: 
                print self.linkstring.decode('utf-8')+':'+self.linkaddr 
                self.processing=None 
                self.linkstring='' 
        def handle_entityref(self,name): 
            if entitydefs.has_key(name): 
                self.handle_data(entitydefs[name]) 
            else: 
                self.handle_data('&'+name+';') 
    
        def handle_charref(self,name): 
            try: 
                charnum=int(name) 
            except ValueError: 
                return 
            if charnum<1 or charnum>255: 
                return 
            self.handle_data(chr(charnum)) 
    
        def gettitle(self): 
            return self.linkaddr 
    tp=TitleParser() 
    tp.feed(urllib.urlopen('http://www.renren.com/').read())

    运行结果:
    分享:http://share.renren.com
    应用程序:http://app.renren.com
    公共主页:http://page.renren.com
    人人生活:http://life.renren.com
    人人小组:http://xiaozu.renren.com/
    同名同姓:http://name.renren.com
    人人中学:http://school.renren.com/allpages.html
    大学百科:http://school.renren.com/daxue/
    人人热点:http://life.renren.com/hot
    人人小站:http://zhan.renren.com/
    人人逛街:http://j.renren.com/
    人人校招:http://xiaozhao.renren.com/
    :http://www.renren.com
    注册:http://wwv.renren.com/xn.do?ss=10113&rt=27
    登录:http://www.renren.com/
    帮助:http://support.renren.com/helpcenter
    给我们提建议:http://support.renren.com/link/suggest
    更多:#
    :javascript:closeError();
    打开邮箱查收确认信:#
    重新输入:javascript:closeError();
    :javascript:closeStop();
    客服:http://help.renren.com/#http://help.renren.com/support/contomvice?pid=2&selection={couId:193,proId:342,cityId:1000375}
    :javascript:closeLock();
    立即解锁:http://safe.renren.com/relive.do
    忘记密码?:http://safe.renren.com/findPass.do
    忘记密码?:http://safe.renren.com/findPass.do
    换一张:javascript:refreshCode_login();
    MSN:#
    360:https://openapi.360.cn/oauth2/authorize?client_id=5ddda4458747126a583c5d58716bab4c&response_type=code&redirect_uri=http://www.renren.com/bind/tsz/tszLoginCallBack&scope=basic&display=default
    天翼:https://oauth.api.189.cn/emp/oauth2/authorize?app_id=296961050000000294&response_type=code&redirect_uri=http://www.renren.com/bind/ty/tyLoginCallBack
    为什么要填写我的生日?:#birthday
    看不清换一张?:javascript:refreshCode();
    想了解更多人人网功能?点击此处:javascript:;
    :javascript:;
    :javascript:;
    立刻注册:http://reg.renren.com/xn6245.do?ss=10113&rt=27
    关于:http://www.renren.com/siteinfo/about
    开放平台:http://dev.renren.com
    人人游戏:http://wan.renren.com
    公共主页:http://page.renren.com/register/regGuide/
    手机人人:http://mobile.renren.com/mobilelink.do?psf=40002
    团购:http://www.nuomi.com
    皆喜网:http://www.jiexi.com
    营销服务:http://ads.renren.com
    招聘:http://job.renren-inc.com/
    客服帮助:http://support.renren.com/helpcenter
    隐私:http://www.renren.com/siteinfo/privacy
    京ICP证090254号:http://www.miibeian.gov.cn/
    互联网药品信息服务资格证:http://a.xnimg.cn/n/core/res/certificate.jpg

    二、利用BeautifulSoup进行网页解析
    1、BeautifulSoup下载和安装
    下载地址:http://www.crummy.com/software/BeautifulSoup/download/3.x/
    中文文档地址:http://www.crummy.com/software/BeautifulSoup/bs3/documentation.zh.html#Entity%20Conversion
    安装方法:将下载的文件解压缩后,文件夹下有个setup.py文件,然后在cmd下,运行python setup.py install进行安装,注意setup.py的路径问题。安装成功后,在python中就可以直接import BeautifulSoup了。
    2、从一个简单的解析例子开始
    例7:

    <html> 
    <head> 
    <title> XHTML 与&quot; HTML 4.01 &quot;标准没有太多的不同</title> 
    </head> 
    <body> 
    i love&#247; you&times; 
    <a href="http://pypi.python.org/pypi" title="link1">我想你</a> 
    <div id="m"><img src="http://www.baidu.com/img/baidu_sylogo1.gif" width="270" height="129" ></div> 
    </body> 
    </html> 

    获取title的代码:

    ##@小五义:http://www.cnblogs.com/xiaowuyi 
    ##BeautifulSoup示例:title 
    #coding: utf8 
    import BeautifulSoup 
    
    a=open('test1.html','r') 
    htmlline=a.read() 
    soup=BeautifulSoup.BeautifulSoup(htmlline.decode('gb2312')) 
    #print soup.prettify()#规范化html文件 
    titleTag=soup.html.head.title 
    print titleTag.string 

    运行结果:
    XHTML 与&quot; HTML 4.01 &quot;标准没有太多的不同
    从代码和结果来看,应注意两点:
    第一,在BeautifulSoup.BeautifulSoup(htmlline.decode('gb2312'))初始化过程中,应注意字符编码格式,从网上搜索了一下,开始用utf-8的编码显示不正常,换为gb2312后显示正常。其实可以用soup.originalEncoding方法来查看原文件的编码格式。
    第二,结果中未对字符实体进行处理,在BeautifulSoup中文文档中,有专门对实体转换的解释,这里将上面的代码改为以下代码后,结果将正常显示:

    ##@小五义:http://www.cnblogs.com/xiaowuyi 
    ##BeautifulSoup示例:title 
    #coding: utf8 
    import BeautifulSoup 
    a=open('test1.html','r') 
    htmlline=a.read() 
    soup=BeautifulSoup.BeautifulStoneSoup(htmlline.decode('gb2312'),convertEntities=BeautifulSoup.BeautifulStoneSoup.ALL_ENTITIES) 
    #print soup.prettify()#规范化html文件 
    titleTag=soup.html.head.title 
    print titleTag.string 

    这里convertEntities=BeautifulSoup.BeautifulStoneSoup.ALL_ENTITIES中的ALL_ENTITIES定义了XML和HTML两者的实体代码。当然,也可以直接用XML_ENTITIES或者HTML_ENTITIES。运行结果如下:
    XHTML 与" HTML 4.01 "标准没有太多的不同
    3、提取链接
    还有用上面的例子,这里代码变为:

    ##@小五义:http://www.cnblogs.com/xiaowuyi 
    ##BeautifulSoup示例:提取链接 
    #coding: utf8 
    import BeautifulSoup 
    a=open('test1.html','r') 
    htmlline=a.read() 
    a.close() 
    soup=BeautifulSoup.BeautifulStoneSoup(htmlline.decode('gb2312'),convertEntities=BeautifulSoup.BeautifulStoneSoup.ALL_ENTITIES) 
    name=soup.find('a').string 
    links=soup.find('a')['href'] 
    print name+':'+links 

    运行结果为:
    我想你:http://pypi.python.org/pypi
    4、提取图片
    依然是用上面的例子,把baidu图片提取出来。
    代码为:

    ##@小五义:http://www.cnblogs.com/xiaowuyi
    #coding: utf8 
    import BeautifulSoup,urllib 
    def getimage(addr):#提取图片并存在当前目录下 
        u = urllib.urlopen(addr) 
        data = u.read() 
        filename=addr.split('/')[-1] 
        f=open(filename,'wb') 
        f.write(data) 
        f.close() 
        print filename+' finished!' 
    a=open('test1.html','r') 
    htmlline=a.read() 
    soup=BeautifulSoup.BeautifulStoneSoup(htmlline.decode('gb2312'),convertEntities=BeautifulSoup.BeautifulStoneSoup.ALL_ENTITIES) 
    links=soup.find('img')['src'] 
    getimage(links) 

    提取链接和提取图片两部分主要都是用了find方法,具体方法为:
    find(name, attrs, recursive, text, **kwargs)
    findAll是列出全部符合条件的,find只列出第一条。这里注意的是findAll返回的是个list。
    5、实际例子:
    例8、获取人人网首页上的各各链接地址,代码如下:

    ##@小五义:http://www.cnblogs.com/xiaowuyi 
    ##BeautifulSoup示例:获取人人网首页上的各各链接地址 
    #coding: utf8 
    import BeautifulSoup,urllib 
    linkname='' 
    htmlline=urllib.urlopen('http://www.renren.com/').read() 
    soup=BeautifulSoup.BeautifulStoneSoup(htmlline.decode('utf-8')) 
    links=soup.findAll('a') 
    for i in links: 
        ##判断tag是a的里面,href是否存在。 
        if 'href' in str(i): 
            linkname=i.string 
            linkaddr=i['href'] 
            if 'NoneType' in str(type(linkname)):#当i无内容是linkname为Nonetype类型。 
                print linkaddr 
            else: 
                print linkname+':'+linkaddr 

    运行结果:
    分享:http://share.renren.com
    应用程序:http://app.renren.com
    公共主页:http://page.renren.com
    人人生活:http://life.renren.com
    人人小组:http://xiaozu.renren.com/
    同名同姓:http://name.renren.com
    人人中学:http://school.renren.com/allpages.html
    大学百科:http://school.renren.com/daxue/
    人人热点:http://life.renren.com/hot
    人人小站:http://zhan.renren.com/
    人人逛街:http://j.renren.com/
    人人校招:http://xiaozhao.renren.com/
    http://www.renren.com
    注册:http://wwv.renren.com/xn.do?ss=10113&rt=27
    登录:http://www.renren.com/
    帮助:http://support.renren.com/helpcenter
    给我们提建议:http://support.renren.com/link/suggest
    更多:#
    javascript:closeError();
    打开邮箱查收确认信:#
    重新输入:javascript:closeError();
    javascript:closeStop();
    客服:http://help.renren.com/#http://help.renren.com/support/contomvice?pid=2&selection={couId:193,proId:342,cityId:1000375}
    javascript:closeLock();
    立即解锁:http://safe.renren.com/relive.do
    忘记密码?:http://safe.renren.com/findPass.do
    忘记密码?:http://safe.renren.com/findPass.do
    换一张:javascript:refreshCode_login();
    MSN:#
    360:https://openapi.360.cn/oauth2/authorize?client_id=5ddda4458747126a583c5d58716bab4c&response_type=code&redirect_uri=http://www.renren.com/bind/tsz/tszLoginCallBack&scope=basic&display=default
    天翼:https://oauth.api.189.cn/emp/oauth2/authorize?app_id=296961050000000294&response_type=code&redirect_uri=http://www.renren.com/bind/ty/tyLoginCallBack
    #birthday
    看不清换一张?:javascript:refreshCode();
    javascript:;
    javascript:;
    立刻注册:http://reg.renren.com/xn6245.do?ss=10113&rt=27
    关于:http://www.renren.com/siteinfo/about
    开放平台:http://dev.renren.com
    人人游戏:http://wan.renren.com
    公共主页:http://page.renren.com/register/regGuide/
    手机人人:http://mobile.renren.com/mobilelink.do?psf=40002
    团购:http://www.nuomi.com
    皆喜网:http://www.jiexi.com
    营销服务:http://ads.renren.com
    招聘:http://job.renren-inc.com/
    客服帮助:http://support.renren.com/helpcenter
    隐私:http://www.renren.com/siteinfo/privacy
    京ICP证090254号:http://www.miibeian.gov.cn/
    互联网药品信息服务资格证:http://a.xnimg.cn/n/core/res/certificate.jpg

  • 相关阅读:
    Android Studio更新升级方法
    手把手教你用android studio创建第一个安卓程序加载html5页面(二)
    手把手教你用android studio创建第一个安卓程序加载html5页面(一)
    spoon kettle连接数据库失败解决方法
    AE多用户同时编辑同一个版本数据的解决方法
    ARCGIS知乎上的好文章
    mac系统访问windows共享文件夹
    奶爸程序员的“育儿”心得
    tomcat取带有中文的参数乱码的解决办法
    java问题排查可能用到的一些命令
  • 原文地址:https://www.cnblogs.com/xiaowuyi/p/2721658.html
Copyright © 2020-2023  润新知