爬虫:HTTP请求和requests库

来源:互联网 发布:二进制是谁发明的 知乎 编辑:程序博客网 时间:2024/04/19 22:50

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.1Host: localhostUser-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.8Accept-Language: en-us,en;q=0.5Accept-Encoding: gzip,deflateAccept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7Keep-Alive: 300Connection: keep-aliveReferer: http://localhost/test.phpContent-Type: application/x-www-form-urlencodedContent-Length: 43first_name=John&last_name=Doe&action=Submit

这里有三个需要注意的地方:

  1. 第一行的路径已经变为简单的 /foo.php , 已经没了查询字符串。
  2. 新增了 Content-Type 和 Content-Lenght 头部,它提供了发送信息的相关信息。
  3. 所有数据都在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 requestspayload = {'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 requestspayload = {'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 requestspayload = {'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 requestsurl = '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 requestsurl = 'http://example.com'r = requests.get(url)print r.cookiesprint r.cookies['example_cookie_name']

另外可以利用 cookies 变量来向服务器发送 cookies 信息:

import requestsurl = '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 requestsrequests.get('http://httpbin.org/cookies/set/sessioncookie/123456789')r = requests.get("http://httpbin.org/cookies")print(r.text)

结果是:

{  "cookies": {}}

很明显,这不在一个会话中,无法获取 cookies,那么在一些站点中,我们需要保持一个持久的会话怎么办呢?就像用一个浏览器逛淘宝一样,在不同的选项卡之间跳转,这样其实就是建立了一个长久会话。方法如下:

import requestss = 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 requestss = 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 requestsr = requests.get('https://github.com', verify=True)print r.text

如果我们想跳过证书验证,把 verify 设置为 False 即可:

import requestsr = requests.get('https://kyfw.12306.cn/otn/', verify=False)print r.text

发现就可以正常请求了。在默认情况下 verify 是 True,所以如果需要的话,需要手动设置下这个变量。

代理

如果需要使用代理,你可以通过为任意请求方法提供 proxies 参数来配置单个请求:

import requestsproxies = { "https": "http://41.118.132.69:4433" }r = requests.post("http://httpbin.org/post",proxies=proxies)print r.text

响应内容

普通响应内容

我们能读取服务器响应的内容。以Github时间线为例:

import requestsr = 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.contentb'[{"repository":{"open_issues":0,"url":"https://github.com/...

Requests会自动为你解码 gzip 和 deflate 传输编码的响应数据。
例如,以请求返回的二进制数据创建一张图片,你可以使用如下代码:

from PIL import Imagefrom StringIO import StringIOi = Image.open(StringIO(r.content))

JSON响应内容

Requests中也有一个内置的JSON解码器,助你处理JSON数据:

import requestsr = 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'
0 0