之前用 Python 写的一个爬虫,现在用 Emacs 打开,提示找不到 urllib.urlopen 这个函数。调试了半天,就是提示找不到这个函数,但是这个爬虫却能正确执行。于是我打开Dash查看其 API,发现 API 中有这个函数,但是却不是使用 urllib.urlopen 来调用。而是变成了 urllib.request.urlopen ,仔细一看当前查看的是 Python3 的版本,看来是 Python 版本的锅。

urllib 简单介绍

urllib 是 Python3 中的标准网络请求库,使用 urllib 来获取网页的内容非常方便。在 Python2.x 版本中有 urllib 和 urllib2,但是到了 Python3 中 urllib 和 urllib2 被合并成了 urllib,所以之前在 Python2 中的写法就不能再出现在 Python3 中了。Python3 是趋势,所以折腾吧,少年。

先来看一下urllib 文档,从文档中可以看出 urllib 中主要有 request、error、parse、robotparser 四个模块,还是比较简单的,我们只介绍 request 其它的就不做介绍。

简单使用

1
2
3
4
from urllib import request

res = request.urlopen('http://www.baidu.com')
print(res.read().decode('utf-8'))

使用 urlopen 可以非常方便的以 GET 方式请求网页。运行代码就能看到百度首页的代码了。

添加请求参数

既然用 urlopen 可以非常方便的请求网页,如果要携带一些参数怎么办? 使用 GET 方式添加请求参数只需要在 url 后面拼接就可以,但是不要忘记对参数进行编码。 在 Python2 中 GET 参数使用的是 urllib.urlencode ,到了 Python3 这个函数被放到了 urllib 的 parse 中。

1
2
3
4
5
6
7
8
from urllib import request
from urllib import parse
q = {
    'wd':'python'
}
url = 'http://www.baidu.com/s?'+parse.urlencode(q)
res = request.urlopen(url)
print(res.read().decode('utf-8'))

提交数据

常用的 http 请求方式除了 GET 外还有 POSTPOST 通常用来提交数据给服务器。urllib 也提供了非常方便的方式来提交数据。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import urilib.parse
import urllib.request

url = "http://127.0.0.1:8080"
form = {
    'username':'jake'
}
data = bytes(parse.urlencode(form), encoding='utf8')
res = request.urlopen(url, data=data)
print(res.read().decode('utf-8'))

进阶

urlopen 除了可以直接添加 url 外,还可以使用 Request 来定制请求。

Request 的使用

1
2
3
4
import urllib.request
req = request.Request('http://www.baidu.com')
res = request.urlopen(req)
print(res.read().decode('utf-8'))

上面的例子和使用 urlopen 是一样的效果。不同地方在于 request 可以进行定制。比如:请求头。

添加请求头

1
2
3
4
5
6
7
from urllib import request

headers={'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.75 Safari/537.36'}
req = request.Request('http://www.baidu.com',headers = headers)

res = request.urlopen(req)
print(res.read().decode('utf-8'))

下面这种方式也是可以的

1
2
3
4
5
6
7
from urllib import request

req = request.Request('http://www.baidu.com')
req.add_header('User-Agent','Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.75 Safari/537.36')

res = request.urlopen(req)
print(res.read().decode('utf-8'))

有些操作需要验证,如果每次使用都要验证,那就太麻烦了。所以我们需要把 cookie 保存下来,不要每次都去验证。当然把 cookie 放在头部也是可以的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from urllib import request
import http.cookiejar

cookie = http.cookiejar.CookieJar()
handler = request.HTTPCookieProcessor(cookie)
opener = request.build_opener(handler)
res = opener.open('http://www.baidu.com')
print(res.read().decode('utf-8'))

for item in cookie:
    print(item.name,':',item.value)

下载文件

有时候碰到一些资源想保存到本地,可以使用`urlretrieve`来把对应的资源下载到本地,也是很方便的。

1
2
3
from urllib import request

request.urlretrieve('http://www.baidu.com/img/bd_logo1.png','logo.png')

urlretrieve 第一个是资源的 url ,第二个是保存在本地的路径和文件名,如果没有填写第二个参数则保存为临时文件。

设置代理

临时代理

在一些特殊的情况下你想访问某个网站,但是你访问不到,另外一台主机可以。所以这台主机可以作为一台代理电脑,由它去访问我们访问不到的网站。通常爬虫被封了,就需要用到代理。

1
2
3
4
5
6
7
8
9
from urllib.request import ProxyHandler,build_opener

handler = ProxyHandler({
    'http':'填上代理地址'
})

opener = build_opener(handler)
res = opener.open('http://www.baidu.com')
print(res.read())

代码还是很少的,只要用代理创建一个 opener ,然后使用 opener 就可以访问到网站了。 除了使用 ProxyHandler 之外还有 HTTPHandler 等,可以根据自己的需要选择需要的使用。

全局代理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from urllib.request import ProxyHandler,build_opener,Request
import urllib

handler = ProxyHandler({
    'http':'代理地址'
})

opener = build_opener(handler)
urllib.request.install_opener(opener)
res = urllib.request.urlopen("http://www.baidu.com")
print(res.read())
全局代理使用 `urllib.request.install_opener(opener)` 把代理注册到全局,这样就能让所有的请求都通过代理。

如果请求每次都要使用代理,那么使用全局代理会省事不少,不必每次都创建代理。