# urllib2

* 1 使用
  * 1.1 实例一，Get 方法， 并且自定义 header
  * 1.2 实例二，post 方法
  * 1.3 实例三：Cookie 的处理
* 2 urllib2 更多细节
  * 2.1 urlopen
    * 构造方法
    * 实例方法
  * 2.2 Request
  * 2.3 install\_opener/build\_opener
  * 2.4 密码验证
  * 2.5 Cookie
  * 2.6 异常处理
    * URLError
    * HTTPError
  * 2.7 Timeout 设置
  * 2.8 在 HTTP Request 中加入特定的 Header
  * 2.9 Redirect
  * 2.10 Cookie
  * 2.11 使用 HTTP 的 PUT 和 DELETE 方法
  * 2.12 Debug Log
* 3 封装为库
  * 3.1 初版
  * 3.2 升级版
* 4 常见问题处理
  * 4.1 urllib2.URLError: urlopen error \[Errno -3]
* 5 其他库

## 1 使用

urllib2 是 Python 自带的标准模块， 用来发送 HTTP Request 的

Python urllib2 发出的 http Request, 中的 header 会被修改成“首字母大写”，

比如你的代码里写的 header 是： content-TYPE=application/x-www-form-urlencoded ， 会被修改为 Content-type=application/x-www-form-urlencoded

### 1.1 实例一，Get 方法， 并且自定义 header

```
# -* - coding: UTF-8 -* -
import urllib2

request = urllib2.Request("http://www.baidu.com/")
request.add_header('content-TYPE', 'application/x-www-form-urlencoded')
response = urllib2.urlopen(request)
print response.getcode()
print response.geturl()
print response.read()
```

### 1.2 实例二，post 方法

> 方式 1

```
# -* - coding: UTF-8 -* -
import urllib2
import urllib

request = urllib2.Request("http://passport.cnblogs.com/login.aspx")
request.add_header('Content-type', 'application/x-www-form-urlencoded')
data={"tbUserName":"test_username", "tbPassword":"test_password"}

# urllib.urlencode(data) :'tbUserName=test_username&tbPassword=test_password'

response = urllib2.urlopen(request, urllib.urlencode(data))

print response.getcode()
print response.geturl()
print response.read()
```

