1、http请求html
一、http请求方式:get和postpython
get通常用于获取/查询资源信息,在浏览器中直接输入url+请求参数点击enter以后链接成功服务器就能获取到的内容,post请求通常用于更新资源,经过form表单或者json、xml等其余形式提交给服务器端,而后等待服务器端给返回一个结果的方式(这个返回结果通常就是被修改以后的是否成功的状态,或者是修改后的最新数据table等)。nginx
http请求,不管是get仍是post请求,都会包含几个部分,分别是header,cookie,get会有param,post会有body。git
这个能够经过fiddler里面抓包就能够拿到须要的Headers,通常须要设置的值可能有:github
header = {
"Host": "x.x.360.cn",
"Authorization": "Basic: someValue",
"Content-Type": r"application/json",
"Connection": "keep-alive",
"Proxy-Connection": "keep-alive",
"Cookie": "xxxxxxxxx(备注:这里的具体值请自行填写,其余key对应的值也是同样)",
"User-Agent": "360xxxxxx(备注:这里的信息也请自行抓到以后填写,不须要的话,能够不用填写)"
} json
针对正式环境和测试环境须要设置url的地址,以及Header的"Host"中的具体域名的方法以下:python3.x
(1)正式环境:url中的host也设置成域名,好比:http://%s/search/searchList的%s就替换成 域名,在headers中的"HOST"的键对应的value也是域名,好比说都是"x.y.360.cn"浏览器
(2)测试环境: url中的host设置成具体的IP,好比:http://%s/search/searchList的%s就替换成 10.108.225.234这样的具体IP(备注,这个IP就是大家平时开发上测试代码的机器),可是headers中的"HOST"的键对应的value必须得写成域名,好比"x.y.360.cn" 安全
缘由:由于一个IP地址对应的服务器上可能会有多个域名,由于可能会上多个不一样业务的服务器代码,如此会有一个默认的域名,可是并不必定是你的这个业务对应的域名,因此必定要在headers中的"HOST"中指定域名才能够找到这个域名,从而找到其对应的接口,进行正确的调用。服务器
进一步,对于一个IP地址对应的服务器,其上会有不少域名,这个是如何部署的呢?须要问一下服务器端的同窗,好比说会有x.360.cn和x.y.360.cn,这个是如何进行配置的呢?具体缘由是使用了nginx的配置:http://www.2cto.com/os/201411/355366.html;具体的内容就是指:一台nginx服务器多域名配置,而后客户端请求的时候,就能自动根据这个host找到对应的文件目录,而后找到对应处理方法,这个后续要再详细了解一下。
cookie信息都是在headers里面的"Cookie"键对应的value后面,这个能够经过日志或者抓包获得,注意,抓到的信息必定要原封不动的所有拿来用。
另外,这个cookie信息也能够经过其余方式获取,好比说,经过登陆接口拿到cookie信息,再将cookie信息设置到后续须要的"Cookie"中。
具体的body的值,须要跟服务器端开发对应一下数据的加密方式,目前比较多的都是经过json格式的,须要确认的是几层json,好比咱们的开发同窗搞了两层json,致使我刚开始的时候就在最外面搞了一层json转换格式,结果请求的时候一直提示Resopnse 200,可是返回的errorMsg一直是错误请求。(备注:首先须要确认Response的Status是200的话,就说明已经跟服务器端链接上了,而后若是拿不到正确的数据,那就要分析是你的数据传送格式不正确,仍是缺乏了哪些内容,致使服务器端解析不出,或者没法给出你想要的内容)
通常的get请求的格式,一个参数的多是这样的:http://xxx/search/YYYY?&kw=123456789,若是是多个参数的话:http://music.baidu.com/search?fr=ps&ie=utf-8&key=%E7%9C%8B%E8%A7%81%E4%BA%86,好比像百度音乐的这个url,在?后面均可以添加一个&,而后url其实也能够变成这样的格式:http://music.baidu.com/search?&fr=ps&ie=utf-8&key=%E7%9C%8B%E8%A7%81%E4%BA%86,可是实际上访问get到的都是相同的内容,也就是说服务器端解析的时候,返回的结果都是相同的内容;多个参数,就每一个参数之间加一个&连接起来,可是注意,有些值传的时候可能须要进行urlencode编码,而且必定要在跟服务器端相同的编码的基础上进行urlencode编码(我本身碰到的坑:个人python程序用的编码方式是:gbk,咱们服务器端的编码方式是utf-8,我最开始的时候,直接对中文进行了urlencode编码,可是获得的结果不是想要的,最后才发现原来我urlencode以后的码与服务器端urlencode以后的码不一样,因此固然解不出了,那么就decode('gbk').encode('utf-8'),而后获得的内容再urlencode,以后才正确。。。因此都是坑)
备注1:须要了解一下get请求在服务器端是怎么处理的?post请求在服务器端又是如何处理的?这个须要另开一篇博客专门写一下。
备注2:关于编码方式,以及几种编码方式的转换(编码解码等),进行urlencode的具体方法,在python26的urllib中有urlencode方法,只能对dict进行编码,若是只是对字符串进行编码,须要使用urllib.quote()方法
好比:
>>> import urllib >>> xx = {'kw': '达达'} >>> urllib.urlencode(xx) 'kw=%B4%EF%B4%EF' >>> ss = File "<stdin>", line 1 ss = ^ SyntaxError: invalid syntax >>> >>> ss = '达达' >>> urllib.urlencode(ss) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "C:\Python26\lib\urllib.py", line 1255, in urlencode raise TypeError TypeError: not a valid non-string sequence or mapping object >>> urllib.quote(ss) '%B4%EF%B4%EF'
查看当前处于什么编码格式:
>>> import sys >>> sys.getdefaultencoding() 'ascii'
编码及解码:
在python中使用decode和encode进行编码和解码,好比咱们get到的str类型是gbk的,那就能够str.decode(''gbk'),以后再encode成咱们想要的格式
通常状况下经常使用的编码格式主要有:utf八、gbk、gb2312;在python26中默认的编码是ascii,可是在python3.x中默认的编码是utf-8
后面再专门针对编码这块作一个大块的总结。
二、http请求端口、cookie,以及实现具体的get和post请求
http请求端口默认是80,若是不指定的话,默认走的就是80,不然就须要指定服务器端指定listen的端口。
cookie是什么?具体见:http://www.cnblogs.com/hdtianfu/archive/2013/05/30/3108295.html, 主要内容:有两个Http头部和Cookie有关:Set-Cookie和Cookie。Set-Cookie由服务器发送,它包含在响应请求的头部中。它用于在客户端建立一个Cookie。Cookie头由客户端发送,包含在HTTP请求的头部中。注意,只有cookie的domain和path与请求的URL匹配才会发送这个cookie。
(1)httplib库——HTTP protocol client
切记:要从用户手册中学习!
httplib在python3.0中已经改名为http.client了。
class httplib.HTTPConnection(host[,port[,strict[,timeout]]])
class httplib.HTTPSConnection(host[,port[,key_file[,cert_file[,strict[,timeout]]]]]) ——这是HTTPConnection的一个子类,使用了SSL,用来跟安全服务器进行通讯。默认的端口是443。key_file是一个pem格式的包含了密钥的文件,cert_file是一个pem格式的证书链文件。
而后这个httplib的HttpConnection的类调用以后,可以获得一个HTTPConnection的instance,就是一个HTTPConnection或者HTTPSConnection的一个对象,好比设置其名称为conn,以后利用这个conn的对象就能够继续走request(method,url[,body[,headers]])的请求,调用request方法以后,继续调用conn.getresponse(),而后返回一个HTTPResponse的实例对象,例如为res,而后调用res.getheaders()方法获取response的头部,获得的一个(header,value)的tuple,经过res.status就能够获得状态(200为OK,链接上的含义),res.read()就能够获得response的body信息,而后本身再针对body信息的类型,好比是json,就解析出来显示便可。
具体的使用例子用户手册中也说明了:
>>> import httplib >>> conn = httplib.HTTPConnection("www.python.org") >>> conn.request("GET", "/index.html") >>> r1 = conn.getresponse() >>> print r1.status, r1.reason 301 Moved Permanently >>> conn.request("GET", "/parrot.spam") >>> r2 = conn.getresponse() >>> print r2.status, r2.reason 301 Moved Permanently >>> conn2 = httplib.HTTPConnection("jia.360.cn") >>> conn2.request("GET", "/standard.html") >>> r3 = conn2.getresponse() >>> print r3.status 200 >>> data = r3.read() >>> print data <!Doctype html><html lang="zh-CN"><head>.......
以上例子中,先用的是用户手册的example中的例子,可是由于www.python.org被永久转移,因此返回的结果如上;因此选择了"jia.360.cn"的url,以后request中请求的是标准版摄像机的页面,即"/standard.html",以后就可以获得r3的结果,为200,说明链接OK了,以后就能经过r3.read()获得body的内容,经过r3.getheaders()就能获取到header的内容。
以上都是request方法中都是"GET"方法,换成"POST"须要传的内容会有一些差异,以下:
>>> import httplib, urllib >>> params = urllib.urlencode({'spam': 1, 'eggs': 2, 'bacon': 0}) >>> headers = {"Content-type": "application/x-www-form-urlencoded", ... "Accept": "text/plain"} >>> conn = httplib.HTTPConnection("musi-cal.mojam.com:80") >>> conn.request("POST", "/cgi-bin/query", params, headers) >>> response = conn.getresponse() >>> print response.status, response.reason 200 OK >>> data = response.read() >>> conn.close()
备注:以上代码也是运行不经过的,由于是比较久远的python版本的例子,主要须要注意的是:须要本身设置headers,在其中根据须要传递Cookie、Content-Type、Accept等信息,经过key-value的形式传递,具体的body中传递的信息,要注意是json格式的,仍是经过urlencode编码等,格式必定要跟开发沟通清楚,不然会有错误请求的问题,以后获得response,并获取response的status、body、headers就与前面的"GET"method同样了。
(2)request库
request库是python的第三方库,官方文档地址:http://www.python-requests.org/en/master/user/quickstart/#make-a-request
get请求:
>>> r = requests.get('http://httpbin.org/get') >>> r <Response [200]> >>> r.text u'{\n "args": {}, \n "headers": {\n "Accept": "*/*", \n "Accept-Encoding": "gzip, deflate", \n "Host": "httpbin.org", \n "User-Agent": "python-requests/2.9.1"\n }, \n "origin": "218.30 .116.9", \n "url": "http://httpbin.org/get"\n}\n'
post请求:
>>> r = requests.post('http://httpbin.org/post', data={'key':'value'}) >>> r <Response [200]> >>> r.text u'{\n "args": {}, \n "data": "", \n "files": {}, \n "form": {\n "key": "value"\n }, \n "headers": {\n "Accept": "*/*", \n "Accept-Encoding": "gzip, deflate", \n "Content-Length": "9" , \n "Content-Type": "application/x-www-form-urlencoded", \n "Host": "httpbin.org", \n "User-Agent": "python-requests/2.9.1"\n }, \n "json": null, \n "origin": "218.30.116.185", \n "url": "http://httpbin.org/post"\n}\n'
我这里用的仍是httplib的,request的后续有详细使用教程会补充上来。
2、https请求
一、https的请求方式:get和post
http和https的区别:
(1)url的前面是https://而不是http://,使用ssl进行加密/身份认证,而且http的默认端口是80,https的默认端口是443。
(2)由于有ssl的认证和加密,因此具体的底层的通讯过程当中会有不一样,https的这一层在创建链接的时候,须要设置socket属性,socket属性的生成须要使用具体的方法调用,方法调用的参数须要指定:ca_certs=服务器端给提供的公钥证书便可。
而后若是还有客户端认证的话,那客户端也能够提供出本身的key_file,cert_file。
什么是ssl?
ssl的全称是(Secure Sockets Layer)安全套接层,另外还有TLS(Transport Layer Secure,传输层安全),这两种协议都是为网络提供安全和数据完整性的一种安全协议,在传输层对网络链接进行加密。
为何要用这个?
防止数据以及网络链接的传输内容被截获,因此涉及到我的或者重要的信息等,都须要进行创建ssl链接,经过https的请求方式加密处理。
二、https请求端口、ssl创建,以及实现具体的get和post请求
post请求:
httpsConn = None try: httpsConn = httplib.HTTPSConnection(host) sock = socket.create_connection((httpsConn.host, httpsConn.port)) try: httpsConn.sock = ssl.wrap_socket(sock, ca_certs=CERT_FILE, cert_reqs=ssl.CERT_REQUIRED, ssl_version=ssl.PROTOCOL_SSLv3) #self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, ssl_version=ssl.PROTOCOL_SSLv3) except ssl.SSLError, e: print("Trying SSLv3.") try: httpsConn.sock = ssl.wrap_socket(sock, ca_certs=CERT_FILE, cert_reqs=ssl.CERT_REQUIRED, ssl_version=ssl.PROTOCOL_SSLv23) #self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, ssl_version=ssl.PROTOCOL_SSLv23) except ssl.SSLError, e: print("Trying SSLv23.") try: httpsConn.sock = ssl.wrap_socket(sock, ca_certs=CERT_FILE, cert_reqs=ssl.CERT_REQUIRED, ssl_version=ssl.PROTOCOL_TLSv1) except ssl.SSLError, e: print("Trying TLSv1.") try: httpsConn.sock = ssl.wrap_socket(sock, ca_certs=CERT_FILE, cert_reqs=ssl.CERT_REQUIRED, ssl_version=ssl.PROTOCOL_SSLv2) except ssl.SSLError, e: print("Trying SSLv2.") httpsConn.request("POST", path, body, headers) res = httpsConn.getresponse() headers = {} for k, v in res.getheaders(): headers[k] = v return res.status, headers, res.read() except Exception, e: import traceback print traceback.format_exc() return e finally: if httpsConn: httpsConn.close
备注:
由于是客户端证书,因此没有使用注释的代码:#self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, ssl_version=ssl.PROTOCOL_SSLv3),这个程序中须要指定客户端的私钥密钥的文件,若是只有服务器端有私钥,客户端有公钥,则客户端的程序须要指定公钥文件,见代码:httpsConn.sock = ssl.wrap_socket(sock, ca_certs=CERT_FILE, cert_reqs=ssl.CERT_REQUIRED, ssl_version=ssl.PROTOCOL_SSLv3),是经过ca_certs参数指定的,CERT_FILE是文件的路径,保证可以找到便可;若是是是一个文件夹下有多个文件,而后这多个文件都是须要用到的,好比A域名的证书和B域名的证书,A服务器在对接口处理请求的时候,会向B端发请求,如此客户端须要将A域名证书和B域名证书都添加进来,因此只要把文件夹路径设置成ca_certs参数的值便可。
另外,若是不肯定SSL的版本,则须要尝试多个不一样的SSL版本:ssl.PROTOCOL_TLSv一、ssl_version=ssl.PROTOCOL_SSLv二、ssl_version=ssl.PROTOCOL_SSLv2三、ssl_version=ssl.PROTOCOL_SSLv3。
get请求的话,就将httpsConn.request("POST", path, body, headers)中的"POST"换成"GET"就行了,而后body设置为None便可。
三、ssl创建的过程当中须要使用的证书(证书格式、证书生成、证书转换)、什么是服务器端/客户端校验?私钥公钥的概念
服务器端会有私钥和公钥,公钥会拿出来提供给客户端,在python的具体程序中,分别是key_file和cert_file,其中cert_file要提供给客户端。
python-cookbook中对创建ssl的链接的讲解见:http://python3-cookbook.readthedocs.io/zh_CN/latest/c11/p10_add_ssl_to_network_services.html :
如下是服务器端代码:
from socket import socket, AF_INET, SOCK_STREAM import ssl KEYFILE = 'server_key.pem' # Private key of the server CERTFILE = 'server_cert.pem' # Server certificate (given to client) def echo_client(s): while True: data = s.recv(8192) if data == b'': break s.send(data) s.close() print('Connection closed') def echo_server(address): s = socket(AF_INET, SOCK_STREAM) s.bind(address) s.listen(1) # Wrap with an SSL layer requiring client certs s_ssl = ssl.wrap_socket(s, keyfile=KEYFILE, certfile=CERTFILE, server_side=True ) # Wait for connections while True: try: c,a = s_ssl.accept() print('Got connection', c, a) echo_client(c) except Exception as e: print('{}: {}'.format(e.__class__.__name__, e)) echo_server(('', 20000))
以后是客户端链接服务器端的例子:
>>> from socket import socket, AF_INET, SOCK_STREAM >>> import ssl >>> s = socket(AF_INET, SOCK_STREAM) >>> s_ssl = ssl.wrap_socket(s, cert_reqs=ssl.CERT_REQUIRED, ca_certs = 'server_cert.pem') >>> s_ssl.connect(('localhost', 20000)) >>> s_ssl.send(b'Hello World?') 12 >>> s_ssl.recv(8192) b'Hello World?' >>>
备注:其中 ssl.wrap_socket(s,cert_reqs=ssl.CERT_REQUIRED,ca_certs = 'server_cert.pem') 的ca_certs就是须要在客户端指定的证书,这个是服务器给的公钥证书。
证书的格式:通常有der格式、pem格式,且格式不能单纯经过后缀名去进行断定,好比一个后缀名是crt,就认为其不是pem的格式是错误的。
证书转换:讲解证书转换的url地址:http://netkiller.github.io/cryptography/openssl/format.html
能够经过OpenSSL(OpenSSL的安装:http://blog.csdn.net/houjixin/article/details/25806151)来生成证书、以及进行证书的格式转换,好比将der转成pem格式,或者将pem转成der格式的。若是你不肯定你的证书的格式,能够将两种转换都尝试一下,由于若是本来就是pem格式的,但愿经过der转成pem格式的命令调用以后,会有错误产生。