python 请求服务器的实现代码(http请求和https请求)


Posted in Python onMay 25, 2018

一、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的配置:https://3water.com/article/140826.htm;具体的内容就是指:一台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成我们想要的格式

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

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

2、http请求端口、cookie,以及实现具体的get和post请求

http请求端口默认是80,如果不指定的话,默认走的就是80,否则就需要指定服务器端指定listen的端口。

cookie是什么?具体见:https://3water.com/article/140830.htm,  主要内容:有两个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的后续有详细使用教程会补充上来。

二、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的安装:https://3water.com/softjc/575021.html)来生成证书、以及进行证书的格式转换,比如将der转成pem格式,或者将pem转成der格式的。如果你不确定你的证书的格式,可以将两种转换都尝试一下,因为如果原本就是pem格式的,希望通过der转成pem格式的命令调用之后,会有错误产生。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python中条件选择和循环语句使用方法介绍
Mar 13 Python
Python中利用sqrt()方法进行平方根计算的教程
May 15 Python
python使用线程封装的一个简单定时器类实例
May 16 Python
编写自定义的Django模板加载器的简单示例
Jul 21 Python
剖析Python的Twisted框架的核心特性
May 25 Python
python中的字典操作及字典函数
Jan 03 Python
基于python批量处理dat文件及科学计算方法详解
May 08 Python
python实现机器学习之元线性回归
Sep 06 Python
python 实现屏幕录制示例
Dec 23 Python
Windows上安装tensorflow  详细教程(图文详解)
Feb 04 Python
python GUI库图形界面开发之PyQt5复选框控件QCheckBox详细使用方法与实例
Feb 28 Python
linux 下selenium chrome使用详解
Apr 02 Python
django将图片上传数据库后在前端显式的方法
May 25 #Python
python3.6.3+opencv3.3.0实现动态人脸捕获
May 25 #Python
Django1.9 加载通过ImageField上传的图片方法
May 25 #Python
python matplotlib 在指定的两个点之间连线方法
May 25 #Python
基于python OpenCV实现动态人脸检测
May 25 #Python
使用matplotlib画散点图的方法
May 25 #Python
python调用OpenCV实现人脸识别功能
May 25 #Python
You might like
php为什么选mysql作为数据库? Mysql 创建用户方法
2007/07/02 PHP
用PHP连接MySQL代码的参数说明
2008/06/07 PHP
PHP网页游戏学习之Xnova(ogame)源码解读(六)
2014/06/23 PHP
PHP fastcgi模式上传大文件(大约有300多K)报错
2014/09/28 PHP
PHP 中魔术常量的实例详解
2017/10/26 PHP
PHP排序二叉树基本功能实现方法示例
2018/05/26 PHP
javascript之ESC(第二类混淆)
2007/05/06 Javascript
Locate a File Using a File Open Dialog Box
2007/06/18 Javascript
js 加载时自动调整图片大小
2008/05/28 Javascript
细品javascript 寻址,闭包,对象模型和相关问题
2009/04/27 Javascript
js跨浏览器实现将字符串转化为xml对象的方法
2013/09/25 Javascript
JavaScript排序算法之希尔排序的2个实例
2014/04/04 Javascript
JS 新增Cookie 取cookie值 删除cookie 举例详解
2014/10/10 Javascript
JQuery复制DOM节点的方法
2015/06/11 Javascript
APP中javascript+css3实现下拉刷新效果
2016/01/27 Javascript
MUI 解决动态列表页图片懒加载再次加载不成功的bug问题
2017/04/13 Javascript
JavaScript引用类型RegExp基本用法详解
2018/08/09 Javascript
vue中promise的使用及异步请求数据的方法
2018/11/08 Javascript
vue实现广告栏上下滚动效果
2020/11/26 Vue.js
Python中的各种装饰器详解
2015/04/11 Python
Python的Flask框架中实现登录用户的个人资料和头像的教程
2015/04/20 Python
Python实现读取文件最后n行的方法
2017/02/23 Python
Python 装饰器使用详解
2017/07/29 Python
Python中enumerate函数代码解析
2017/10/31 Python
Python数据分析中Groupby用法之通过字典或Series进行分组的实例
2017/12/08 Python
一个月入门Python爬虫学习,轻松爬取大规模数据
2018/01/03 Python
详解flask表单提交的两种方式
2018/07/21 Python
对python的输出和输出格式详解
2018/12/08 Python
Scrapy-Redis结合POST请求获取数据的方法示例
2019/05/07 Python
TensorFlow tf.nn.conv2d_transpose是怎样实现反卷积的
2020/04/20 Python
Python如何实现定时器功能
2020/05/28 Python
Django视图、传参和forms验证操作
2020/07/15 Python
HTML5 video 视频标签使用介绍
2014/02/03 HTML / CSS
公关关系专员的自我评价分享
2013/11/20 职场文书
自信主题班会
2015/08/14 职场文书
企业愿景口号
2015/12/25 职场文书