Python爬虫系列(三):requests高级耍法

昨天,我们更多的讨论了request的基础API,让我们对它有了基础的认知。学会上一课程,我们已经能写点基本的爬虫了。但是还不够,因为,很多站点是需要登录的,在站点的各个请求之间,是需要保持回话状态的,有的站点还需要证书验证,等等这一系列的问题,我们将在今天这一环节,加以讨论。

1.会话对象

会话:session,就是你点进这个站点后,由浏览器与服务器之间保持的一次连接。这次连接里面,你跳转页面,或发起其他请求,服务器要求某些数据验证。服务器不会叫你在每次跳转时候进行验证,而是用已验证的结果进行跳转。这样就节省服务器资源,底层的TCP连接也会被重用。

跨请求保持数据(客户端存数据):

s = requests.Session()

s.get('http://httpbin.org/cookies/set/sessioncookie/123456789')

r = s.get("http://httpbin.org/cookies")

print(r.text)

回话提供请求默认数据(数据会被存到服务端):

s = requests.Session()

s.auth = ('user', 'pass')

s.headers.update({'x-test': 'true'})

# 'x-test' 和 'x-test2' 一起发送给url

s.get('http://httpbin.org/headers', headers={'x-test2': 'true'})

注意:即便使用了session,方法级别的参数,仍然不会再跨请求保持。

以下代码,另个请求分别有自己的cookies

s = requests.Session()

r = s.get('http://httpbin.org/cookies')

print(r.text)

# '{"cookies": {}}'

r = s.get('http://httpbin.org/cookies', cookies={'from-my': 'browser'})

print(r.text)

# '{"cookies": {"from-my": "browser"}}'

会话上下文管理器:是指用with 块限定会话对象的使用范围

with requests.Session() as s:

s.get('http://httpbin.org/cookies/set/sessioncookie/123456789')

2.请求与响应对象

任何时候,我们往服务器发消息,都会返回一个response的响应对象,同时,还能获得我们自己创建的request对象

r = requests.get('http://en.wikipedia.org/wiki/Monty_Python')

print (r.headers)

print (r.request.headers)

在发送请求之前,需要对body获header做一些额外处理,使用如下方法:

from requests import Request, Session

s = Session()

req = Request('GET', url,

data=data,

headers=header

)

#要想获取有状态的请求,使用:

prepped = s.prepare_request(req)

而不是使用:

prepped = req.prepare()

# do something with prepped.body

# do something with prepped.headers

resp = s.send(prepped,

stream=stream,

verify=verify,

proxies=proxies,

cert=cert,

timeout=timeout

)

print(resp.status_code)

3.SSL 证书验证

requests.get('https://requestb.in')

requests.get('https://github.com', verify=True)

requests.get('https://github.com', verify='/path/to/certfile')

# 将验证路径保持在会话中

s = requests.Session()

s.verify = '/path/to/certfile'

# 忽略对SSL的验证

requests.get('https://kennethreitz.org', verify=False)

4.客户端证书

requests.get('https://kennethreitz.org',cert=('/path/client.cert','/path/client.key'))

#证书在会话中

s = requests.Session()

s.cert = '/path/client.cert'

注意:本地证书的私有 key 必须是解密状态。目前,Requests 不支持使用加密的 key。

这里补充一句:当登录12306时喊你要安装证书。当向有证书验证要求的站点,就使用上面的代码

http://5.CA证书 科普:

Requests 默认附带了一套它信任的根证书,来自于Mozilla trust store。然而它们在每次 Requests 更新时才会更新。这意味着如果你固定使用某一版本的 Requests,你的证书有可能已经 太旧了。

从 Requests 2.4.0 版之后,如果系统中装了certifi包,Requests 会试图使用它里边的 证书。这样用户就可以在不修改代码的情况下更新他们的可信任证书。

6.响应体内容工作流

import requests

# 默认情况下,发起请求会同时下载响应头和响应体(就是响应内容)

tarball_url = 'https://github.com/kennethreitz/requests/tarball/master'

# 如果将stream=True 则会推迟响应内容的下载

r = requests.get(tarball_url, stream=True)

# 这里就是:满足某种条件才去下载

if int(r.headers['content-length']) < TOO_LONG:

content = r.content

# 在请求中把 stream 设为 True,Requests 无法将连接释放回连接池,

# 除非消耗了所有的数据,或者调用了 Response.close。

# 这样会带来连接效率低下的问题。如果在使用 stream=True 的同时还在部分读取请求的 body(或者完全没有读取 body),

# 那么就应该使用 with 语句发送请求,这样可以保证请求一定会被关闭

with requests.get('http://httpbin.org/get', stream=True) as r:

# 这里处理响应

content = r.content

7.保持活动状态(持久连接)科普

同一会话内的持久连接是完全自动处理的,同一会话内你发出的任何请求都会自动复用恰当的连接。

注意:只有所有的响应体数据被读取完毕连接才会被释放回连接池;所以确保将 stream 设置为 False 或读取 Response 对象的 content 属性。

8.流式上传

Requests支持流式上传,这允许发送大的数据流或文件,而无需先把它们读入内存。要使用流式上传,需,为请求体,提供一个类文件对象即可:

with open('massive-body') as f:

requests.post('http://some.url/streamed', data=f)

注意的问题:

最好使用二进制模式打开文件。这是因为 requests 有默认设置 header 中的 Content-Length,在这种情况下该值会被设为文件的字节数。如果用文本模式打开文件,就可能碰到错误

9.块编码请求

对于出去和进来的请求,Requests 也支持分块传输编码。要发送一个块编码的请求,仅需为请求体提供一个生成器(或任意没有具体长度的迭代器)

def gen():

yield 'hi'

yield 'there'

requests.post('http://some.url/chunked', data=gen())

