python2.7下urllib2的connection自动close的原因及解决办法

前文介绍了urllib2的常见问题的解决方案,今天来特别讨论下urllib2中短连接问题。

1、urllib2代码

如下文代码所示,自定义 'Connection': 'keep-alive',通知服务器交互结束后,不断开连接,即所谓长连接。

 1 #测试8 使用urllib2 测试Connection=keep-alive
 2 import urllib2 
 3 import cookielib
 4 
 5 
 6 httpHandler = urllib2.HTTPHandler(debuglevel=1)
 7 httpsHandler = urllib2.HTTPSHandler(debuglevel=1)
 8 opener = urllib2.build_opener(httpHandler, httpsHandler)
 9 urllib2.install_opener(opener)
10 
11 loginHeaders={
12 'User-Agent': 'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Maxthon/4.0 Chrome/30.0.1599.101 Safari/537.36',
13 'Referer': 'https://www.baidu.com',
14 'Connection': 'keep-alive'
15 }
16 request=urllib2.Request('http://www.suning.com.cn',headers=loginHeaders)
17 response = urllib2.urlopen(request)
18 page=''
19 page= response.read()
20 print response.info()
21 print page

输出报文:

注意日志中划线部分,可以看到请求报文其他头部,例如User-agent已被修改成功,但connection仍然保持close

  1. Connection: close
  2. header: Connection: close
 1 send: 'GET / HTTP/1.1\r\nAccept-Encoding: identity\r\nHost: www.suning.com.cn\r\nConnection: close\r\nUser-Agent: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Maxthon/4.0 Chrome/30.0.1599.101 Safari/537.36\r\n\r\n'
 2 reply: 'HTTP/1.1 200 OK\r\n'
 3 header: Connection: close
 4 header: Transfer-Encoding: chunked
 5 header: Expires: Thu, 19 Nov 1981 08:52:00 GMT
 6 header: Date: Sun, 15 May 2016 06:09:43 GMT
 7 header: Content-Type: text/html; charset=utf-8
 8 header: Server: nginx/1.2.9
 9 header: Vary: Accept-Encoding
10 header: X-Powered-By: ThinkPHP
11 header: Set-Cookie: PHPSESSID=tv7ced9sbu7lph60o8ilfj4207; path=/
12 header: Cache-Control: private
13 header: Pragma: no-cache
14 <!doctype html>
15 <html lang="zh-CN">
16 <head>
17     <meta charset="utf-8">
18     <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />

2、httplib2写法代码

换成httplib2协议的代码,当然这也是urllib2不支持keep-alive的解决办法之一,另一个方法是Requests。

 1 #测试9 使用httplib2测试Connection=keep-alive
 2 import httplib2
 3 
 4 ghttp = httplib2.Http()
 5 httplib2.debuglevel=1
 6 loginHeaders={
 7 'User-Agent': 'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Maxthon/4.0 Chrome/30.0.1599.101 Safari/537.36',
 8 'Connection': 'keep-alive'
 9 }
10 
11 response ,page= ghttp.request('http://www.suning.com.cn',headers=loginHeaders )
12 print page.decode('utf-8')

可以看到输出中,长连接设置成功。

  1. header: Connection: Keep-Alive
 1 connect: (www.suning.com.cn, 80) ************
 2 send: 'GET / HTTP/1.1\r\nHost: www.suning.com.cn\r\nconnection: keep-alive\r\naccept-encoding: gzip, deflate\r\nuser-agent: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Maxthon/4.0 Chrome/30.0.1599.101 Safari/537.36\r\n\r\n'
 3 reply: 'HTTP/1.1 200 OK\r\n'
 4 header: Connection: Keep-Alive
 5 header: Transfer-Encoding: chunked
 6 header: Expires: Thu, 19 Nov 1981 08:52:00 GMT
 7 header: Date: Sun, 15 May 2016 06:15:16 GMT
 8 header: Content-Type: text/html; charset=utf-8
 9 header: Server: nginx/1.2.9
