《Python3 网络爬虫开发实战》:request(2)
Session 维持
在 requests 中,如果直接利用 get 或 post 等方法的确可以做到模拟网页的请求,但是这实际上是相当于不同的 Session,也就是说相当于你用了两个浏览器打开了不同的页面。
设想这样一个场景,第一个请求利用 requests 的 post 方法登录了某个网站,第二次想获取成功登录后的自己的个人信息,又用了一次 requests 的 get 方法去请求个人信息页面。
实际上,这相当于打开了两个浏览器,是两个完全独立的操作,对应两个完全不相关的 Session,能成功获取个人信息吗?那当然不能。
有人可能说了,我在两次请求时设置一样的 Cookies 不就行了?可以,但这样做起来显得很烦琐,我们有更简单的解决方法。
其实解决这个问题的主要方法就是维持同一个 Session,也就是相当于打开一个新的浏览器选项卡而不是新开一个浏览器。但是我又不想每次设置 Cookies,那该怎么办呢?这时候就有了新的利器 —— Session 对象。
利用它,我们可以方便地维护一个 Session,而且不用担心 Cookie 的问题,它会帮我们自动处理好。
我们先做一个小实验吧,如果沿用之前的写法,示例如下:/
SSL 证书验证
现在很多网站都要求使用 HTTPS 协议,但是有些网站可能并没有设置好 HTTPS 证书,或者网站的 HTTPS 证书可能并不被 CA 机构认可,这时候,这些网站可能就会出现 SSL 证书错误的提示。
比如这个示例网站:https://ssr2.scrape.center/,如果我们用 Chrome 浏览器打开这个 URL,则会提示「您的连
接不是私密连接」这样的错误,如图所示:
我们可以在浏览器中通过一些设置来忽略证书的验证。
但是如果我们想用 requests 来请求这类网站,会遇到什么问题呢?我们用代码来试一下
可以看到,这里直接抛出了 SSLError 错误,原因就是因为我们请求的 URL 的证书是无效的。
那如果我们一定要爬取这个网站怎么办呢?我们可以使用 verify 参数控制是否验证证书,如果将其设置为 False,在请求时就不会再验证证书是否有效。如果不加 verify 参数的话,默认值是 True,会自动验证。
我们改写代码如下:
可以看到,这里直接抛出了 SSLError 错误,原因就是因为我们请求的 URL 的证书是无效的。
那如果我们一定要爬取这个网站怎么办呢?我们可以使用 verify 参数控制是否验证证书,如果将其设置为 False,在请求时就不会再验证证书是否有效。如果不加 verify 参数的话,默认值是 True,会自动验证。
我们改写代码如下:
不过我们发现报了一个警告,它建议我们给它指定证书。我们可以通过设置忽略警告的方式来屏蔽这个警告:
超时设置
在本机网络状况不好或者服务器网络响应太慢甚至无响应时,我们可能会等待特别久的时间才可能收到响应,甚至到最后收不到响应而报错。为了防止服务器不能及时响应,应该设置一个超时时间,即超过了这个时间还没有得到响应,那就报错。这需要用到 timeout 参数。这个时间的计算是发出请求到服务器返回响应的时间。示例如下:
通过这样的方式,我们可以将超时时间设置为 1 秒,如果 1 秒内没有响应,那就抛出异常。
实际上,请求分为两个阶段,即连接(connect)和读取(read)。
上面设置的 timeout 将用作连接和读取这二者的 timeout 总和。
如果要分别指定,就可以传入一个元组:
如果想永久等待,可以直接将 timeout 设置为 None,或者不设置直接留空,因为默认是 None。这样的话,如果服务器还在运行,但是响应特别慢,那就慢慢等吧,它永远不会返回超时错误的。其用法如下:
代理设置
对于某些网站,在测试的时候请求几次,能正常获取内容。但是一旦开始大规模爬取,对于大规模且频繁的请求,网站可能会弹出验证码,或者跳转到登录认证页面,更甚者可能会直接封禁客户端的 IP,导致一定时间段内无法访问。
那么,为了防止这种情况发生,我们需要设置代理来解决这个问题,这就需要用到 proxies 参数。可以用这样的方式设置:
Prepared Request
我们使用 requests 库的 get 和 post 方法当然直接可以发送请求,但有没有想过,这个请求在 requests 内部是怎么实现的呢?
实际上,requests 在发送请求的时候,是在内部构造了一个 Request 对象,并给这个对象赋予了各种参数,包括 url、headers、data 等等,然后直接把这个 Request 对象发送出去,请求成功后会再得到一个 Response 对象,再解析即可。
那么这个 Request 是什么类型呢?实际上它就是 Prepared Request。
我们深入一下,不用 get 方法,直接构造一个 Prepared Request 对象来试试,代码如下