注意:

对于分块的编码请求,最好使用 Response.iter_content() 对其数据进行迭代。在理想情况下,request 会设置 stream=True,这样就可以通过调用 iter_content 并将分块大小参数设为 none,从而进行分块的迭代。如果要设置分块的最大体积,可以把分块大小参数设为任意整数。

说白了,就是段点续传

妹的,报了一堆错。先记录在这,后面遇到了再研究

10.POST 多个分块编码的文件

上传图片

import requests

url = 'http://httpbin.org/post'

multiple_files = [

('images', ('foo.png', open('foo.png', 'rb'), 'image/png')),

('images', ('bar.png', open('bar.png', 'rb'), 'image/png'))]

r = requests.post(url, files=multiple_files)

print (r.text)

11.事件挂钩

import requests

def print_url(r, *args, **kwargs):

print(r.url)

# Requests有一个钩子系统,你可以用来操控部分请求过程,或信号事件处理。

# 在产生响应之前调用print_url 就是server响应之前的回调

hooks = dict(response=print_url)

r = requests.get('http://httpbin.org', hooks=dict(response=print_url))

print (r.text)

也没看有什么用

12.自定义身份验证

import requests

from requests.auth import AuthBase

class PizzaAuth(AuthBase):

def __init__(self, username):

# setup any auth-related data here

self.username = username

def __call__(self, r):

# modify and return the request

r.headers['X-Pizza'] = self.username

return r

#注意:auth参数必须是一个可调用对象 实现了 __call__ 方法

requests.get('http://pizzabin.org/admin', auth=PizzaAuth('kenneth'))

作用就是在请求发出之前,有机会修改请求

13.流式请求

import requests

r = requests.get('http://httpbin.org/stream/20', stream=True)

for line in r.iter_lines():

# filter out keep-alive new lines

if line:

decoded_line = line.decode('utf-8')

print(json.loads(decoded_line))

就是将请求参数设置stream=True

import requests

r = requests.get('http://httpbin.org/stream/20', stream=True)

#当使用 decode_unicode=True 在Response.iter_lines()Response.iter_content()中#时,需要提供一个编码方式,以防服务器没有提供默认回退编码,从而导致错误

if r.encoding is None:

r.encoding = 'utf-8'#设置编码方式

for line in r.iter_lines(decode_unicode=True):

if line:

print(json.loads(line))

#注意:iter_lines不保证重进入时的安全性。多次调用该方法 会导致部分收到的数据丢失。#如果要在多处调用它,就应该使用生成的迭代器对象:

lines = r.iter_lines()

# 保存第一行以供后面使用,或者直接跳过

first_line = next(lines)

forlineinlines:

print(line)

不晓得各位看懂什么是流式请求没。指的不是请求是流,而是请求返回的数据流。返回一点即取一点。而不是普通的是返回完成后在取内容

14.代理

import requests

proxies = {

"http": "http://10.10.1.10:3128",

"https": "http://10.10.1.10:1080",

}

requests.get("http://example.org", proxies=proxies)

代理:就是你访问一个网站,其实并不是你直接访问的,而是你发请求给A机器,A机器取请求B机器。B返回给A,A再返回给你。代理就是中间人的意思。为什么需要代理?因为:反爬虫网站一般使用IP来识别一个机器。老是一个IP再不停访问网站,该网站就会把这个IP拉入黑名单,不允许访问。这时,就需要很多IP再扰乱反爬虫工具的思维,避免封IP。

15.SOCKS

除了基本的 HTTP 代理,Request 还支持 SOCKS 协议的代理。这是一个可选功能,若要使用, 你需要安装第三方库。

在后面的项目实战中,我们将会使用

16.HTTP动词

GET、OPTIONS、HEAD、POST、PUT、PATCH、DELETE

GET POST最常用

PUT,DELETE在调用rest的接口时,也会用到

17.定制动词

有些服务器不接受GET POST等,要求用自定义的,就使用如下方法

r = requests.request('MKCOL',url,data=data)

r.status_code

18.响应头链接字段

许多 HTTP API 都有响应头链接字段的特性,它们使得 API 能够更好地自我描述和自我显露。

比如在使用分页的情况下最有用

url = 'https://api.github.com/users/kennethreitz/repos?page=1&per_page=10'

r = requests.head(url=url)

print(r.headers['link'])

print(r.links["next"])

19.阻塞和非阻塞

使用默认的传输适配器,Requests 不提供任何形式的非阻塞 IO。Response.content属性会阻塞,直到整个响应下载完成。如果你需要更多精细控制,该库的数据流功能(见流式请求) 允许你每次接受少量的一部分响应,不过这些调用依然是阻塞式的。

如果你对于阻塞式 IO 有所顾虑,还有很多项目可以供你使用,它们结合了 Requests 和 Python 的某个异步框架。典型的优秀例子是grequestsrequests-futures

20.Header 排序

在某些特殊情况下你也许需要按照次序来提供 header,如果你向 headers 关键字参数传入一个OrderedDict,就可以向提供一个带排序的 header。然而,Requests 使用的默认 header 的次序会被优先选择,这意味着如果你在 headers 关键字参数中覆盖了默认 header,和关键字参数中别的 header 相比,它们也许看上去会是次序错误的。

如果这个对你来说是个问题,那么用户应该考虑在Session对象上面设置默认 header,只要将Session设为一个定制的 OrderedDict 即可。这样就会让它成为优选的次序。

以上内容,是request的高级耍法,也不算好高级。接下来的一周,我们将着重讨论如何解析网页。当然,这一部分内容不会包含JS生成的HTML。这个我们将会在项目实战环节再讨论。

最近培训新人,搞得很晚才下班。精力不够哇。若是喜欢我的系列文章,还请帅哥美女点个赞,足矣

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容