10 header: Vary: Accept-Encoding
11 header: X-Powered-By: ThinkPHP
12 header: Set-Cookie: PHPSESSID=ep580j8bq3uba0ud3fe3rgu5i5; path=/
13 header: Cache-Control: private
14 header: Pragma: no-cache

3、分析原因

还是上urllib2的源码吧,可以看到在do_open核心方法中,connection被写死成了close。 至于原因就是上面那一堆注释,大概意思是addinfourl这个类一旦启用长链接,可以读取到上次交互未读完的应答报文,为了防止此类情况,所以强制性将Connection写死成close。    def do_open(self, http_class, req, **http_conn_args):         ……         # We want to make an HTTP/1.1 request, but the addinfourl         # class isn't prepared to deal with a persistent connection.         # It will try to read all remaining data from the socket,         # which will block while the server waits for the next request.         # So make sure the connection gets closed after the (only)         # request.  headers["Connection"] = "close" headers = dict((name.title(), val) for name, val in headers.items())         if req._tunnel_host:             tunnel_headers = {}             proxy_auth_hdr = "Proxy-Authorization"             if proxy_auth_hdr in headers:                 tunnel_headers[proxy_auth_hdr] = headers[proxy_auth_hdr]                 # Proxy-Authorization should not be sent to origin                 # server.                 del headers[proxy_auth_hdr]             h.set_tunnel(req._tunnel_host, headers=tunnel_headers)         try:             h.request(req.get_method(), req.get_selector(), req.data, headers)

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Python

浏览器User-Agent大全

HttpHeader之User-Agent UserAgent中文名为用户代理,是Http协议中的一部分,属于头域的组成部分,UserAgent也简称UA。它是...

6302
来自专栏高性能服务器开发

+从零实现一款12306刷票软件1.4

这里还有个注意细节,就是通过POST请求发送的数据需要对一些符号做URL Encode,这个我在上一篇文章《从零实现一个http服务器》也详细做了介绍,还不清楚...

2252
来自专栏程序员同行者

django-pure-pagination实现分页

1383
来自专栏小狼的世界

Virtualbox镜像文件克隆问题

VBoxManage clonehd centos-1.vdi centos-2.vdi

1174
来自专栏Jerry的SAP技术分享

如何用JavaScript判断前端应用运行环境(移动平台还是桌面环境)

我们部署在某些云平台或者Web服务器上的前端应用,既可以用PC端浏览器访问,也可以用手机上的浏览器访问。

1173
来自专栏Python自动化测试

python接口测试之http请求

python的强大之处在于提供了很多的标准库,这些标准库可以直接调用,本节部分,重点学习和总结在接口测试中Python的Http请求的库的学习。

3913
来自专栏我和PYTHON有个约会

User-agent大全

一、基础知识篇: Http Header之User-Agent User Agent中文名为用户代理,是Http协议中的一部分,属于头域的组成部分,User...

1542
来自专栏JAVA后端开发

修复shiro重定向引起的Response for preflight is invalid (redirect)的网络报错问题

最近集成shiro到项目中,遇到该一个报复Response for preflight is invalid (redirect)的问题。

5.1K2
来自专栏扎心了老铁

Mybatis分页插件PageHelper的配置和使用方法

前言 在web开发过程中涉及到表格时,例如dataTable,就会产生分页的需求,通常我们将分页方式分为两种:前端分页和后端分页。 前端分页 一次性请求数据表格...

9654
来自专栏一个会写诗的程序员的博客

《Springboot极简教程》问题解决:Idea解决Gradle库依赖冲突问题:Detected both log4j-over-slf4j.jar AND slf4j-log4j12.jar on

参考: https://docs.gradle.org/current/userguide/userguide_single.html#sec:listing...

953

扫码关注云+社区

领取腾讯云代金券