requests
在 urllib 的应用中,有一些不方便的地方,比如处理网页验证和 Cookies 时,需要写 Opener 和 Handler 来处理。为了更加方便地实现这些操作,需要用到更强大的库:requests。
1. 基本用法
1.1. 简单示例
urllib 库中的 urlopen() 方法以 GET 方式请求网页,而 requests 中相应的就是 get() 方法。
import requests
r = requests.get('https://www.baidu.com/')
print(type(r))
# <class 'requests.models.Response'>
print(r.status_code)
# 200
# 响应体的类型
print(type(r.text))
# <class 'str'>
# 响应体的内容
print(r.text)
# Cookies的类型
print(r.cookies)
# <RequestsCookieJar[<Cookie BDORZ=27315 for .baidu.com/>]>
1.2. GET请求
HTTP中最常见的请求之一就是GET请求。构建一个最简单的GET请求,链接为 http://httpbin.org/get,该网站会判断请求方式,如果是GET请求的话就返回相应的请求信息。
1.2.1. 基本实例
import requests
r = requests.get('http://httpbin.org/get')
print(r.text)
"""
运行结果:
{
"args": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Connection": "close",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.20.1"
},
"origin": "123.160.224.118",
"url": "http://httpbin.org/get"
}
"""
如果要在GET请求中附加额外的信息,一般用字典来存储:
import requests
data = {
'name': 'germey',
'age': 22
}
r = requests.get('http://httpbin.org/get', params=data)
print(r.text)
"""
{
"args": {
"age": "22",
"name": "germey"
},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Connection": "close",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.20.1"
},
"origin": "123.160.224.118",
"url": "http://httpbin.org/get?name=germey&age=22"
}
"""
网页的返回类型实际上是 str 类型,但是它很特殊,是JSON格式的。如果想直接解析返回结果,得到一个字典格式的话,可以直接调用 json() 方法。
import requests
r = requests.get('http://httpbin.org/get')
print(type(r.text))
print(r.json())
print(type(r.json()))
"""
运行结果:
<class 'str'>
{'args': {}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'close', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.20.1'}, 'origin': '123.160.224.118', 'url': 'http://httpbin.org/get'}
<class 'dict'>
"""
调用 json() 方法可以将返回结果是JSON格式的字符串转化为字典,但是在返回结果不是JSON格式时会出现解析错误,抛出 json.decoder.JSONDecoderError 异常。
1.2.2. 抓取网页
以“知乎”的“发现”页面为例:
import requests
import re
headers = {
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36\
(KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36'
}
r = requests.get('https://www.zhihu.com/explore', headers=headers)
pattern = re.compile('explore-feed.*?question_link.*?>(.*?)</a>', re.S)
titles = re.findall(pattern, r.text)
print(titles)
"""
运行结果:
['\n历史上有那些长期流传的,错误的地理记载,后来是如何被推翻的?\n',
'\n人生应该活成什么样子,该以什么样的方式活着?\n',
'\nfgo里面的从者和礼装卡面有哪些不仔细看看不出来的小彩蛋?\n',
'\n如何看待新浪娱乐的对话特辑《听老高鹿晗说》?\n',
'\n如何评价《群星(Stellaris)》新DLC《巨型企业(Megacorp)》?\n',
'\n有哪些句子是真正写到你的心里去了?\n',
'\n经过此次马思纯回怼风波后,杨紫后续的资源是否更差?\n',
'\n你有过哪些像段子的亲身经历?\n',
'\n如何看待网传朱一龙抽烟并随地扔烟头?\n',
'\n怎么评价防弹续约7年?\n']
"""
User-Agent 字段信息是浏览器标识信息,不加会被禁止抓取。
1.2.3. 抓取二进制数据
以 GitHub 的站点图标为例:
import requests
r = requests.get('https://github.com/favicon.ico')
print(r.text)
print(r.content)
结果如下:
r.text 部分结果:�������t������������������������������
r.content 部分结果:
b'\x00\x00\x01\x00\x02\x00\x10\x10\x00\x00\x01\x00
图片是二进制数据,前者在打印时转化为 str 类型,图片直接转化为字符串,会出现乱码
将提取的图片保存下来
import requests
r = requests.get('https://github.com/favicon.ico')
with open('favicon.ico', 'wb') as f:
f.write(r.content)
使用 open() 方法,第一个参数是文件名称,第二个参数代表以二进制形式打开,可以向文件中写入二进制数据。运行结束会在文佳家中产生名为 favicon.ico 的图片文件。
同样,音频和视频文件也可以用这种方法获取。
1.2.4. 添加 headers
与 urllib.request 一样,requests 也通过 headers 来传递头消息。例如前面的“知乎”例子中,如果不传递 headers,就不能正常请求:
import requests
r = requests.get('https://www.zhihu.com/explore')
print(r.text)
"""
运行结果:
<html>
<head><title>400 Bad Request</title></head>
<body bgcolor="white">
<center><h1>400 Bad Request</h1></center>
<hr><center>openresty</center>
</body>
</html>
"""
1.3. POST请求
请求 http://httpbin.org/post,该网站判断是POST请求后把相关信息返回,其中 form 部分就是提交的数据,证明POST请求成功。
import requests
data = {'name': 'germey', 'age': '22'}
r = requests.post('http://httpbin.org/post', data=data)
print(r.text)
"""
{
"args": {},
"data": "",
"files": {},
"form": {
"age": "22",
"name": "germey"
},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Connection": "close",
"Content-Length": "18",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.20.1"
},
"json": null,
"origin": "123.160.224.118",
"url": "http://httpbin.org/post"
}
"""
1.4. 响应
发送请求后,可以使用 text 和 content 得到的响应内容,此外还有很多属性和方法可以用来获取其他信息,如状态码、响应头、Cookies 等。
import requests
r = requests.get('http://www.jianshu.com')
# 输出状态码
print(type(r.status_code), r.status_code)
# 输出响应头
print(type(r.headers), r.headers)
# 输出Cookies
print(type(r.cookies), r.cookies)
# 输出URL
print(type(r.url), r.url)
# 输出请求历史
print(type(r.history), r.history)
<class 'int'> 403
<class 'requests.structures.CaseInsensitiveDict'> {'Date': 'Sat, 24 Nov 2018 09:26:34 GMT', 'Content-Type': 'text/html', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', 'Server': 'Tengine', 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload', 'Content-Encoding': 'gzip', 'X-Via': '1.1 PSzjwzdx11at80:10 (Cdn Cache Server V2.0), 1.1 PShbycdx6yh51:11 (Cdn Cache Server V2.0)'}
<class 'requests.cookies.RequestsCookieJar'> <RequestsCookieJar[]>
<class 'str'> https://www.jianshu.com/
<class 'list'> []
headers 和 cookies 两个属性得到的结果分别是CaseInsensitiveDict 和 RequestsCookieJar 类型
状态码常用来判断请求是否成功,而 requests 还提供了一个内置的状态码查询对象 requests.Codes,示例如下:
import requests
r = requests.get('http://www.jianshu.com')
exit() if not r.status_code == requests.codes.ok else print('Request Successfully')
通过比较返回码和内置的成功的返回码,来保证请求得到了正常响应,输出成功请求的消息,否则程序终止,用 requests.codes.ok 得到的成功的状态码是 200。
一些返回码和相应的查询条件:
如果想判断结果是不是 404 状态,可以用 requests.codes.not_foend 来对比。
2. 高级用法
2.1. 文件上传
import requests
files = {'file': open('favicon.ico', 'rb')}
r = requests.post('http://httpbin.org/post', files=files)
print(r.text)
上传的部分会在一个单独的 files 字段里标识出来。
2.2. Cookies
import requests
r = requests.get('https://www.baidu.com')
print(r.cookies)
for key, value in r.cookies.items():
print(key + '=' + value)
可以直接用 Cookies 维持登录状态,登录后将 Headers 中的 Cookie 内容复制下来,替换成自己的 Cookie, 将其设置到 Headers 里,然后发送请求。
2.3. 会话维持
import requests
requests.get('http://httpbin.org/cookies/set/number/123456789')
r = requests.get('http://httpbin.org/cookies')
print(r.text)
运行结果为:
{
"cookies": {}
}
不能获取到设置的 Cookies。
import requests
s = requests.Session()
s.get('http://httpbin.org/cookies/set/number/123456789')
r = s.get('http://httpbin.org/cookies')
print(r.text)
运行结果为:
{
"cookies": {
"number": "123456789"
}
}
Yes,获取成功!
2.4. SSL 证书验证
requests 还提供了证书验证的功能。当发送 HTTP 请求的时候,它会检查 SSL 证书,我们可以使用 verify 参数控制是否检查此证书.其实如果不加 verify 参数的话,默认是 True ,会自动验证。
import requests
response = requests.get('https://www.12306.cn')
print(response.status_code)
"""
requests.exceptions.SSLError: ("bad handshske: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')],)",)
"""
这里提示一个错误 SSLError ,表示证书验证错误。所以,如果请求一个 HTTPS 站点,但是证书验证错误的页面时,就会报这样的错误,那么如何避免这个错误呢?很简单,把 verify 参数设置为 False 即可,相关代码如下:
import requests
response = requests.get('https://www.12306.cn', verify=False)
print(response.status_code)
也可以通过指定一个本地证书用作客户端证书,可以是单个文件(包含秘钥和证书)或一个包含两个文件路径的元组:
import requests
response = requests.get('https://www.12306.cn', cert=('path/server.crt', 'path/key'))
print(response.status_code)
代码是演示示例,需要的有 crt 和 key 文件,并且指定它们的路径。注意:本地私有证书的 key 必须是解密状态,加密状态的 key 是不支持的。
2.5. 代理设置
对于某些网站,在测试的时候请求几次,能正常获取内容。但是一旦开始大规模爬取,对于大规模且频繁的请求,网站可能会弹出验证码,或者跳转到登录认证页面 更甚者可能会直接封禁客户端的 IP,导致一定时间段内无法访问。
那么,为了防止这种情况发生,需要设置代理来解决这个问题,这就需要用到 proxies 参数,可以用这样的方式设置:
import requests
proxies = {
"http": "http://10.10.1.10:3128",
"https": "https://10.10.1.10:1080"
}
requests.get("https://www.taobao.com", proxies=proxies)
# 如果代理需要使用 HTTP Basic Auth ,可以使用类似 http://user:password@host:port 这样的语法来设置代理
# requests.get("https://www.taobao.com", proxies=proxies)
2.6. 超时设置
在本机网络状况不好或者服务器网络响应太慢甚至无响应时,我们可能会等待特别久的时间才能收到响应,甚至到最后收不到响应而报错。为了防止服务器不能及时响应,应该设置一个超时时间,即超过了这个时间还没有得到响应,那就报错,这需要用到 timeout 参数。这个时间的计算是发出请求到服务器返回响应的时间。示例如下:
import requests
r = requests.get("https://www.taobao.com", timeout=1)
print(r.status_code)
超时时间设置为1秒,1秒内没有响应就抛出异常。实际上请求分为两个阶段:连接(connect)和读取(read),上面设置的 timeout 是两者用时的总和。
其他设置方式:
如果需要分开指定,可以传入一个元组:
r = requests.get('https://www.taobao.com', timeout=(5, 11, 30))
如果想永久等待,timeout 默认值为 None ,可以直接设置为 None:
r = requests.get("https://www.taobao.com", timeout=None)
也可以直接不加参数:
r = requests.get("https://www.taobao.com")
2.7. 身份认证
requests 自带的身份认证功能:
import requests
from requests.auth import HTTPBasicAuth
r = requests.get('http://localhost:5000', auth=HTTPBasicAuth('username', 'password'))
print(r.status_code)
如果用户名和密码正确,请求时会自动认证成功,返回 200 状态码;如果认证失败,则返回 401 状态码。
还有一种简单的写法,不用传 HTTPBasicAuth 类参数,直接传一个元组,会默认使用 HTTPBasicAuth 这个类来认证:
import requests
r = requests.get('http://localhost:5000', auth=('username', 'password'))
print(r.status_code)
requests 还提供了其他认证方式,如 OAuth 认证,不过需要安装 oauth 包,安装命令如下:
pip install requests.oauthlib
import requests
from requests_oauthlib import OAuth1
url = 'https://api.twitter.com/1.1/account/verify_credentials.json'
auth = OAuth1('YOUR_APP_KEY', 'YOUR_APP_SECRET', 'USER_OAUTH_TOKEN', 'USER_OAUTH_TOKEN_SECRET')
requests.get(url, auth=auth)
更多详细功能请参考 requests_oauthlib 官方文档 https://requests-oauthlib.readthedocs.org/