urllib 源码小剖

urllib 是 python 内置的网络爬虫模块,如果熟悉 python 一定能很快上手使用 urllib。

写这篇文章的目的是因为用到了它,但因为用的次数较多,又或者是具体的需求,有必要深入去理解内部的工作方式。

urllib 最简单的使用,我也从下面的语句中开始:

1
2
3
4
import urllib
params = urllib.urlencode({'spam': 1, 'eggs': 2, 'bacon': 0})
f = urllib.urlopen("http://www.musi-cal.com/cgi-bin/query?%s" % params)
print f.read()

urllib 是模块,urlopen 是模块中的一个方法,它应该属于最高层的封装了,对于传入的任意 url 都能够处理,不管是 http还是https,还是 ftp 还是 file(本地文件).
它返回一个文件对象的包装类,里面除了文件对象,还有 HTTP response 的头和状态码,url 等;根据网络环境或者服务响应速度,会延迟一些时间。

注意,在这个时候,网络上的资源已经读取到了本地,被放在一个文件中。

接下来,f.read 从文件对象中读取数据。

下面是 urlopen 的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def urlopen(url, data=None, proxies=None):
    ......
    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)

其中,我们可以得到的讯息是,它创建了类 FancyURLopener 对象,并调用了它的 open方法,而类 FancyURLopener 就是 urllib 的核心。

FancyURLopener 其实是 URLopener 的子类,所以从 URLopener 开始说起。

__tempfiles 是一个 list,用来存储从网络爬取到本地的本地文件名,你可以单独调用这个方法
addheader 添加 HTTP 头,得到了一个 URLopener 对象,就可以使用此函数添加额外的 HTTP 头

open 上面已经提到的,它相当于一个工程老板,会根据不同的 url 来为不同的部门派发不同任务,比如,提供的是 http://baidu.com 就会调用 open_http
open_unknown 无法解析的 url就会调用它,抛出异常
retrieve 爬取网络资源,存储在本地文件,返回一个本地文件的文件名和 HTTP 的response 头
open_http 上面提到过,很综合的处理函数,可以提供 HTTP 基本访问认证,proxy 认证等功能,调用 httplib库的函数。在得到 HTTP response后,会根据 HTTP status 状态码返回爬取的结果或者调用 error 处理函数 http_error

http_error 它其实也是个老板,会根据不同的状态码,为不同的部门分发不同的任务,比如,302 状态码就会调用 http_error_302 方法,302 是资源被临时迁移了,所以会发起再次的请求。
http_error_default 抛出异常,当懒得理那些毛毛小小的错误,就会使用这样的函数

open_https 提供 https 的爬取,和 open_http 差不多
open_file 爬取 ftp 或者直接读取本地文件
open_local_file open_file 当需要直接读取本地文件时候会调用此函数
open_ftp open_file 当 ftp 资源时候会调用此函数
open_data 好似官方没怎么介绍,应该可以忽略它

FancyURLopener 是 urlopener 的子类,主要提供了更详细的错误处理
http_error_302 302 状态码的处理
redirect_internal 302 里边调用这个

http_error_301 直接调用302
http_error_303 直接调用302
http_error_307 当是 POST 的时候,调用直接调用 http_error_default;其他调用 http_error_default

http_error_401 是认证处理
http_error_407 是认证处理,但需要 proxy 代理
retry_proxy_http_basic_auth 代理重新认证 401 的时候会用到
retry_proxy_https_basic_auth 同上
retry_http_basic_auth 访问认证
retry_https_basic_auth 同上
prompt_user_passwd 认证的时候需要账号密码,控制台输入

关于 HTTP 协议的基本认证,推荐阅读:HTTP://www.cnblogs.com/TankXiao/archive/2012/09/26/2695955.html 简单明了

从上面可以看出,无论是 urlopener 还是 FancyURLopener 都没有涉及具体的 ftp 操作,因为在 urllib 中有为 ftp 提供封装:class ftpwrapper 在 open_ftp 中会直接创建 ftpwrapper 对象,然后执行其内部操作。
具体不叙述了。

class addbase 主要包装对文件对象的操作 read close 等
class addinfo addbase 的子类,添加了返回 HTTP response 头方法
class addinfourl addinfo 的子类,添加返回 url 方法

print f.read() 这一句调用其实就是 文件对象的 read,但它是 addinfourl 对象
接下来就是一些实用的工具函数了,主要处理各式各样的 url,譬如提取url里面的 host,port等。源码里有各种实用方法的效果图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Utilities to parse URLs (most of these return None for missing parts):
# unwrap('<URL:type://host/path>') --> 'type://host/path'
# splittype('type:opaquestring') --> 'type', 'opaquestring'
# splithost('//host[:port]/path') --> 'host[:port]', '/path'
# splituser('user[:passwd]@host[:port]') --> 'user[:passwd]', 'host[:port]'
# splitpasswd('user:passwd') -> 'user', 'passwd'
# splitport('host:port') --> 'host', 'port'
# splitquery('/path?query') --> '/path', 'query'
# splittag('/path#tag') --> '/path', 'tag'
# splitattr('/path;attr1=value1;attr2=value2;...') ->
# '/path', ['attr1=value1', 'attr2=value2', ...]
# splitvalue('attr=value') --> 'attr', 'value'
# unquote('abc%20def') -> 'abc def'
# quote('abc def') -> 'abc%20def')