> 方式 2 (butterfly POST 请求方式）

```
# -* - coding: UTF-8 -* -
import urllib2
import json

request = urllib2.Request("http://127.0.0.1:8585/x/hello")
request.add_header('Content-type', 'application/json')
data={"str_info":"meetbill"}
data_json = json.dumps(data)


response = urllib2.urlopen(request, data_json)

# 200
print response.getcode()

# http://127.0.0.1:8585/x/hello
print response.geturl()

# {"stat": "OK", "str_info": "meetbill"}
print response.read()
```

### 1.3 实例三：Cookie 的处理

```
# -* - coding: UTF-8 -* -
import urllib2
import urllib
import cookielib

cj = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))

request = urllib2.Request("https://dynamic.12306.cn/otsweb/")
request.add_header('content-TYPE', 'application/x-www-form-urlencoded')
data={"tbUserName":"test_username", "tbPassword":"test_password"}

response = opener.open(request, urllib.urlencode(data))

# send again, you will see cookie sent to web server
response = opener.open(request, urllib.urlencode(data))

print response.getcode()
print response.geturl()
print response.read()
```

## 2 urllib2 更多细节

相对于 urllib 模块，urllib2 模块提供了很多辅助的接口用于处理基本认证，重定向，cookies 等相关问题

### 2.1 urlopen

#### 构造方法

```
urllib2.urlopen(url[, data][, timeout])
```

url 参数可以是字符串也可以是 Request 对象

data 是一个标准 application/x-www-form-urlencoded 格式的缓存。urllib.urlencode() 使用一个词典或者二元组的序列参数以这种格式返回这样格式的字符串。urllib2 使用包含 Connection:close 头信息的 HTTP/1.1 来发送请求。

timeout 以秒为单位指定像访问链接阻塞操作的超时时间 （如果不指定这个时间，那么就使用所设置的全局默认超时时间）. 这个设置只用于 HTTP, HTTPS 和 FTP 连接

#### 实例方法

构造方法返回一个类文件对象，可用类似文件方法操作 read(),readline(),readlines(),fileno(),close()

```
geturl() 通常决定是否跟随者一个重定向
info() 返回的头部信息
getcode() 响应的状态码
```

### 2.2 Request

Request 类是一个抽象的 URL 请求

```
class urllib2.Request(url[, data][, headers][, origin_req_host][, unverifiable])
```

URL 链接地址，字符串

data 也是字符串，与 urlopen 中的 data 参数相同，Encode 需要通过 urllib 来完成

headers 字典类型，可以通过实例方法 add\_header() 方法添加键值对，headers = { 'User-Agent' : user\_agent }, 在使用服务器提供的 RESTful 或 SOAP 服务时， Content-Type 设置错误会导致服务器拒绝服务

有关 headers 的属性：

```
User-Agent :        有些服务器或 Proxy 会通过该值来判断是否是浏览器发出的请求
Content-Type :      在使用 REST 接口时，服务器会检查该值，用来确定 HTTP Body 中的内容该怎样解析。
application/xml ：  在 XML RPC，如 RESTful/SOAP 调用时使用
application/json ： 在 JSON RPC 调用时使用
application/x-www-form-urlencoded ： 浏览器提交 Web 表单时使用
origin_req_host 是 RFC2965 定义的源交互的 request-host。默认的取值是 cookielib.request_host(self)。这是由用户发起的原始请求的主机名或 IP 地址。例如，如果请求的是一个 HTML 文档中的图像，这应该是包含该图像的页面请求的 request-host。
```

unverifiable 代表请求是否是无法验证的，它也是由 RFC2965 定义的。默认值为 false。一个无法验证的请求是，其用户的 URL 没有足够的权限来被接受。例如，如果请求的是在 HTML 文档中的图像，但是用户没有自动抓取图像的权限，unverifiable 的值就应该是 true。

### 2.3 install\_opener/build\_opener

```
urllib2.install_opener(opener)
urllib2.build_opener([handler, ...])
```

可以看一下 the5fire 博文中对于 urllib2 库的分析

```
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)
```

在程序第一次执行 urlopen 操作的时候，其实就是构建了一个全局的\_opener 对象，然后用这个\_opener 对象来处理 url 以及 data。避免多次构建\_opener 对象，urllib2 库提供了 build\_opener 来创建\_opener 对象，install\_opener 来加载

\_opener 是 OpenerDirector 的一个实例，其中存放着用于处理 request，打开 request，处理 response 的句柄 handler(handler 也就是处理特种协议如 http，ftp 或者 cookie 等具体句柄，如 HTTPBasicAuthHandler、HTTPCookieProcessor、ProxyHandler 实例）

对于 build\_opener 函数，就是把所有的 handler 都实例化通过 opener.add\_handler 方法添加给 OpenerDirector。最后返回构建好的 OpenerDirector 实例。build\_opener 用来构建处理器（handlers），建造一些默认的或者通过参数传递进来的 handler 到 openerdirector 中待用。

build\_opener 默认会加入许多 handlers，它提供了一个快速的方法添加更多东西和使默认的 handler 失效。

```
import urllib2
opener = urllib2.build_opener()
opener.addheaders = [('User-agent', 'Mozilla/5.0')]
opener.open('http://www.example.com/')
```

install\_opener 如上所述也能用于创建一个 opener 对象，但是这个对象是（全局）默认的 opener。这意味着调用 urlopen 将会用到你刚创建的 opener。这段代码最终还是使用的默认 opener。一般情况下我们用 build\_opener 为的是生成自定义 opener，没有必要调用 install\_opener，除非是为了方便

```
import urllib2
req = urllib2.Request('http://www.python.org/')
opener=urllib2.build_opener()
urllib2.install_opener(opener)
f = urllib2.urlopen(req)
```

### 2.4 密码验证

HTTPBasicAuthHandler() 处理程序可用 add\_password() 来设置密码。

实例方法

```
add_password(realm,url,user,passwd)
```

realm 是与验证相关联的名称或描述信息，取决于远程服务器。uri 是基 URL。user 和 passwd 分别指定用户名和密码。

```
import urllib2
auth=urllib2.HTTPBasicAuthHandler()
auth.add_password('Administrator','http://www.example.com','Dave','123456')
opener=urllib2.build_opener(auth)
u=opener.open('http://www.example.com/evilplan.html')
```

### 2.5 Cookie

使用 HTTPCookieProcessor, 这样可以得到某个 cookie 项的值

```
import urllib2,cookielib
cookie=cookielib.CookieJar()
cookiehand=urllib2.HTTPCookieProcessor(cookie)
opener=urllib2.build_opener(cookiehand)
response = opener.open('http://www.google.com')
for item in cookie:
    if item.name == 'some_cookie_item_name':
        print item.value
```

### 2.6 异常处理

当不能处理一个 response 时，urlopen 抛出一个 URLError, HTTPError 是 HTTP URL 在特别的情况下被抛出的 URLError 的一个子类。

#### URLError

handlers 当运行出现问题时（通常是因为没有网络连接也就是没有路由到指定的服务器，或在指定的服务器不存在），抛出这个异常。

这个抛出的异常包括一个‘reason’ 属性，他包含一个错误编码和一个错误文字描述。

#### HTTPError

HTTPError 是 URLError 的子类。每个来自服务器 HTTP 的 response 都包含”status code”. 有时 status code 不能处理这个 request. 默认的处理程序将处理这些异常的 responses。例如，urllib2 发现 response 的 URL 与你请求的 URL 不同时也就是发生了重定向时，会自动处理。对于不能处理的请求，urlopen 将抛出 HTTPError 异常。典型的错误包含’404’ （没有找到页面）, ‘403’ （禁止请求）,’401’ （需要验证）等。它包含 2 个重要的属性 reason 和 code。

当一个错误被抛出的时候，服务器返回一个 HTTP 错误代码和一个错误页。你可以使用返回的 HTTP 错误示例。这意味着它不但具有 code 和 reason 属性，而且同时具有 read，geturl，和 info 等方法

如果我们想同时处理 HTTPError 和 URLError，因为 HTTPError 是 URLError 的子类，所以应该把捕获 HTTPError 放在 URLError 前面，如不然 URLError 也会捕获一个 HTTPError 错误，

```
import urllib2
req = urllib2.Request('http://www.python.org/fish.html')
try:
    response=urllib2.urlopen(req)
except urllib2.HTTPError,e:
    print 'The server couldn\'t fulfill the request.'
    print 'Error code: ',e.code
    print 'Error reason: ',e.reason
except urllib2.URLError,e:
    print 'We failed to reach a server.'
    print 'Reason: ', e.reason
else:
    # everything is fine
    response.read()
```

### 2.7 Timeout 设置

在老版 Python 中，urllib2 的 API 并没有暴露 Timeout 的设置，要设置 Timeout 值，只能更改 Socket 的全局 Timeout 值。

```
import urllib2
import socket

socket.setdefaulttimeout(10) # 10 秒钟后超时
urllib2.socket.setdefaulttimeout(10) # 另一种方式
```

在 Python 2.6 以后，超时可以通过 urllib2.urlopen() 的 timeout 参数直接设置。

```
import urllib2
response = urllib2.urlopen('http://www.google.com', timeout=10)
```

### 2.8 在 HTTP Request 中加入特定的 Header

要加入 header，需要使用 Request 对象：

```
import urllib2

request = urllib2.Request(uri)
request.add_header('User-Agent', 'fake-client')
response = urllib2.urlopen(request)
```

对有些 header 要特别留意，服务器会针对这些 header 做检查

> * User-Agent : 有些服务器或 Proxy 会通过该值来判断是否是浏览器发出的请求
> * Content-Type : 在使用 REST 接口时，服务器会检查该值，用来确定 HTTP Body 中的内容该怎样解析。常见的取值有：

```
application/xml ： 在 XML RPC，如 RESTful/SOAP 调用时使用
application/json ： 在 JSON RPC 调用时使用
application/x-www-form-urlencoded ： 浏览器提交 Web 表单时使用
```

在使用服务器提供的 RESTful 或 SOAP 服务时， Content-Type 设置错误会导致服务器拒绝服务

### 2.9 Redirect

urllib2 默认情况下会针对 HTTP 3XX 返回码自动进行 redirect 动作，无需人工配置。要检测是否发生了 redirect 动作，只要检查一下 Response 的 URL 和 Request 的 URL 是否一致就可以了。

```
import urllib2
response = urllib2.urlopen('http://www.google.cn')
redirected = response.geturl() == 'http://www.google.cn'
```

如果不想自动 redirect，除了使用更低层次的 httplib 库之外，还可以自定义 HTTPRedirectHandler 类。

```
import urllib2

class RedirectHandler(urllib2.HTTPRedirectHandler):
    def http_error_301(self, req, fp, code, msg, headers):
        pass
    def http_error_302(self, req, fp, code, msg, headers):
        pass

opener = urllib2.build_opener(RedirectHandler)
opener.open('http://www.google.cn')
```

### 2.10 Cookie

urllib2 对 Cookie 的处理也是自动的。如果需要得到某个 Cookie 项的值，可以这么做：

```
import urllib2
import cookielib

cookie = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie))
response = opener.open('http://www.google.com')
for item in cookie:
    if item.name == 'some_cookie_item_name':
        print item.value
```

### 2.11 使用 HTTP 的 PUT 和 DELETE 方法

urllib2 只支持 HTTP 的 GET 和 POST 方法，如果要使用 HTTP PUT 和 DELETE ，只能使用比较低层的 httplib 库。虽然如此，我们还是能通过下面的方式，使 urllib2 能够发出 PUT 或 DELETE 的请求：

```
import urllib2

request = urllib2.Request(uri, data=data)
request.get_method = lambda: 'PUT' # or 'DELETE'
response = urllib2.urlopen(request)
```

这种做法虽然属于 Hack 的方式，但实际使用起来也没什么问题。

### 2.12 Debug Log

使用 urllib2 时，可以通过下面的方法把 debug Log 打开，这样收发包的内容就会在屏幕上打印出来，方便调试，有时可以省去抓包的工作

```
import urllib2

httpHandler = urllib2.HTTPHandler(debuglevel=1)
httpsHandler = urllib2.HTTPSHandler(debuglevel=1)
opener = urllib2.build_opener(httpHandler, httpsHandler)

urllib2.install_opener(opener)
response = urllib2.urlopen('http://www.google.com')
```

## 3 封装为库

### 3.1 初版

```
import urllib
import urllib2
import json
import os

# HTTP library

# HTTP Delete wrapper
def httpDelete(url):
    opener = urllib2.build_opener(urllib2.HTTPHandler)
    request = urllib2.Request(url)
    request.get_method = lambda: 'DELETE'
    try:
        ret = opener.open(request)
        return ret

    except urllib2.HTTPError, err:
        if err.code == 404:
            print "Page not found!"
        elif err.code == 403:
            print "Access denied!"
        else:
            print "HTTP Error response! Error code", err.code
        return "Error"
    except urllib2.URLError, err:
        print "URL error:", err.reason
        return "Error"

# HTTP POST wrapper
def httpPost(url, data):
    try:
        retData = urllib2.urlopen(url, data)
        return retData.read()
    except urllib2.HTTPError, err:
        if err.code == 404:
            print "Page not found!"
        elif err.code == 403:
            print "Access denied!"
        else:
            print "HTTP Error! Error code", err.code
        return "Error"
    except urllib2.URLError, err:
        print "URL error:", err.reason
        return "Error"

# Wrapper for HTTP get
def httpGet(url):
    try:
        retData = urllib2.urlopen(url)
        return retData.read()

    except urllib2.HTTPError, err:
        if err.code == 404:
            print "Page not found!"
        elif err.code == 403:
            print "Access denied!"
        else:
            print "HTTP Error! Error code", err.code
        return "Error"
    except urllib2.URLError, err:
        print "URL error:", err.reason
        return "Error"
```

### 3.2 升级版

> xlib/util/http\_util

## 4 常见问题处理

### 4.1 urllib2.URLError: urlopen error \[Errno -3]

```
有可能是解析域名失败
```

## 5 其他库

> * [request](https://www.liaoxuefeng.com/wiki/1016959663602400/1183249464292448)
> * [pycurl](https://junyiseo.com/python/607.html)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://meetbill.gitbook.io/butterfly-project-doc/project-other/net-web/urllib2.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
