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 相关文章推荐
一个检测OpenSSL心脏出血漏洞的Python脚本分享
Apr 10 Python
python实现按任意键继续执行程序
Dec 30 Python
pandas中去除指定字符的实例
May 18 Python
使用Django启动命令行及执行脚本的方法
May 29 Python
python给微信好友定时推送消息的示例
Feb 20 Python
Python面向对象总结及类与正则表达式详解
Apr 18 Python
python利用tkinter实现屏保
Jul 30 Python
python快速排序的实现及运行时间比较
Nov 22 Python
解决Python 写文件报错TypeError的问题
Oct 23 Python
python使用matplotlib的savefig保存时图片保存不完整的问题
Jan 08 Python
Python还能这么玩之用Python修改了班花的开机密码
Jun 04 Python
python3操作redis实现List列表实例
Aug 04 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分页函数示例代码分享
2014/02/24 PHP
Zend Framework教程之前端控制器Zend_Controller_Front用法详解
2016/03/07 PHP
PHP创建word文档的方法(平台无关)
2016/03/29 PHP
PHP生成图像验证码的方法小结(2种方法)
2016/07/18 PHP
JavaScipt基本教程之JavaScript语言的基础
2008/01/16 Javascript
JavaScript 学习初步 入门教程
2010/03/25 Javascript
javascript cookie操作类的实现代码小结附使用方法
2010/06/02 Javascript
fancybox1.3.1 基于Jquery的插件在IE中图片显示问题
2010/10/01 Javascript
js常用代码段收集
2011/10/28 Javascript
js获取多个tagname的节点数组
2013/09/22 Javascript
Jquery实现自定义窗口随意的拖拽
2014/03/12 Javascript
javascript框架设计读书笔记之种子模块
2014/12/02 Javascript
JavaScript中实现无缝滚动、分享到侧边栏实例代码
2016/04/06 Javascript
JQuery之proxy实现绑定代理方法
2016/08/01 Javascript
浅谈原生JS实现jQuery的animate()动画示例
2017/03/08 Javascript
JavaScript中undefined和null的区别
2017/05/03 Javascript
Nodejs实现多房间简易聊天室功能
2017/06/20 NodeJs
JavaScript实现简单图片轮播效果
2017/08/21 Javascript
[23:18]Spirit vs Liquid Supermajor小组赛A组 BO3 第二场 6.2
2018/06/03 DOTA
python计算书页码的统计数字问题实例
2014/09/26 Python
Python实现好友全头像的拼接实例(推荐)
2017/06/24 Python
Python设计模式之迭代器模式原理与用法实例分析
2019/01/10 Python
解决python2 绘图title,xlabel,ylabel出现中文乱码的问题
2019/01/29 Python
Python高级特性——详解多维数组切片(Slice)
2019/11/26 Python
从训练好的tensorflow模型中打印训练变量实例
2020/01/20 Python
Python自动化操作实现图例绘制
2020/07/09 Python
CSS3实现可爱的小黄人动画
2016/07/11 HTML / CSS
IE10 Error.stack 让脚本调试更加方便快捷
2013/04/22 HTML / CSS
美国肌肉和力量商店:Muscle & Strength
2019/06/22 全球购物
考试作弊被抓检讨书
2014/01/10 职场文书
岗位廉洁从业承诺书
2014/03/28 职场文书
服务标兵事迹材料
2014/05/04 职场文书
法学专业毕业生求职信
2014/06/12 职场文书
十八大标语口号
2014/10/09 职场文书
2015年度团总支工作总结
2015/04/23 职场文书
大学生如何逃脱“毕业季创业队即散伙”魔咒?
2019/08/19 职场文书