最后总结,urlopen 是最高层的封装,简单的一句话就可以爬取 WWW 很简单;其内部是通由 FancyURLopener 实现,FancyURLopener 是 URLopener 的父类:URLopener 实现了爬取方法,但未定义对应具体状态码的 error handlers,这些由 FancyURLopener 定义。

捣乱  2013-08-25

http://daoluan.net

 
 
分类: 网络

urllib 源码小剖的更多相关文章

  1. urllib2 源码小剖

    urllib2 源码小剖 2013-08-25 23:38 by 捣乱小子, 272 阅读, 0 评论, 收藏, 编辑 两篇小剖已经完成: urllib 源码小剖 urllib2 源码小剖 urlli ...

  2. Django 源码小剖: 响应数据 response 的返回

    响应数据的返回 在 WSGIHandler.__call__(self, environ, start_response) 方法调用了 WSGIHandler.get_response() 方法, 由 ...

  3. Django 源码小剖: 初探 WSGI

    Django 源码小剖: 初探 WSGI python 作为一种脚本语言, 已经逐渐大量用于 web 后台开发中, 而基于 python 的 web 应用程序框架也越来越多, Bottle, Djan ...

  4. Django 源码小剖: 初探中间件(middleware)

    因为考虑到文章的长度, 所以 BaseHandler 的展开被推迟了. 在 BaseHandler 中隐藏着中间件的信息, 较常见的 SessionMiddleware 就已经默认安装.  BaseH ...

  5. Django 源码小剖: Django 对象关系映射(ORM)

    引 从前面已经知道, 一个 request 的到来和一个对应 response 的返回的流程, 数据处理和数据库离不开. 我们也经常在 views.py 的函数定义中与数据库打交道. django O ...

  6. Django 源码小剖: Django 中的 WSGI

    Django 其内部已经自带了一个方便本地测试的小服务器, 所以在刚开始学习 Django 的时候并不需搭建 apache 或者 nginx 服务器. Django 自带的服务器基于 python w ...

  7. Django 源码小剖: Django ORM 查询管理器

    ORM 查询管理器 对于 ORM 定义: 对象关系映射, Object Relational Mapping, ORM, 是一种程序设计技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换.从 ...

  8. Django 源码小剖: 更高效的 URL 调度器(URL dispatcher)

    效率问题 django 内部的 url 调度机制说白了就是给一张有关匹配信息的表, 这张表中有着 url -> action 的映射, 当请求到来的时候, 一个一个(遍历)去匹配. 中, 则调用 ...

  9. Django 源码小剖: URL 调度器(URL dispatcher)

    在刚开始接触 django 的时候, 我们尝试着从各种入门文档中创建一个自己的 django 项目, 需要在 mysite.urls.py 中配置 URL. 这是 django url 匹配处理机制的 ...

随机推荐

  1. 手工制作的年份Java老A发售量

    Java老A这本书是写了很长的时间,昨天终于开始china-pub.京东.活动当天发售的猫,现在,简称买卖,他当然还没有到. 有兴趣的人能够去看看哈(兴许其它站点地址也会在这里公开): china-p ...

  2. Linux下javaweb

    Linux下javaweb环境搭建 步骤: 1.使用远程工具连接上服务器,例如xsheel(ssh).filezilla(ftp) 2.JDK安装及相关配置 3.Mysql安装及相关配置 4.Tomc ...

  3. 在ubuntu上部署hadoop时出现的问题

    1. 配置ssh登录 不须要改动/etc/ssh/sshd_config 2. 新建hadoop用户时,home以下没有hadoop文件夹 用以下命令创建 useradd -m hadoop 3. n ...

  4. MVC自定义过滤器,自定义Area过滤器,自定义Controller,Action甚至是ViewData过滤器

    实现MVC自定义过滤器,自定义Area过滤器,自定义Controller,Action甚至是ViewData过滤器 MVC开发中几种以AOP方式实现的Filters是非常好用的,默认情况下,我们通过A ...

  5. Linux 下 简单客户端服务器通讯模型(TCP)

    原文:Linux 下 简单客户端服务器通讯模型(TCP) 服务器端:server.c #include<stdio.h> #include<stdlib.h> #include ...

  6. 天体程序猿叹息——变化hosts对

    没有解释更新一SDK像贼,我真诚地希望在天上Android.ios我们已经禁止了.要玩不能玩才最寂寞 安装成功不寂寞hosts文件: 74.125.237.1 dl-ssl.google.com173 ...

  7. C++ ofstream和ifstream具体的方法和C语言file说明

    ofstream是从内存到硬盘,ifstream是从硬盘到内存,事实上所谓的流缓冲就是内存空间; 在C++中,有一个stream这个类,全部的I/O都以这个"流"类为基础的,包含我 ...

  8. DDD分层架构之值对象(介绍篇)

    DDD分层架构之值对象(介绍篇) 前面介绍了DDD分层架构的实体,并完成了实体层超类型的开发,同时提供了验证方面的支持.本篇将介绍另一个重要的构造块——值对象,它是聚合中的主要成分. 如果说你已经在使 ...

  9. android的EditText获取另一个焦点

    在android关于开发过程EditText在setFocusable(false);后,设置时需要再次获得输入焦点setFocusable(true);问题后仍然无法获得焦点: 解决的方法: 对Ed ...

  10. SpringMVC 异常处理

    SpringMVC学习系列(10) 之 异常处理 在项目中如何处理出现的异常,在每个可能出现异常的地方都写代码捕捉异常?这显然是不合理的,当项目越来越大是也是不可维护的.那么如何保证我们处理异常的代码 ...