• urllib2与urllib一些常用方法的比较

    对比urllib2与urllib
    服务器君一共花费 9.648 ms 进行了 4 次数据库查询,努力地为您提供了这个页面。
    广告很萌的

    现在分析 urllib2 里的urlopen, 一般我们直接调用urlopen()就是表示去调用build_opener()方法,然后用build_opener()方法返回的类对象去调用该对象的open方法,下面给出部分build_opener()的代码:

    #build_opener()
    def build_opener(*handlers):
        
        opener = OpenerDirector()
        default_classes = [ProxyHandler, UnknownHandler, HTTPHandler,
                           HTTPDefaultErrorHandler, HTTPRedirectHandler,
                           FTPHandler, FileHandler, HTTPErrorProcessor]
    
        skip = set()
        for klass in default_classes:
            for check in handlers:
                if isclass(check):
                    if issubclass(check, klass):
                        skip.add(klass)
                elif isinstance(check, klass):
                    skip.add(klass)
        for klass in skip:
            default_classes.remove(klass)
    
        for klass in default_classes:
            opener.add_handler(klass())
    
        for h in handlers:
            if isclass(h):
                h = h()
            opener.add_handler(h)
        return opener
    

    这里我们可以看到,使用 urllib2.urlopen() 生成的是我们前面提到的管理很多处理器类的 OpenerDirector 操作类,然后给他加入很多的处理器,作为其一个属性,然后调用该处理器操作类对象的 open 方法就可以获取页面了。 流程就是:

    1. 生成一个处理器 opener = OpenerDirector() 对象
    2. 给这个处理器对象加入处理器 opener.add_handler(h)
    3. 使用打开方法,可能是具体 handler 类的打开方法。获取至本地的“文件流”对象,使用“文件流”。read() 获取内容,写入文件。

    就是说,只要你是使用urllib2,不管你是要用代理,还是要用ftp,http,都逃不过这套过程。

    下面再分析 urllib 里的 urlopen(),使用 urllib.urlopen() 方法会生成一个 FancyURLopener 类的对象,而 FancyURLopener 类是URLopener 类的子类,那么这个类对象直接调用 URLopener 类的 open(url) 方法就行了,对于使用者来说,urllib.urlopen() 的使用方式更易于使用,但这只是初步使用上简单,在深度使用后,urllib 就没有 urllib2 结构清晰了。

    下面给出2种方法的不同实现:

    1. HTTPHandler方式

    #! -*- encoding:utf-8 -*-
    
    import urllib2
    
    opener = urllib2.OpenerDirector()
    handler = urllib2.HTTPHandler()
    opener.add_handler(handler)
    content_stream = opener.open('http://www.baidu.com/')
    print content_stream.read()
    

    2. ProxyHandler方式

    #! -*- encoding:utf-8 -*-
    
    import urllib2
    
    handler = urllib2.ProxyHandler(proxies = {'http' : 'http://217.66.205.76:8080/'})
    opener = urllib2.build_opener(handler)
    f = opener.open('http://www.baidu.com/')
    print f.read()
    

    这里可以看到做的操作有先实例化一个处理类,然后调用 build_opener 类产生我们的管理器对象,调用管理器的 open 法,就能获取网页内容了。细心的读者对这段示例程序不知道会不会有一个疑问,确定这里是以代理去打开的 baidu 首页吗?而不是这里的代理根本没起作用,实际仍然是以本地IP打开的?那么就需要测试了,只需要做一个简单的操作,将代理地址改一个不能使用的地址,比如:

    #! -*- encoding:utf-8 -*-
    
    import urllib2
    
    handler = urllib2.ProxyHandler(proxies = {'http' : 'http://216.664.205.76:8080/'})
    opener = urllib2.build_opener(handler)
    f = opener.open('http://www.baidu.com/')
    print f.read()
    

    运行这段代码就会得到错误的信息的。因为这代理地址根本就是我胡编乱造的。

    3. FileHandler

    #! -*- encoding:utf-8 -*-
    
    import urllib2
    
    handler = urllib2.FileHandler()
    request = urllib2.Request(url='file:/D:\myapplesapple_id.txt')
    opener = urllib2.build_opener(handler)
    f = opener.open(request)
    print f.read()
    

    注意这里一定要指定是文件类型,即url一定要有file:/而不能单单只写url='D:\myapplesapple_id.txt'

    4. FTPHandler

    先一步步的来:

    #! -*- encoding:utf-8 -*-
    
    import urllib2
    
    handler = urllib2.FTPHandler()
    request = urllib2.Request(url='ftp://www.×××××.com/')
    opener = urllib2.build_opener(handler)
    f = opener.open(request)
    print f.read()
    

    执行结果显示:

        if resp[0] == '3': resp = self.sendcmd('PASS ' + passwd)
      File "C:\Python26\lib\ftplib.py", line 243, in sendcmd
        return self.getresp()
      File "C:\Python26\lib\ftplib.py", line 218, in getresp
        raise error_perm, resp
    urllib2.URLError: <urlopen error ftp error: 530 Login or password incorrect!>
    

    提示需要登录用户名和密码:

    #! -*- encoding:utf-8 -*-
    
    import urllib2
    
    handler = urllib2.FTPHandler()
    request = urllib2.Request(url='ftp://用户名:密码@ftp地址/')
    opener = urllib2.build_opener(handler)
    f = opener.open(request)
    print f.read()
    

    执行以下,获得结果:

    drwxr-xr-x 1 ftp ftp              0 Jan 08  2011 *****
    drwxr-xr-x 1 ftp ftp              0 Jan 11  2011 *****
    drwxr-xr-x 1 ftp ftp              0 Oct 28  2010 *******
    

    OK了,带*的该ftp服务器下跟目录下的文件名。

    以上是一些 urllib 2 基本使用方法,但大家也可以看出就那么一套模式。下面是 urllib 的在写这些程序的做法。

    还是先给urllib的简单介绍吧。urllib 可以打开任意的一个 url 地址,遵循了一些标准,比如:

    • RFC1808:相对路径处理方法
    • RFC1738:标准url地址
    • RFC1630:url细则

    通过使用 URLopener().open(file) 将返回一个使用了不同协议操作的对象。接下来这个对象就可以调用像read(),readline(),readlines(),fileno(), close()和info()方法,大家可以看出很多都是类似于文件对象的方法。info方法返回一个mimetools.Message对象,能用于这个对象的各种信息状态的显示。如果使用info方法,将相应的调用getheader方法.

    urllib中主题就2个类,一个URLopener类,一个FancyURLopener类,FancyURLopener是URLopener类的子类,也就是对URLopener类的扩展。而其他绝大部分的类都是围绕或者基于这2个类进行处理,一个urllib模块只要通了URLopener其他甚至都可以自己扩展了。

    大多数情况下我们都是使用urllib.urlopen()。

    刚才已经对比过这2个方法了,可以看到urllib.urlopen()可以直接使用代理,假如我们真的使用不那么高级点,这个还是不错的,而urllib2 的 urlopen 却不是能直接支持代理的。所以这对于很多同学认定 urllib 比 urllib2 好,不明白为什么会有 urllib2 这个玩意的一个原因吧。

    直接使用urlopen:

    #! -*- encoding:utf-8 -*-
    
    import urllib
    
    f = urllib.urlopen('http://www.baidu.com/')
    print f.read()
    

    加入代理:

    #! -*- encoding:utf-8 -*-
    
    import urllib
    
    f = urllib.urlopen(url='http://www.baidu.com/', proxies={'has_key' : 'http://216.66.205.76:8080/'})
    
    print f.read()
    

    这里对比一下urllib2的代理的写法:

    • urllib2:             proxies = {'http' : 'http://217.66.205.76:8080/'}
    • urllib :              proxies={'has_key' : 'http://216.66.205.76:8080/'}

    这里没有谁要谁坏的一说,只是告诉大家加以区别而已。

    3. 打开本地文件

    #! -*- encoding:utf-8 -*-
    
    import urllib
    f = urllib.urlopen(url='file:/D:\\myapplesapple_id.txt')
    print f.read()
    

    4. ftp

    #! -*- encoding:utf-8 -*-
    
    import urllib
    f = urllib.urlopen(url='ftp://python:read@www.*****.com/')
    print f.read()
    

    URLopener结构:

    class URLopener:
        __tempfiles = None
        version = "Python-urllib/%s" % __version__
        def __init__(self, proxies=None, **x509):
        def __del__(self):
        def close(self):
        def cleanup(self):
        def addheader(self, *args):
        def open(self, fullurl, data=None):
        def open_unknown(self, fullurl, data=None):
        def open_unknown_proxy(self, proxy, fullurl, data=None):
        def retrieve(self, url, filename=None, reporthook=None, data=None):
        def open_http(self, url, data=None):
        def http_error(self, url, fp, errcode, errmsg, headers, data=None):
        def http_error_default(self, url, fp, errcode, errmsg, headers):
        def open_https(self, url, data=None):
        def open_file(self, url):
        def open_local_file(self, url):
        def open_ftp(self, url):
        def open_data(self, url, data=None):
    

    该类的设计逻辑是不管37是否等于21,先实例化该类对象出来,如果有代理,在实例化的时候将代理制定给这个实例化对象的一个属性,然后直接调用这个类的open方法,open方法里面有很多处理逻辑,比如,通过你给定url来判断要使用什么协议来对这个url进行处理,调用本类中的那个方法,设计中的一个经典核心代码:

    urltype, url = splittype(fullurl)  #解析url,分析出该url的特点,比如file,ftp,http,等
    	if not urltype:
    		urltype = 'file'
    	if urltype in self.proxies:
    		proxy = self.proxies[urltype]
    		urltype, proxyhost = splittype(proxy)
    		host, selector = splithost(proxyhost)
    		url = (host, fullurl) # Signal special case to open_*()
    	else:
    		proxy = None
    	name = 'open_' + urltype
    	self.type = urltype
    	name = name.replace('-', '_')
    	if not hasattr(self, name):
    		if proxy:
    			return self.open_unknown_proxy(proxy, fullurl, data)
    		else:
    			return self.open_unknown(fullurl, data)
    	try:
    		if data is None:
    			return getattr(self, name)(url)   #getattr()方法,如果name(形如'open_http','open_ftp','open_local_file')为真,返回这个属性,
                                   #即调用了这个方法,并且url是他传入的一个参数!从该类中其他方法名可以看出作者就是这个意思。
    		else:
    			return getattr(self, name)(url, data)
    	except socket.error, msg:
    		raise IOError, ('socket error', msg), sys.exc_info()[2]
    

    调用到符合 url 协议的方法了,然后就知道识别找方法,多么有意思的 python 代码,但是 guido 不满意,我不能妄自揣夺各种原因,一点我都不敢说,因为我在用他设计的语言,并且用的还很高兴。

更多 推荐条目

Welcome to NowaMagic Academy!

现代魔法 推荐于 2013-02-27 10:23   

本章最新发布
随机专题
  1. [软件工程与项目管理] 了解一点WebKit 9 个条目
  2. [PHP程序设计] httpd.conf设置相关 3 个条目
  3. [移动开发] Layout_weight属性解析 5 个条目
  4. [数据库技术] 数据库范式篇 5 个条目
  5. [Python程序设计] Tornado背景知识介绍 4 个条目
  6. [数据结构] 散列表(哈希表) 13 个条目
  7. [智力开发与知识管理] 学习编程为什么没会这么难? 7 个条目
  8. [移动开发] Content Provider内容提供者 3 个条目
  9. [C语言程序设计] 结构体基本知识 1 个条目
  10. [移动开发] Android SQLite增删查改实例(数据:魔弹之王) 2 个条目
  11. [计算机算法] 从双端队列引出的卡特兰数 3 个条目
  12. [Python程序设计] Django模板系统 11 个条目
窗口 -- [博客]