python模块介绍-httplib:HTTP协议客户端
注意:httplib模块在Python3.0中已更名为http.client。2to3脚本会自动修改import当你的源代码转换时到3.0时。
httplib实现了HTTP和HTTPS的客户端协议,一般不直接使用,在python更高层的封装模块中(urllib,urllib2)使用了它的HTTP和HTTPS实现。其中HTTPS需要socket编译了SSL支持。HTTP类仅仅是和python1.5.2及以前的版本兼容,不推荐使用。
代码:Lib/httplib.py
接口
基本类
-
class httplib.HTTPConnection(host[, port[, strict[, timeout[, source_address]]]]) :
HttpConnection的实例表示与HTTP服务器的事务。实例化时需要传递主机和可选的端口号。如果没有端口号,试图以host:port格式从主机字符串提取,如果提取失败则使用默认的HTTP端口(80)。参数strict默认为false,表示在无法解析状态行时(status line)不能被HTTP/1.0或1.1解析时不抛出BadStatusLine异常;可选参数timeout表示即阻塞在多少秒后超时,如果没有给出默认使用全局超时设置。可选参数source_address表示HTTP的源地址(host, port)。
timeout为2.6添加,source_address为2.7添加。
1
|
>>>
import
httplib>>> h1
=
httplib.HTTPConnection(
'automationtesting.sinaapp.com'
)>>> h1
=
httplib.HTTPConnection(
'automationtesting.sinaapp.com:80'
)>>> h1
=
httplib.HTTPConnection(
'automationtesting.sinaapp.com'
,
80
)>>> h1
=
httplib.HTTPConnection(
'automationtesting.sinaapp.com'
,
80
, timeout
=
10
)
|
-
class httplib.HTTPSConnection(host[, port[, key_file[, cert_file[, strict[, timeout[, source_address]]]]]])
HttpConnection的子类,使用SSL与安全服务器通信。默认端口为443。key_file是包含PEM格式私钥的文件名称。 cert_file中是PEM格式的证书链文件。
注意不支持验证服务器的证书。timeout为2.6添加,source_address为2.7添加。
-
class httplib.HTTPResponse(sock, debuglevel=0, strict=0)
实例连接成功之后返回的类,不能由用户实例化。
-
class httplib.HTTPMessage
HTTPMessage实例用于保存HTTP响应头。它使用mimetools.Message类实现,并提供了处理HTTP头的工具函数。它不直接实例化的用户。不能由用户实例化。
异常
-
exception httplib.HTTPException
Exception的子类,此模块中的其他异常的基类。下面的类默认是该类的直接子类。
-
httplib.NotConnected
-
httplib.InvalidURL
-
httplib.UnknownProtocol
-
httplib.UnknownTransferEncoding
-
httplib.UnimplementedFileMode
-
httplib.IncompleteRead
-
httplib.ImproperConnectionState
-
httplib.CannotSendRequest
ImproperConnectionState的一个子类。
-
httplib.CannotSendHeader
ImproperConnectionState的一个子类。
-
httplib.ResponseNotReady
ImproperConnectionState的一个子类。
-
httplib.BadStatusLine
服务器返回的HTTP状态码不认识时产生。
常量和类变量
-
httplib.HTTP_PORT:HTTP协议的默认端口,恒为80。
-
httplib.HTTPS_PORT:HTTPS协议的默认端口,恒为443。
-
httplib.responses:映射HTTP1.1状态代码映射到W3C的名字的字典。2.5新增
1
|
>>> httplib.responses[httplib.NOT_FOUND]
'Not Found'
|
HTTPConnection类的方法
-
HTTPConnection.request(method, url[, body[, headers]])
发送请求HTTP请求到服务器,使用方法method和指定的地址url。如果有body,一般是字符串数据,将在headers之后发送。当然body也可以是支持fileno()和read()方法的文件对象,这时会发送文件内容。header内容长度会自动填充为正确的值,headers是额外HTTP头的映射,会和请求一起发送。2.6开始支持文件对象。
-
HTTPConnection.getresponse()
用于在请求发送以后从服务器端获取响应。返回HTTPResponse实例。注意发送下一条请求之前必须读完整个响应。
-
HTTPConnection.set_debuglevel(level)
设置调试级别(调试输出打印量)。默认的调试级别为0 ,表示没有调试输出打印。
-
HTTPConnection.set_tunnel(host, port=None, headers=None)
设置HTTP连接隧道的主机和端口。需要通过代理服务器做HTTPS连接时使用。header参数必须为额外HTTP头的映射随CONNECT一起发送。2.7新增。
-
HTTPConnection.connect()
对象创建之后连接到指定的服务器。
-
HTTPConnection.close()
关闭到服务器的连接。
下面四个函数可以代替request()一步步发送请求。
-
HTTPConnection.putrequest(request, selector[, skip_host[, skip_accept_encoding]])
连接到服务器后的第一个调用。它发送由request字符串,selector字符串, HTTP版本(HTTP/1.1)的行数据到服务器。设置skip_host为非Fasle值可禁用自动发送主机,设置skip_accept_encoding为非Fasle值可禁用接受编码。skip_accept_encoding为2.4添加。
-
HTTPConnection.putheader(header, argument[, ...])
发送一个RFC 822样式头到服务器。它发送由header,一个冒号和一个空格,第一个参数到服务器。如果有更多参数会发送多行,每行由一个tab和一个参数组成。
-
HTTPConnection.endheaders(message_body=None)
发送空行到服务器,指示header的结束。可选的optional message_body参数可以用来传递与请求相关的消息体。消息体如果是字符串将在消息头的包发送,如果它是字符串,否则用单独数据包。2.7加入message_body。
-
HTTPConnection.send(data)
将数据发送到服务器。endheaders()之后,getresponse()之前使用。
HTTPResponse类的方法
-
HTTPResponse.read([amt])
读取并返回response的body,或下面的amt字节。
-
HTTPResponse.getheader(name[, default])
获得头名,如果没有匹配的头返回default。
-
HTTPResponse.getheaders()
返回(header, value)元组构成的列表。2.4新增。
-
HTTPResponse.fileno()
返回底层套接字的的fileno。
-
HTTPResponse.msg
包含响应头的mimetools.Message实例。
-
HTTPResponse.version
服务器使用的HTTP协议版本。10表示HTTP/1.011表示HTTP/1.1。
-
HTTPResponse.status
由服务器返回的状态代码。
-
HTTPResponse.reason
服务器的原因短语。
实例
HTTP GET
1
2
3
|
#!/usr/bin/env python# -*- coding: utf-8 -*-import httplibconn = httplib.HTTPConnection("www.python.org")conn.request("GET", "/index.html")r1 = conn.getresponse()print r1.status, r1.reason#data1 = r1.read()print data1
conn.request(
"GET"
,
"/parrot.spam"
)r2
=
conn.getresponse()
print
r2.status, r2.reason
data2
=
r2.read()
#print data2conn.close()
|
执行结果
1
2
3
|
# ./http_get.py
200 OK
404 Not Found
|
HTTP HEAD
注意HEAD不会返回任何数据。
1
2
|
#!/usr/bin/env python# -*- coding: utf-8 -*-import httplibconn = httplib.HTTPConnection("www.python.org")conn.request("HEAD","/index.html")res = conn.getresponse()print res.status, res.reason
data
=
res.read()
print
len
(data)
print
data
=
=
''conn.close()
|
执行结果
1
2
3
4
|
# ./http_head.py
200 OK
0
True
|
HTTP POST
1
2
3
4
|
#!/usr/bin/env python# -*- coding: utf-8 -*-import httplib, urllibparams = urllib.urlencode({'@number': 12524, '@type': 'issue', '@action': 'show'})headers = {"Content-type": "application/x-www-form-urlencoded",
"Accept"
:
"text/plain"
}conn
=
httplib.HTTPConnection(
"bugs.python.org"
)conn.request(
"POST"
, "", params, headers)response
=
conn.getresponse()
print
response.status, response.reason
data
=
response.read()
print
data
conn.close()
|
执行结果
1
2
3
4
|
# ./http_head.py
# ./test.py
302 Found
Redirecting to <a href=
"http://bugs.python.org/issue12524"
>http:
//bugs
.python.org
/issue12524
<
/a
>
|
HTTP PUT
客户端的HTTP PUT请求和POST类似。不同之处在于服务器端允许PUT创建资源。
1
|
>>>
# This creates an HTTP message>>> # with the content of BODY as the enclosed representation>>> # for the resource http://localhost:8080/foobar...>>> import httplib>>> BODY = "***filecontents***">>> conn = httplib.HTTPConnection("localhost", 8080)>>> conn.request("PUT", "/file", BODY)>>> response = conn.getresponse()>>> print resp.status, response.reason200, OK
|
相关实例应用
客户端的HTTP PUT请求和POST类似。不同之处在于服务器端允许PUT创建资源。
1
|
举例1 http对象定义: #!/usr/bin/env python # coding: gbk import os import httplib import socket import urllib import json class HTTPClient( object ): """ http通讯协议,客户端常用方法 """ # Client对象属性 host=None port=None timeout=None METHOD = "POST" # 默认为POST方式提交 # http头信息 HEADERS = { "Content-Type": "application/x-www-form-urlencoded" } # Client对象行为 def __init__( self, host, port=8080, timeout=60): self.host = host self.port = port self.timeout = timeout def get_conn( self ): """ 获取http连接对象 """ conn = None try: conn = httplib.HTTPConnection( self.host, self.port, timeout=self.timeout) return conn except: raise def request( self, conn, url, params, method=None, headers=None): """ 发送请求 """ try: if not method: method = self.METHOD if not headers: headers = self.HEADERS if type( headers ) != dict: raise RuntimeError( "headers数据异常" ) if not headers.has_key( "Content-Type" ): headers.update( self.HEADERS ) if method == 'POST': ctype = headers.get( 'Content-Type' ).upper() if 'application/x-www-form-urlencoded'.upper() in ctype: params = urllib.urlencode( params ) # params要求是字典 conn.request( method, url, params, headers) elif 'application/json'.upper() in ctype: params = json.dumps( params ) # 默认encoding utf-8 headers = { "Content-Type" : ctype } conn.request( method, url, params, headers) elif 'text/xml'.upper() in ctype: headers = { "Content-Type" : ctype } conn.request( method, url, params, headers) else: raise RuntimeError( "暂不支持[%s]类请求" % ctype ) elif method == 'GET': params = urllib.urlencode( params ) # params要求是字典 _url = "%s?%s" % ( url, params ) conn.request( method, _url, None, headers ) else: raise RuntimeError( "暂不支持[%s]类请求" % method ) resp = conn.getresponse() return resp except: raise def close_conn( self, conn ): """ 关闭连接 """ conn.close() def test( host ): import hashlib conn = None try: client = HTTPClient( host, port='9077' ) # 获取https客户端对象 conn = client.get_conn() # 获取连接对象 secret = '8d939eabe1839d3784cb28de32b0a092' aid = '99990001' d = { "header":{ "version":"1.0", "method":"create"}, "data":{ "aid":"99990001", "desc":"30#啤酒500箱,立即发货".decode("gbk").encode("utf-8"), "logid":"13911119999", "amount":"000000000100", "localtime":"20141120201120", "orderid":"20150108101110", "callback": urllib.unquote( "https://46.17.189.110/ordernotice" ) } } hash = hashlib.md5() hash.update( '%s%s%s%s' % ( secret, d['data']['orderid'], d['data']['amount'], d['data']['callback'] ) ) d['data']['token'] = hash.hexdigest() url="/callback/khzf" # 支付订单查询 headers = { 'Content-Type' : "text/xml"} d = """<?xml version="1.0" encoding="GBK"?><packet><transName>KHCS</transName><Plain>BTermSsn=140918000000|ETermSsn=140918112019|MercCode=983708160002901|OTranAbbr=KHZF</Plain><Signature>12fec0b699ccdd97696cc855147221c418532a9ba9b6ad22be21d7f78f6ed580b22931ff6be69769e18b262e9986595c6a854825a840febf48d9e2316e2a8de722c28f035cd61ebd20ad99329e046e84f0f17d57bb0f22a83019976d7c446cfb5f7190c4365e4f078bc5933b436ac0c742824de0576cc8cb4c859f8c8452f31d</Signature></packet>""" resp = client.request( conn, url, d, headers=headers, method="POST" ) print resp.status, resp.reason except RuntimeError, e: print e finally: if conn: conn.close() if __name__ == '__main__': test( '42.96.249.190' ) 举例2 https对象定义: #!/usr/bin/env python # coding: gbk import os import httplib import socket import urllib import json from jnxlk_utils import str_to_dic class HTTPSClient( object ): """ https 通讯,客户端常用方法 """ # Client对象属性 host=None port=None key_file=None cert_file=None timeout=None METHOD = "POST" # 默认为POST方式提交 # http头信息 HEADERS = { "Content-Type": "application/x-www-form-urlencoded" } # Client对象行为 def __init__( self, host, port=443, key_file=None, cert_file=None, timeout=60): self.host = host self.port = port self.key_file = key_file self.cert_file = cert_file self.timeout = timeout def get_conn( self ): """ 获取http连接对象 """ conn = None try: conn = httplib.HTTPSConnection( self.host, self.port, self.key_file, self.cert_file, timeout=self.timeout) return conn except: raise def request( self, conn, url, params, method=None, headers=None): """ 发送请求 """ try: if not method: method = self.METHOD if not headers: headers = self.HEADERS if type( headers ) != dict: raise RuntimeError( "headers数据异常" ) if not headers.has_key( "Content-Type" ): headers.update( self.HEADERS ) if method == 'POST': ctype = headers.get( 'Content-Type' ).upper() if 'application/x-www-form-urlencoded'.upper() in ctype: params = urllib.urlencode( params ) # params要求是字典 conn.request( method, url, params, headers) elif 'application/json'.upper() in ctype: params = json.dumps( params ) # 默认encoding utf-8 headers = { "Content-Type" : ctype } conn.request( method, url, params, headers) elif 'text/xml'.upper() in ctype: headers = { "Content-Type" : ctype } conn.request( method, url, params, headers) else: raise RuntimeError( "暂不支持[%s]类请求" % ctype ) elif method == 'GET': params = urllib.urlencode( params ) # params要求是字典 _url = "%s?%s" % ( url, params ) conn.request( method, _url, None, headers ) else: raise RuntimeError( "暂不支持[%s]类请求" % method ) resp = conn.getresponse() return resp except: raise def close_conn( self, conn ): """ 关闭连接 """ conn.close() 举例3 http通讯应用: # coding: gbk import os import httplib import socket import urllib from http_client import HTTPClient from uni.apis.utils import ftp_put, ftp_get from uni.core import logp from shangjie.conf import settings __all__ = ["SignVerify"] class SignVerify( object ): """ 证书验证类 url="/jnhxsdgsetcsjpay/sign" # 无文件签名 url1="/jnhxsdgsetcsjpay/signFile" # 有文件签名 url2="/jnhxsdgsetcsjpay/signcheck" # 签名验签 url2="/jnhxsdgsetcsjpay/signcheckFile" # 有文件签名验签 """ url = None host = None url = None def __init__( self, host, port, url=None, timeout=30 ): """ 创建验证类 """ self.host = host self.port = port self.url = url self.timeout = timeout def getSignData( self, plainData, filename = None, user=None, passwd=None ): """ 根据原始报文数据plainData 生成签名signData @filename : 文件是否存在的标志,非空即表示有文件 @plainData : 原始报文 @filename : 文件名 """ self.url = "/jnhxsdgsetcsjpay/sign" # 无文件时需要访问的路径 if filename: # host , port , user , passwd self.url = "/jnhxsdgsetcsjpay/signFile" # 有文件时需要访问的路径 ftp_put( self.host , 21 , user , passwd , os.path.join(settings.FTP_JAVA_PATH,filename) ) logp.info( '文件传送java服务器成功' ) method = "POST" conn = None try: # 获得httpsclient对象 http_client = HTTPClient( self.host, self.port ) # 连接三方 conn = http_client.get_conn() # 构造请求字典 headers = { # http请求头 'Content-Type':"application/x-www-form-urlencoded" } data = { "plain":plainData } response = http_client.request( conn, self.url, data, method=method, headers=headers ) # 解析报文 返回的报文eval()一下就可以转换成字典格式 成功时:{'response':'True','return_value':'签名串'} 失败时:{'response':'False','return_value':'失败原因'} resp = response.read() ############返回的resp是字符串,需要转换成字典,注意修改他行批扣流程############# resp = eval( resp ) # print '签名后的返回内容:', response.status, response.reason, resp except Exception, e: logp.info( 'java加解密服务器报错:%s'%str(e) ) resp = {} finally: if conn : conn.close() return resp def verSignData( self, plainData, filename=None, user=None, passwd=None ): """ 根据原始报文判断签名是否正确; 批量查询类交易、文件传输类交易这两支交易需要在验签成功后返回一个文件名; @ filename : 文件是否存在的标志,非空即表示有文件 @ plainData : 原始报文 @ return True,filename=None or False """ self.url = "/jnhxsdgsetcsjpay/signcheck" if filename: self.url = "/jnhxsdgsetcsjpay/signcheckFile" method = "POST" conn = None try: # 获得httpsclient对象 http_client = HTTPClient( self.host, self.port ) # 连接三方 conn = http_client.get_conn() # 构造请求字典 headers = { # http请求头 'Content-Type':"application/x-www-form-urlencoded" } data = { "plain":plainData } response = http_client.request( conn, self.url, data, method=method, headers=headers ) # 解析报文 resp = response.read() # 一个字符串 logp.info( '验签的返回内容:%s,%s,%s'%(response.status, response.reason, resp) ) ############返回的resp是字符串,需要转换成字典,注意修改他行批扣流程############# resp = eval( resp ) # 转换成字典:成功时:{'response':'True','return_value':'空值','filename':'文件名'} 失败时:{'response':'False','return_value':'失败原因','filename':'空值'} if resp['filename']: # host , port , user , passwd ftp_get( self.host , 21 , user , passwd , os.path.join(settings.FTP_JAVA_PATH,resp['filename']) ) logp.info( '成功从java服务模块的D:/file文件中获取到文件!' ) except Exception, e: raise resp = {} finally: if conn : conn.close() return resp 举例4 https通讯应用: 举例3 http通讯应用: # coding: gbk import os import httplib import socket import urllib from http_client import HTTPClient from uni.apis.utils import ftp_put, ftp_get from uni.core import logp from shangjie.conf import settings __all__ = ["SignVerify"] class SignVerify( object ): """ 证书验证类 url="/jnhxsdgsetcsjpay/sign" # 无文件签名 url1="/jnhxsdgsetcsjpay/signFile" # 有文件签名 url2="/jnhxsdgsetcsjpay/signcheck" # 签名验签 url2="/jnhxsdgsetcsjpay/signcheckFile" # 有文件签名验签 """ url = None host = None url = None def __init__( self, host, port, url=None, timeout=30 ): """ 创建验证类 """ self.host = host self.port = port self.url = url self.timeout = timeout def getSignData( self, plainData, filename = None, user=None, passwd=None ): """ 根据原始报文数据plainData 生成签名signData @filename : 文件是否存在的标志,非空即表示有文件 @plainData : 原始报文 @filename : 文件名 """ self.url = "/jnhxsdgsetcsjpay/sign" # 无文件时需要访问的路径 if filename: # host , port , user , passwd self.url = "/jnhxsdgsetcsjpay/signFile" # 有文件时需要访问的路径 ftp_put( self.host , 21 , user , passwd , os.path.join(settings.FTP_JAVA_PATH,filename) ) logp.info( '文件传送java服务器成功' ) method = "POST" conn = None try: # 获得httpsclient对象 http_client = HTTPClient( self.host, self.port ) # 连接三方 conn = http_client.get_conn() # 构造请求字典 headers = { # http请求头 'Content-Type':"application/x-www-form-urlencoded" } data = { "plain":plainData } response = http_client.request( conn, self.url, data, method=method, headers=headers ) # 解析报文 返回的报文eval()一下就可以转换成字典格式 成功时:{'response':'True','return_value':'签名串'} 失败时:{'response':'False','return_value':'失败原因'} resp = response.read() ############返回的resp是字符串,需要转换成字典,注意修改他行批扣流程############# resp = eval( resp ) # print '签名后的返回内容:', response.status, response.reason, resp except Exception, e: logp.info( 'java加解密服务器报错:%s'%str(e) ) resp = {} finally: if conn : conn.close() return resp def verSignData( self, plainData, filename=None, user=None, passwd=None ): """ 根据原始报文判断签名是否正确; 批量查询类交易、文件传输类交易这两支交易需要在验签成功后返回一个文件名; @ filename : 文件是否存在的标志,非空即表示有文件 @ plainData : 原始报文 @ return True,filename=None or False """ self.url = "/jnhxsdgsetcsjpay/signcheck" if filename: self.url = "/jnhxsdgsetcsjpay/signcheckFile" method = "POST" conn = None try: # 获得httpsclient对象 http_client = HTTPClient( self.host, self.port ) # 连接三方 conn = http_client.get_conn() # 构造请求字典 headers = { # http请求头 'Content-Type':"application/x-www-form-urlencoded" } data = { "plain":plainData } response = http_client.request( conn, self.url, data, method=method, headers=headers ) # 解析报文 resp = response.read() # 一个字符串 logp.info( '验签的返回内容:%s,%s,%s'%(response.status, response.reason, resp) ) ############返回的resp是字符串,需要转换成字典,注意修改他行批扣流程############# resp = eval( resp ) # 转换成字典:成功时:{'response':'True','return_value':'空值','filename':'文件名'} 失败时:{'response':'False','return_value':'失败原因','filename':'空值'} if resp['filename']: # host , port , user , passwd ftp_get( self.host , 21 , user , passwd , os.path.join(settings.FTP_JAVA_PATH,resp['filename']) ) logp.info( '成功从java服务模块的D:/file文件中获取到文件!' ) except Exception, e: raise resp = {} finally: if conn : conn.close() return resp |
举例3 http通讯应用: