python——请求服务器(http请求和https请求)

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格式的命令调用以后,会有错误产生。

相关文章
相关标签/搜索