HTTP请求和Requests
HTTP请求方式:get和post
get和post是HTTP的两个常用方法。
什么是HTTP?
超文本传输协议(HyperText Transfer Protocol – HTTP)是一个设计来使客户端和服务器顺利进行通讯的协议。
HTTP在客户端和服务器之间以request-response protocol(请求-回复协议)工作。
- get - 从指定的服务器中获取数据
- post - 提交数据给指定的服务器处理
get请求
get是获取信息,而不是修改信息,类似数据库查询功能一样,数据不会被修改。
get请求的参数会跟在url后进行传递,请求的数据会附在URL之后,以?分割URL和传输数据,参数之间以&相连,%XX中的XX为该符号以16进制表示的ASCII,如果数据是英文字母/数字,原样发送,如果是空格,转换为+,如果是中文/其他字符,则直接把字符串用BASE64加密。
get传输的数据有大小限制,因为get是通过URL提交数据,那么get可提交的数据量就跟URL的长度有直接关系了,不同的浏览器对URL的长度的限制是不同的。
get请求的数据会被浏览器缓存起来,用户名和密码将明文出现在URL上,其他人可以查到历史浏览记录,数据不太安全。
在服务器端,用Request.QueryString来获取get方式提交来的数据
表单也可以通过get方法发送,下面是个例子:
GET /foo.php?first_name=John&last_name=Doe&action=Submit HTTP/1.1
你可以将表单输入通过附加进查询字符串的方式发送至服务器。
post请求
post请求发送数据至服务器,数据放置在HTML Header内提交,post没有限制提交的数据。
尽管你可以通过get方法将数据附加到url中传送给服务器,但在很多情况下使用post发送数据给服务器更加合适。通过get发送大量数据是不现实的,它有一定的局限性。
post比get安全,当数据是中文或者不敏感的数据,则用get,因为使用get,参数会显示在地址,对于敏感数据和不是中文字符的数据,则用post。
post可能修改服务器上的资源,在服务器端,用post方式提交的数据只能用Request.Form来获取。
可以把上面的例子改造成post请求的方式:
POST /foo.php HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost/test.php
Content-Type: application/x-www-form-urlencoded
Content-Length: 43
first_name=John&last_name=Doe&action=Submit
这里有三个需要注意的地方:
- 第一行的路径已经变为简单的 /foo.php , 已经没了查询字符串。
- 新增了 Content-Type 和 Content-Lenght 头部,它提供了发送信息的相关信息。
- 所有数据都在headers之后,以查询字符串的形式被发送。
requests库
Python的requests库提供了http所有的基本请求方式。例如
r = requests.post("http://httpbin.org/post")
r = requests.put("http://httpbin.org/put")
r = requests.delete("http://httpbin.org/delete")
r = requests.head("http://httpbin.org/get")
r = requests.options("http://httpbin.org/get")
get请求
r = requests.get("http://httpbin.org/get")
如果想要加参数,可以利用 params 参数:
import requests
payload = {'key1': 'value1', 'key2': 'value2'}
r = requests.get("http://httpbin.org/get", params=payload)
print r.url
运行结果:
http://httpbin.org/get?key2=value2&key1=value1
如果想添加 headers,可以传 headers 参数以增加请求头中的headers信息:
import requests
payload = {'key1': 'value1', 'key2': 'value2'}
headers = {'content-type': 'application/json'}
r = requests.get("http://httpbin.org/get", params=payload, headers=headers)
print r.url
post请求
post 请求一般需要一些参数。最基本的传参方法是利用 data 这个参数。
import requests
payload = {'key1': 'value1', 'key2': 'value2'}
r = requests.post("http://httpbin.org/post", data=payload)
print r.text
运行结果:
{
"args": {},
"data": "",
"files": {},
"form": {
"key1": "value1",
"key2": "value2"
},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "23",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.9.1"
},
"json": null,
"url": "http://httpbin.org/post"
}
上传文件
如果想要上传文件,那么直接用 file 参数即可,例如新建一个 a.txt 的文件,内容写上 Hello World!
import requests
url = 'http://httpbin.org/post'
files = {'file': open('test.txt', 'rb')}
r = requests.post(url, files=files)
print r.text
可以看到运行结果如下:
{
"args": {},
"data": "",
"files": {
"file": "Hello World!"
},
"form": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "156",
"Content-Type": "multipart/form-data; boundary=7d8eb5ff99a04c11bb3e862ce78d7000",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.9.1"
},
"json": null,
"url": "http://httpbin.org/post"
}
流式传输文件
requests 支持流式上传,这允许你发送大的数据流或文件而无需先把它们读入内存。要使用流式上传,仅需为你的请求体提供一个类文件对象即可:
with open('massive-body') as f:
requests.post('http://some.url/streamed', data=f)
cookies
Cookies是一种能够让网站服务器把少量数据储存到客户端的硬盘或内存,或是从客户端的硬盘读取数据的一种技术。Cookies是当你浏览某网站时,由Web服务器置于你硬盘上的一个非常小的文本文件,它可以记录你的用户ID、密码、浏览过的网页、停留的时间等信息。当你再次来到该网站时,网站通过读取Cookies,得知你的相关信息,就可以做出相应的动作,如在页面显示欢迎你的标语,或者让你不用输入ID、密码就直接登录等等。
如果一个响应中包含了cookie,那么我们可以利用 cookies 变量来拿到:
import requests
url = 'http://example.com'
r = requests.get(url)
print r.cookies
print r.cookies['example_cookie_name']
另外可以利用 cookies 变量来向服务器发送 cookies 信息:
import requests
url = 'http://httpbin.org/cookies'
cookies = dict(cookies_are='working')
r = requests.get(url, cookies=cookies)
print r.text
cookies和session的区别
Cookie和Session有很多相似的地方,都是用来临时存储来访者信息,两者的根本区别是Cookie对象将信息存放在客户端,Session对象存放在服务器端;从生存期上讲,Cookie可以长期保存,而Session的生存期仅仅到会话结束;
Cookie保存在客户端,用户可以看到Cookie文件,并能对Cookie文件进行类似修改、删除的操作,Cookie数据的安全性很难得到保障;而Session数据保存在服务器端,有较好的安全性,若和数据库配合使用,可以使Session数据长期保持,并得到很好的安全性。
timeout 请求超时
可以利用 timeout 变量来配置最大请求时间:
requests.get('http://github.com', timeout=0.001)
注:timeout 仅对连接过程有效,与响应体的下载无关。
会话对象
在以上的请求中,每次请求都相当于发起了一次新的请求,相当于我们对每个请求都用了不同的浏览器单独打开的效果。它并不是指的一个会话,即使请求的是同一个网址。比如:
import requests
requests.get('http://httpbin.org/cookies/set/sessioncookie/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/sessioncookie/123456789')
r = s.get("http://httpbin.org/cookies")
print(r.text)
在这里我们请求了两次,一次是设置 cookies,一次是获得 cookies,运行结果:
{
"cookies": {
"sessioncookie": "123456789"
}
}
发现可以成功获取到 cookies 了,这就是建立一个会话的作用。
既然会话是一个全局变量,那么我们可以用来全局的配置了。
import requests
s = requests.Session()
s.headers.update({'x-test': 'true'})
r = s.get('http://httpbin.org/headers', headers={'x-test2': 'true'})
print r.text
通过 s.headers.update 方法设置了 headers 的变量。然后我们又在请求中设置了一个 headers,那么会出现什么结果?
很简单,两个变量都传送过去了。
运行结果:
{
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.9.1",
"X-Test": "true",
"X-Test2": "true"
}
}
如果get方法传的headers 同样也是 x-test 呢?
r = s.get('http://httpbin.org/headers', headers={'x-test': 'true'})
它会覆盖掉全局的配置:
{
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.9.1",
"X-Test": "true"
}
}
如果不想要全局配置中的一个变量了呢?很简单,设置为 None 即可:
r = s.get('http://httpbin.org/headers', headers={'x-test': None})
运行结果:
{
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.9.1"
}
}
SSL证书验证
Requests可以为HTTPS请求验证SSL证书,就像web浏览器一样。要想检查某个主机的SSL证书,你可以使用 verify 参数:
import requests
r = requests.get('https://github.com', verify=True)
print r.text
如果我们想跳过证书验证,把 verify 设置为 False 即可:
import requests
r = requests.get('https://kyfw.12306.cn/otn/', verify=False)
print r.text
发现就可以正常请求了。在默认情况下 verify 是 True,所以如果需要的话,需要手动设置下这个变量。
代理
如果需要使用代理,你可以通过为任意请求方法提供 proxies 参数来配置单个请求:
import requests
proxies = { "https": "http://41.118.132.69:4433" }
r = requests.post("http://httpbin.org/post",proxies=proxies)
print r.text
响应内容
普通响应内容
我们能读取服务器响应的内容。以Github时间线为例:
import requests
r = requests.get('https://github.com/timeline.json')
r.text
>>>'[{"repository":{"open_issues":0,"url":"https://github.com/...
Requests会自动解码来自服务器的内容。大多数unicode字符集都能被无缝地解码。
请求发出后,Requests会基于HTTP头部对响应的编码作出有根据的推测。当你访问r.text 之时,Requests会使用其推测的文本编码。你可以找出Requests使用了什么编码,并且能够使用 r.encoding 属性来改变它:
r.encoding
>>>'utf-8'
#改变encoding属性
r.encoding = 'ISO-8859-1'
如果你改变了编码,每当你访问 r.text ,Request都将会使用 r.encoding 的新值。
在你需要的情况下,Requests也可以使用定制的编码。如果你创建了自己的编码,并使用codecs 模块进行注册,你就可以轻松地使用这个解码器名称作为 r.encoding 的值, 然后由Requests来为你处理编码。
二进制响应内容
你也能以字节的方式访问请求响应体,对于非文本请求:
>>> r.content
b'[{"repository":{"open_issues":0,"url":"https://github.com/...
Requests会自动为你解码 gzip 和 deflate 传输编码的响应数据。
例如,以请求返回的二进制数据创建一张图片,你可以使用如下代码:
from PIL import Image
from StringIO import StringIO
i = Image.open(StringIO(r.content))
JSON响应内容
Requests中也有一个内置的JSON解码器,助你处理JSON数据:
import requests
r = requests.get('https://github.com/timeline.json')
r.json()
>>>[{u'repository': {u'open_issues': 0, u'url': 'https://github.com/...
如果JSON解码失败, r.json 就会抛出一个异常。
原始响应内容
你可能想获取来自服务器的原始套接字响应,那么你可以访问 r.raw 。 如果你确实想这么干,那请你确保在初始请求中设置了 stream=True 。实例:
r = requests.get('https://github.com/timeline.json', stream=True)
r.raw
>>> <requests.packages.urllib3.response.HTTPResponse object at 0x101194810>
>>> r.raw.read(10)
'\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03'


浙公网安备 33010602011771号