设为首页 收藏本站
查看: 623|回复: 0

[经验分享] [python相关]urllib2详解

[复制链接]

尚未签到

发表于 2015-10-26 12:44:16 | 显示全部楼层 |阅读模式
  from:http://www.cnblogs.com/CLTANG/archive/2011/09/15/2178163.html
  


  先发段代码:

?

main() { printf(&unix["\021%six\012\0"],(unix)["have"]+"fun"-0x60);}  以上的一条代码是1987年,由贝尔实验室的David Korn提交的获奖作品,为什么我想起这茬儿呢?还不是因为urllib和urllib2,“大师把代码写成上面那样可以获奖,你要把代码写成那样,就是垃圾”,这不是我的话,不过是有他的意思的。我看到了urllib和urllib2在设计,代码构造上很多不同,想到,或者是猜测python发展过程中,guido越来越看不惯urllib的混乱结构了,但是很多人已经习惯import
urllib了,并且用的还可以,所以urllib不管代码里多么混乱,但他能运行!很好,于是guido只能在urllib外在开发了urllib2,来满足一个有“洁癖”的程序员的心理需求呢,所以,当我这个菜鸟程序员看到urllib2的代码结构的时候,明显比urllib清晰了很多,明确了很多,心情好多了。介绍的时候我会对比urllib2怎么做的,而urllib又是怎么做的。

  


  1. 初步介绍
  urllib2
  urlib2是使用各种协议完成打开url的一个扩展包。最简单的使用方式是调用urlopen方法,比如

?

import
urllib2
content_stream
= urllib2.urlopen('http://www.baidu.com/')

content =
content_stream.read()
print
content  即可以接受一个字符串型的url地址或者一个Request对象。将打开这个url并返回结果为一个像文件对象一样的对象.
  接下来是OpenerDirector操作类。这是一个管理很多处理类(Handler)的类。而所有这些 Handler 类都对应处理相应的协议,或者特殊功能。分别有下面的处理类:BaseHandler,HTTPErrorProcessor,HTTPDefaultErrorHandler,HTTPRedirectHandler,ProxyHandler,
  AbstractBasicAuthHandler,HTTPBasicAuthHandler,ProxyBasicAuthHandler,AbstractDigestAuthHandler
  ProxyDigestAuthHandler,AbstractHTTPHandler,HTTPHandler,HTTPCookieProcessor,UnknownHandler,FileHandler,
  FTPHandler,CacheFTPHandler
  是不是很多?很怕,和我说看到看的结构简单不一致,不是的,他们都是遵循相应的规则,需求创建的类,甚至是相似的。你何必一口想吃成个胖子,全部都要会,先只管最最基本的需求吧,http协议的处理类。
  刚才我们说的最简单的urlib2的使用,也就是源码中给出的使用方式:

?

#file: urllib2.py

_opener =
None
def
urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):

    global
_opener
    if
_opener is
None:
        _opener
= build_opener()

    return
_opener.open(url, data, timeout)  对比给出urllib的最基本的使用方式:

?

_urlopener
= None
def
urlopen(url, data=None, proxies=None):

    """Create a file-like object for the specified URL to read from."""
    from
warnings import
warnpy3k
    warnpy3k("urllib.urlopen() has been removed in Python 3.0 in "
             "favor of urllib2.urlopen()", stacklevel=2)

  
    global
_urlopener
    if
proxies is
not None:
        opener
= FancyURLopener(proxies=proxies)

    elif
not _urlopener:
        opener
= FancyURLopener()

        _urlopener
= opener
    else:

        opener
= _urlopener

    if
data is
None:
        return
opener.open(url)

    else:

        return
opener.open(url, data)  有个小警告: warnpy3k("urllib.urlopen() has been removed in Python 3.0 in " "favor of urllib2.urlopen()", stacklevel=2) ,说在py3k中已经移除了urllib.urlopen,更加偏爱urllib2.urlopen(),我想或多或少证明了我前面的一点猜测,guido看不惯urllib里的一些代码,在python2.6里先给你些小提示,慢慢改习惯吧,兄弟们。
  现在分析urllib2里的urlopen, 一般我们直接调用urlopen()就是表示去调用build_opener()方法,然后用build_opener()方法返回的类对象去调用该对象的open方法,下面给出部分build_opener()的代码:

DSC0000.gif
#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. urllib2
  a. 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()
  b. 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()
  运行这段代码就会得到错误的信息的。因为这代理地址根本就是我胡编乱造的。
  c. 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'
  d  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 *******
  带*的该ftp服务器下跟目录下的文件名,bingo!
  以上是一些基本使用方法,但大家也可以看出就那么一套模式。下面是urllib的在写这些程序的做法
  还是先给urllib的介绍吧,前面虽然写过urllib,但是有同学说我写的有点虎头蛇尾,弥补下自己的罪过
  urllib可以打开任意的一个url地址,遵循了一些标准,比如
  -RFC1808:相对路径处理方法
  -RFC1738:标准url地址
  -RFC1630:url细则
  通过使用URLopener().open(file)将返回一个使用了不同协议操作的对象。接下来这个对象就可以调用像read(),readline(),readlines(),fileno(), close()和info()方法,大家可以看出很多都是类&#20284;于文件对象的方法。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这个玩意的一个原因吧。
  a. 直接使用urlopen

#! -*- encoding:utf-8 -*-

import urllib
f = urllib.urlopen('http://www.baidu.com/')
print f.read()
  b. 加入代理


#! -*- 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/'}
  这里没有谁要谁坏的一说,只是告诉大家加以区别而已。
  c 打开本地文件
  


#! -*- encoding:utf-8 -*-

import urllib
f = urllib.urlopen(url='file:/D:\\myapplesapple_id.txt')
print f.read()
  
  这里我们可以看到怎么看怎么我在瞎扯,论代码量来说urllib做同样的工作都是urllib比urllib2少写代码!老实说我有点心虚了,难道urllib2真的不那么能方便程序员吗?
  d ftp


#! -*- encoding:utf-8 -*-

import urllib
f = urllib.urlopen(url='ftp://python:read@www.*****.com/')
print f.read()
  正确执行,难道?!
  URLopener结构:


class URLopener:
__tempfiles = None
version = &quot;Python-urllib/%s&quot; % __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_' &#43; 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不满意,我不能妄自揣夺各种原因,一点我都不敢说,因为我在用他设计的语言。并且用的还很高兴,呵呵。
  好吧,就这样,我又失败了一次,我真是个失败者。想分析出urllib2比urllib好,结果却成现在这个模样,urllib比urllib2用起来简单。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-130972-1-1.html 上篇帖子: Python环境搭建 下篇帖子: Python框架、库和软件资源大全
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表