zoukankan      html  css  js  c++  java
  • python——请求服务器(http请求和https请求)

    一、http请求

    1、http请求方式:get和post

    get一般用于获取/查询资源信息,在浏览器中直接输入url+请求参数点击enter之后连接成功服务器就能获取到的内容,post请求一般用于更新资源,通过form表单或者json、xml等其他形式提交给服务器端,然后等待服务器端给返回一个结果的方式(这个返回结果一般就是被修改之后的是否成功的状态,或者是修改后的最新数据table等)。

    http请求,不论是get还是post请求,都会包含几个部分,分别是header,cookie,get会有param,post会有body。

    这个可以通过fiddler里面抓包就可以拿到需要的Headers,一般需要设置的值可能有:

    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(备注:这里的信息也请自行抓到之后填写,不需要的话,可以不用填写)"

    针对正式环境和测试环境需要设置url的地址,以及Header的"Host"中的具体域名的方法如下:

    (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:Python26liburllib.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成我们想要的格式

    一般情况下常用的编码格式主要有:utf8、gbk、gb2312;在python26中默认的编码是ascii,但是在python3.x中默认的编码是utf-8

    后面再专门针对编码这块做一个大块的总结。

    2、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'{
      "args": {}, 
      "headers": {
        "Accept": "*/*", 
        "Accept-Encoding": "gzip, deflate", 
        "Host": "httpbin.org", 
        "User-Agent": "python-requests/2.9.1"
      }, 
      "origin": "218.30
    .116.9", 
      "url": "http://httpbin.org/get"
    }
    '

    post请求:

    >>> r = requests.post('http://httpbin.org/post', data={'key':'value'})
    >>> r
    <Response [200]>
    >>> r.text
    u'{
      "args": {}, 
      "data": "", 
      "files": {}, 
      "form": {
        "key": "value"
      }, 
      "headers": {
        "Accept": "*/*", 
        "Accept-Encoding": "gzip, deflate", 
        "Content-Length": "9"
    , 
        "Content-Type": "application/x-www-form-urlencoded", 
        "Host": "httpbin.org", 
        "User-Agent": "python-requests/2.9.1"
      }, 
      "json": null, 
      "origin": "218.30.116.185", 
      "url":
     "http://httpbin.org/post"
    }
    '

    我这里用的还是httplib的,request的后续有详细使用教程会补充上来。

    二、https请求

    1、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的请求方式加密处理。

    2、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_TLSv1、ssl_version=ssl.PROTOCOL_SSLv2、ssl_version=ssl.PROTOCOL_SSLv23、ssl_version=ssl.PROTOCOL_SSLv3。

    get请求的话,就将httpsConn.request("POST", path, body, headers)中的"POST"换成"GET"就好了,然后body设置为None即可。

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

     
  • 相关阅读:
    python 小爬虫
    动态规划,网易秋招
    leetcode 3
    leetcode 27 水
    leetcode 21 list merge
    leetcode 15 3sum & leetcode 18 4sum
    leetcode 389 map iterator 的使用
    [转]使用flask实现mock server
    python __str__repr__ 区别
    Robot Framework 源码阅读 day2 TestSuitBuilder
  • 原文地址:https://www.cnblogs.com/keke-xiaoxiami/p/5580021.html
Copyright © 2011-2022 走看看