Python3标准库urllib
Python3标准库urllib
前言
做web开发的,和http请求打交道最多了,不得不熟悉的就是urllib。当然爬虫也经常用。并且有好的第三方库requests。
本文就介绍这些东东。
note:是在python3.5下测试运行。
urllib
urllib有4个库。分别是:
- urllib.request 打开和读url
- urllib.error 包含由urllib.request抛出的异常。
- urllib.parse 用来解析url
- urllib.robotparser 用来解析robots.txt文件。
玩爬虫的同学请关注一下urllib.robotparser,做一个好程序员。-^
request
两行代码拿到页面内容。
get请求
from urllib.request import urlopen
print(urlopen("http://www.baidu.com").read())
urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)
支持http、https、ftp协议。
urlopen返回 一个类文件对象,他提供了如下方法:
- read() , readline() , readlines() , fileno() , close() :这些方法的使用方式与文件对象完全一样;
- info():返回一个httplib.HTTPMessage 对象,表示远程服务器返回的头信息;
- getcode():返回Http状态码。如果是http请求,200表示请求成功完成;404表示网址未找到;
- geturl():返回请求的url;
参数说明:
- url 可以是字符串,也可以是Request对象.
- data 用于post请求时的数据。
- timeout 超时时间
- cafile & capath & cadefault 用于https的ca证书
- context 同样是用于https请法庭。
需要注意的是这个方法使用的HTTP/1.1协议,并且在HTTP header里自动加入了Connection:close。
post请求
同样可以用urllib.request.urlopen实现。传入data即可。
第二种方式是传入的url是个Request对象。Request的构造函数中传入data即可。
如果没有数据传输,只能用第二第方式,只是Request对象的函数函数中需要明确指定method=’POST’。
完整的Request类定义如下:
urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)
参数说明:
- url 字符串的网址
- data 用于post的数据。如果有,则method自动为’POST’
- headers http头。
- origin_req_host 原始请求host。
- unverifiable 用于指示请求是否是不可证实的。
- method http方法
示例如下:
from urllib import request
from urllib import parse
#从这可以看出,前端的校验是给小白用的.码农是可以直接发起http请求,绕过前端js校验的.
data = {'name':'test1','email':'test@qq.com','passwd':'123456'}
response = request.urlopen('http://awesome.go2live.cn/api/users',parse.urlencode(data).encode('utf-8'))
print(response.getcode())
print(response.read())
自定义header
前文已经说了。在构造Request对象时,传入headers参数即可。也可以之后调用Request的add_header方法。
这个主要用来模拟User-Agent和HTTP_REFERER。因为这两个经常用来在后端屏蔽掉请求。
譬如我的博客站点就是有屏蔽功能的。
直接请求会失败。
from urllib.request import urlopen
print(urlopen("http://www.go2live.cn").read())
输出结果:
Traceback (most recent call last):
…
urllib.error.HTTPError: HTTP Error 403: Forbidden
通过构造合适的user-agent就可以正常访问了。
from urllib import request
req = request.Request('http://www.go2live.cn',headers={'User-Agent':'Mozilla/5.0 (X11; U; Linux i686) Gecko/20071127 Firefox/2.0.0.11'})
response = request.urlopen(req)
print(response.getcode())
输出结果:
200
还有Referer是用来防盗链的。请求某些资源时,Referer必须是来自指定的网站才可以,否则403拒绝访问。
另一个是Content-Type。
在post请求,自动成了application/x-www-form-urlencoded。
但是在api请求时,现在好多都改成了json的。这个时候需要用application/json。
另外上传文件时也不一样:multipart/form-data。
增加cookie
这个主要用来处理需要登录才能访问的页面。
这个稍为麻烦点。修复修改下opener才能处理。
http.cookiejiar包是来保存cookie的。
request.HTTPCookieProcessor是用来处理cookie的。
完整的代码示例如下:
from urllib import request
from urllib import parse
import http.cookiejar
import hashlib
URL_ROOT = 'http://awesome.go2live.cn'
cookie = http.cookiejar.CookieJar()# 用来保存cookie的对象
handler = request.HTTPCookieProcessor(cookie) #处理cookie的工具
opener = request.build_opener((handler))
response = opener.open(URL_ROOT)
print("before login")
for item in cookie:
print('Name={0}, Value={1}'.format(item.name, item.value))
data = {'email':'test@qq.com','passwd':'123456'}
data['passwd'] = data['email']+":"+data['passwd']
data['passwd'] = hashlib.sha1(data['passwd'].encode()).hexdigest()
req = request.Request(URL_ROOT+'/api/authenticate', parse.urlencode(data).encode())
response = opener.open(req)
print(response.read())
print("after login")
for item in cookie:
print('Name={0}, Value={1}'.format(item.name, item.value))
输出结果:
before login
b'{“admin”: 0, “created_at”: 1486789465.7326, “id”: “001486789465697a2db999d99f84506a24a457bee0eab76000”, “name”: “test”, “email”: “test@qq.com“, “passwd”: “******”, “image”: “http://www.gravatar.com/avatar/bf58432148b643a8b4c41c3901b81d1b?d=mm&s=120″}‘
after login
Name=awesession, Value=001486789465697a2db999d99f84506a24a457bee0eab76000-1486887360-220a1b13969736bb0f868cfef9076023a7ea3b02
上传文件
用还是urlopen方法。只是data的构造真的好蛋疼。。
#!/usr/bin/env python
urllib.request
import urllib.parse
import random, string
import mimetypes
def random_string (length):
return ''.join (random.choice (string.ascii_letters) for ii in range (length + 1))
def encode_multipart_data (data, files):
def get_content_type (filename):
return mimetypes.guess_type (filename)[0] or 'application/octet-stream'
def encode_field (field_name):
return ('-----------------------------96951961826872/r/n',
'Content-Disposition: form-data; name="%s"' % field_name, '/r/n'
'', str (data [field_name]))
def encode_file (field_name):
filename = files [field_name]
return ('-----------------------------96951961826872/r/n',
'Content-Disposition: form-data; name="%s"; filename="%s"' % (field_name, filename), '/r/n'
'Content-Type: %s' % get_content_type(filename), '/r/n/r/n'
'', open (filename, 'rb').read ())
lines = []
for name in data:
lines.extend (encode_field (name))
for name in files:
lines.extend (encode_file (name))
lines.extend ('/r/n-----------------------------96951961826872--/r/n')
body = b''
for x in lines:
if(type(x) == str):
body += x.encode('ascii')
else:
body += x
headers = {'Content-Type': 'multipart/form-data; boundary=---------------------------96951961826872',
'Content-Length': str (len (body))}
return body, headers
def main():
url = 'http://awesome.go2live.cn'
data = {}
files = {'notePhoto': '01.jpg'}
req = urllib.request.Request (url, *encode_multipart_data (data, files))
response = urllib.request.urlopen(req)
if __name__ == '__main__':
main()
下载文件
这个其实没啥。
from urllib import request
response = request.urlopen('http://stock.gtimg.cn/data/get_hs_xls.php?id=ranka&type=1&metric=chr')
with open('test1.xls', 'wb') as fd:
fd.write(response.read())
Debug调试
这个有时候要看看具体的http请求是啥样的。
使用下面的方式会把收发包的内容在屏幕上打印出来。
from urllib import request
http_handler = request.HTTPHandler(debuglevel=1)
https_handler = request.HTTPSHandler(debuglevel=1)
opener = request.build_opener(http_handler,https_handler)
request.install_opener(opener)
response = request.urlopen('http://m.baidu.com')
print(response.read())
输出结果:
send: b’GET http://m.baidu.com HTTP/1.1\r\nAccept-Encoding…
reply: ‘HTTP/1.1 200 OK\r\n’
header: Cache-Control header: Content-Length header: Content-Type…
内容太长。打…省略。
requests库
相当强大的一个第三方库。支持以下特性:
- International Domains and URLs
- Keep-Alive & Connection Pooling
- Sessions with Cookie Persistence
- Browser-style SSL Verification
- Basic/Digest Authentication
- Elegant Key/Value Cookies
- Automatic Decompression
- Automatic Content Decoding
- Unicode Response Bodies
- Multipart File Uploads
- HTTP(S) Proxy Support
- Connection Timeouts
- Streaming Downloads
- .netrc Support
- Chunked Requests
- Thread-safety
get请求
同样简单的很,两行代码即可。
import requests
print(requests.get('http://blog.go2live.cn').content)
post请求
requests的api好简单。get请求就调用get方法。post请求就调用post方法。
import requests
print(requests.post('http://awesome.go2live.cn/api/users',data={'name':'test1','email':'test@qq.com','passwd':'123456'}).content)
两行代码搞定。对比下urllib用了4行代码简单多了。。
而要传入json数据。再加个json参数就ok了。
自定义header
这个好简单,直接添加一个参数就可以了。
>>> url = 'https://api.github.com/some/endpoint'
>>> headers = {'user-agent': 'my-app/0.0.1'}
>>> r = requests.get(url, headers=headers)
处理cookie
cookie可以直接用响应对象的cookies变量拿到。
>>> url = 'http://example.com/some/cookie/setting/url'
>>> r = requests.get(url)
>>> r.cookies['example_cookie_name']
'example_cookie_value'
发送cookie可以加个cookies字典。
>>> url = 'http://httpbin.org/cookies'
>>> cookies = dict(cookies_are='working')
>>> r = requests.get(url, cookies=cookies)
>>> r.text
'{"cookies": {"cookies_are": "working"}}'
RequestsCookieJar可以设置得更复杂。
>>> jar = requests.cookies.RequestsCookieJar()
>>> jar.set('tasty_cookie', 'yum', domain='httpbin.org', path='/cookies')
>>> jar.set('gross_cookie', 'blech', domain='httpbin.org', path='/elsewhere')
>>> url = 'http://httpbin.org/cookies'
>>> r = requests.get(url, cookies=jar)
>>> r.text
'{"cookies": {"tasty_cookie": "yum"}}'
上传文件
上传文件也简单。用post方法,传个files参数。
>>> url = 'http://httpbin.org/post'
>>> files = {'file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel', {'Expires': '0'})}
>>> r = requests.post(url, files=files)
>>> r.text
{
...
"files": {
"file": "<censored...binary...data>"
},
...
}
下载文件
感觉好简单。见下面。
import requests
url = 'http://stock.gtimg.cn/data/get_hs_xls.php?id=ranka&type=1&metric=chr'
r = requests.get(url)
with open('test.xls', "wb") as code:
code.write(r.content)
考虑到一次下载,可能文件过大,会消耗过多的内存。
requests推荐的下载方式如下:
import requests
r = requests.get('http://stock.gtimg.cn/data/get_hs_xls.php?id=ranka&type=1&metric=chr',stream=True)
with open('test.xls', 'wb') as fd:
for chunk in r.iter_content(chunk_size=128):
fd.write(chunk)
写在后面
对比了urllib和requests库。我决定以后都用requests库了。太方便了。
给个git地址,去fork下吧。