溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

python中如何實(shí)現(xiàn)請(qǐng)求服務(wù)器

發(fā)布時(shí)間:2021-08-07 09:38:18 來源:億速云 閱讀:111 作者:小新 欄目:開發(fā)技術(shù)

這篇文章主要介紹了python中如何實(shí)現(xiàn)請(qǐng)求服務(wù)器,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

一、http請(qǐng)求

1、http請(qǐng)求方式:get和post

get一般用于獲取/查詢資源信息,在瀏覽器中直接輸入url+請(qǐng)求參數(shù)點(diǎn)擊enter之后連接成功服務(wù)器就能獲取到的內(nèi)容,post請(qǐng)求一般用于更新資源,通過form表單或者json、xml等其他形式提交給服務(wù)器端,然后等待服務(wù)器端給返回一個(gè)結(jié)果的方式(這個(gè)返回結(jié)果一般就是被修改之后的是否成功的狀態(tài),或者是修改后的最新數(shù)據(jù)table等)。

http請(qǐng)求,不論是get還是post請(qǐng)求,都會(huì)包含幾個(gè)部分,分別是header,cookie,get會(huì)有param,post會(huì)有body。

這個(gè)可以通過fiddler里面抓包就可以拿到需要的Headers,一般需要設(shè)置的值可能有:

header = {
 "Host": "x.x.#",
 "Authorization": "Basic: someValue",
 "Content-Type": r"application/json",
 "Connection": "keep-alive",
 "Proxy-Connection": "keep-alive",
 "Cookie": "xxxxxxxxx(備注:這里的具體值請(qǐng)自行填寫,其他key對(duì)應(yīng)的值也是一樣)",
 "User-Agent": "360xxxxxx(備注:這里的信息也請(qǐng)自行抓到之后填寫,不需要的話,可以不用填寫)"
 }

針對(duì)正式環(huán)境和測(cè)試環(huán)境需要設(shè)置url的地址,以及Header的"Host"中的具體域名的方法如下:

(1)正式環(huán)境:url中的host也設(shè)置成域名,比如:http://%s/search/searchList的%s就替換成 域名,在headers中的"HOST"的鍵對(duì)應(yīng)的value也是域名,比如說都是"x.y.#"

(2)測(cè)試環(huán)境: url中的host設(shè)置成具體的IP,比如:http://%s/search/searchList的%s就替換成 10.108.225.234這樣的具體IP(備注,這個(gè)IP就是你們平時(shí)開發(fā)上測(cè)試代碼的機(jī)器),但是headers中的"HOST"的鍵對(duì)應(yīng)的value必須得寫成域名,比如"x.y.#" 

原因:因?yàn)橐粋€(gè)IP地址對(duì)應(yīng)的服務(wù)器上可能會(huì)有多個(gè)域名,因?yàn)榭赡軙?huì)上多個(gè)不同業(yè)務(wù)的服務(wù)器代碼,如此會(huì)有一個(gè)默認(rèn)的域名,但是并不一定是你的這個(gè)業(yè)務(wù)對(duì)應(yīng)的域名,所以一定要在headers中的"HOST"中指定域名才可以找到這個(gè)域名,從而找到其對(duì)應(yīng)的接口,進(jìn)行正確的調(diào)用。

進(jìn)一步,對(duì)于一個(gè)IP地址對(duì)應(yīng)的服務(wù)器,其上會(huì)有很多域名,這個(gè)是如何部署的呢?需要問一下服務(wù)器端的同學(xué),比如說會(huì)有x.#和x.y.#,這個(gè)是如何進(jìn)行配置的呢?具體原因是使用了nginx的配置:https://www.jb51.net/article/140826.htm;具體的內(nèi)容就是指:一臺(tái)nginx服務(wù)器多域名配置,然后客戶端請(qǐng)求的時(shí)候,就能自動(dòng)根據(jù)這個(gè)host找到對(duì)應(yīng)的文件目錄,然后找到對(duì)應(yīng)處理方法,這個(gè)后續(xù)要再詳細(xì)了解一下。

cookie信息都是在headers里面的"Cookie"鍵對(duì)應(yīng)的value后面,這個(gè)可以通過日志或者抓包得到,注意,抓到的信息一定要原封不動(dòng)的全部拿來用。

另外,這個(gè)cookie信息也可以通過其他方式獲取,比如說,通過登錄接口拿到cookie信息,再將cookie信息設(shè)置到后續(xù)需要的"Cookie"中。

具體的body的值,需要跟服務(wù)器端開發(fā)對(duì)應(yīng)一下數(shù)據(jù)的加密方式,目前比較多的都是通過json格式的,需要確認(rèn)的是幾層json,比如我們的開發(fā)同學(xué)搞了兩層json,導(dǎo)致我剛開始的時(shí)候就在最外面搞了一層json轉(zhuǎn)換格式,結(jié)果請(qǐng)求的時(shí)候一直提示Resopnse 200,但是返回的errorMsg一直是錯(cuò)誤請(qǐng)求。(備注:首先需要確認(rèn)Response的Status是200的話,就說明已經(jīng)跟服務(wù)器端連接上了,然后如果拿不到正確的數(shù)據(jù),那就要分析是你的數(shù)據(jù)傳送格式不正確,還是缺少了哪些內(nèi)容,導(dǎo)致服務(wù)器端解析不出,或者無(wú)法給出你想要的內(nèi)容)

一般的get請(qǐng)求的格式,一個(gè)參數(shù)的可能是這樣的:http://xxx/search/YYYY?&kw=123456789,如果是多個(gè)參數(shù)的話:http://music.baidu.com/search?fr=ps&ie=utf-8&key=%E7%9C%8B%E8%A7%81%E4%BA%86,比如像百度音樂的這個(gè)url,在?后面都可以添加一個(gè)&,然后url其實(shí)也可以變成這樣的格式:http://music.baidu.com/search?&fr=ps&ie=utf-8&key=%E7%9C%8B%E8%A7%81%E4%BA%86,但是實(shí)際上訪問get到的都是相同的內(nèi)容,也就是說服務(wù)器端解析的時(shí)候,返回的結(jié)果都是相同的內(nèi)容;多個(gè)參數(shù),就每個(gè)參數(shù)之間加一個(gè)&鏈接起來,但是注意,有些值傳的時(shí)候可能需要進(jìn)行urlencode編碼,并且一定要在跟服務(wù)器端相同的編碼的基礎(chǔ)上進(jìn)行urlencode編碼(我自己碰到的坑:我的python程序用的編碼方式是:gbk,我們服務(wù)器端的編碼方式是utf-8,我最開始的時(shí)候,直接對(duì)中文進(jìn)行了urlencode編碼,但是得到的結(jié)果不是想要的,最后才發(fā)現(xiàn)原來我urlencode之后的碼與服務(wù)器端urlencode之后的碼不同,所以當(dāng)然解不出了,那么就decode('gbk').encode('utf-8'),然后得到的內(nèi)容再urlencode,之后才正確。。。所以都是坑)

備注1:需要了解一下get請(qǐng)求在服務(wù)器端是怎么處理的?post請(qǐng)求在服務(wù)器端又是如何處理的?這個(gè)需要另開一篇博客專門寫一下。

備注2:關(guān)于編碼方式,以及幾種編碼方式的轉(zhuǎn)換(編碼解碼等),進(jìn)行urlencode的具體方法,在python26的urllib中有urlencode方法,只能對(duì)dict進(jìn)行編碼,如果只是對(duì)字符串進(jìn)行編碼,需要使用urllib.quote()方法

比如:

>>> import urllib
>>> xx = {'kw': '達(dá)達(dá)'}
>>> urllib.urlencode(xx)
'kw=%B4%EF%B4%EF'
>>> ss =
 File "<stdin>", line 1
 ss =
 ^
SyntaxError: invalid syntax
>>>
>>> ss = '達(dá)達(dá)'
>>> 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'

查看當(dāng)前處于什么編碼格式:

>>> import sys
>>> sys.getdefaultencoding()
'ascii'

編碼及解碼:

在python中使用decode和encode進(jìn)行編碼和解碼,比如我們get到的str類型是gbk的,那就可以str.decode(''gbk'),之后再encode成我們想要的格式

一般情況下常用的編碼格式主要有:utf8、gbk、gb2312;在python26中默認(rèn)的編碼是ascii,但是在python3.x中默認(rèn)的編碼是utf-8

后面再專門針對(duì)編碼這塊做一個(gè)大塊的總結(jié)。

2、http請(qǐng)求端口、cookie,以及實(shí)現(xiàn)具體的get和post請(qǐng)求

http請(qǐng)求端口默認(rèn)是80,如果不指定的話,默認(rèn)走的就是80,否則就需要指定服務(wù)器端指定listen的端口。

cookie是什么?具體見:https://www.jb51.net/article/140830.htm,  主要內(nèi)容:有兩個(gè)Http頭部和Cookie有關(guān):Set-Cookie和Cookie。Set-Cookie由服務(wù)器發(fā)送,它包含在響應(yīng)請(qǐng)求的頭部中。它用于在客戶端創(chuàng)建一個(gè)Cookie。Cookie頭由客戶端發(fā)送,包含在HTTP請(qǐng)求的頭部中。注意,只有cookie的domain和path與請(qǐng)求的URL匹配才會(huì)發(fā)送這個(gè)cookie。

(1)httplib庫(kù)——HTTP protocol client

切記:要從用戶手冊(cè)中學(xué)習(xí)!

httplib在python3.0中已經(jīng)更名為http.client了。

class  httplib.HTTPConnection(host[,port[,strict[,timeout]]])

class  httplib.HTTPSConnection(host[,port[,key_file[,cert_file[,strict[,timeout]]]]])          ——這是HTTPConnection的一個(gè)子類,使用了SSL,用來跟安全服務(wù)器進(jìn)行通信。默認(rèn)的端口是443。key_file是一個(gè)pem格式的包含了密鑰的文件,cert_file是一個(gè)pem格式的證書鏈文件。

然后這個(gè)httplib的HttpConnection的類調(diào)用之后,能夠得到一個(gè)HTTPConnection的instance,就是一個(gè)HTTPConnection或者HTTPSConnection的一個(gè)對(duì)象,比如設(shè)置其名稱為conn,之后利用這個(gè)conn的對(duì)象就可以繼續(xù)走request(method,url[,body[,headers]])的請(qǐng)求,調(diào)用request方法之后,繼續(xù)調(diào)用conn.getresponse(),然后返回一個(gè)HTTPResponse的實(shí)例對(duì)象,例如為res,然后調(diào)用res.getheaders()方法獲取response的頭部,得到的一個(gè)(header,value)的tuple,通過res.status就可以得到狀態(tài)(200為OK,連接上的含義),res.read()就可以得到response的body信息,然后自己再針對(duì)body信息的類型,比如是json,就解析出來顯示即可。

具體的使用例子用戶手冊(cè)中也說明了:

>>> 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.#")
>>> conn2.request("GET", "/standard.html")
>>> r3 = conn2.getresponse()
>>> print r3.status
200
>>> data = r3.read()
>>> print data
<!Doctype html><html lang="zh-CN"><head>.......

 以上例子中,先用的是用戶手冊(cè)的example中的例子,但是因?yàn)閣ww.python.org被永久轉(zhuǎn)移,所以返回的結(jié)果如上;所以選擇了"jia.#"的url,之后request中請(qǐng)求的是標(biāo)準(zhǔn)版攝像機(jī)的頁(yè)面,即"/standard.html",之后就能夠得到r3的結(jié)果,為200,說明連接OK了,之后就能通過r3.read()得到body的內(nèi)容,通過r3.getheaders()就能獲取到header的內(nèi)容。

以上都是request方法中都是"GET"方法,換成"POST"需要傳的內(nèi)容會(huì)有一些差別,如下:

>>> 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()

備注:以上代碼也是運(yùn)行不通過的,因?yàn)槭潜容^久遠(yuǎn)的python版本的例子,主要需要注意的是:需要自己設(shè)置headers,在其中根據(jù)需要傳遞Cookie、Content-Type、Accept等信息,通過key-value的形式傳遞,具體的body中傳遞的信息,要注意是json格式的,還是通過urlencode編碼等,格式一定要跟開發(fā)溝通清楚,否則會(huì)有錯(cuò)誤請(qǐng)求的問題,之后得到response,并獲取response的status、body、headers就與前面的"GET"method一樣了。

(2)request庫(kù)

request庫(kù)是python的第三方庫(kù),官方文檔地址:http://www.python-requests.org/en/master/user/quickstart/#make-a-request

get請(qǐng)求:

>>> 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請(qǐng)求:

>>> 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的后續(xù)有詳細(xì)使用教程會(huì)補(bǔ)充上來。

二、https請(qǐng)求

1、https的請(qǐng)求方式:get和post

http和https的區(qū)別:

(1)url的前面是https://而不是http://,使用ssl進(jìn)行加密/身份認(rèn)證,并且http的默認(rèn)端口是80,https的默認(rèn)端口是443。

(2)因?yàn)橛衧sl的認(rèn)證和加密,所以具體的底層的通信過程中會(huì)有不同,https的這一層在建立連接的時(shí)候,需要設(shè)置socket屬性,socket屬性的生成需要使用具體的方法調(diào)用,方法調(diào)用的參數(shù)需要指定:ca_certs=服務(wù)器端給提供的公鑰證書即可。

然后如果還有客戶端認(rèn)證的話,那客戶端也可以提供出自己的key_file,cert_file。

什么是ssl?

ssl的全稱是(Secure Sockets Layer)安全套接層,另外還有TLS(Transport Layer Secure,傳輸層安全),這兩種協(xié)議都是為網(wǎng)絡(luò)提供安全和數(shù)據(jù)完整性的一種安全協(xié)議,在傳輸層對(duì)網(wǎng)絡(luò)連接進(jìn)行加密。

為什么要用這個(gè)?

防止數(shù)據(jù)以及網(wǎng)絡(luò)連接的傳輸內(nèi)容被截獲,所以涉及到個(gè)人或者重要的信息等,都需要進(jìn)行建立ssl連接,通過https的請(qǐng)求方式加密處理。

2、https請(qǐng)求端口、ssl建立,以及實(shí)現(xiàn)具體的get和post請(qǐng)求

post請(qǐng)求:

 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

備注:

因?yàn)槭强蛻舳俗C書,所以沒有使用注釋的代碼:#self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, ssl_version=ssl.PROTOCOL_SSLv3),這個(gè)程序中需要指定客戶端的私鑰密鑰的文件,如果只有服務(wù)器端有私鑰,客戶端有公鑰,則客戶端的程序需要指定公鑰文件,見代碼:httpsConn.sock = ssl.wrap_socket(sock, ca_certs=CERT_FILE, cert_reqs=ssl.CERT_REQUIRED, ssl_version=ssl.PROTOCOL_SSLv3),是通過ca_certs參數(shù)指定的,CERT_FILE是文件的路徑,保證能夠找到即可;如果是是一個(gè)文件夾下有多個(gè)文件,然后這多個(gè)文件都是需要用到的,比如A域名的證書和B域名的證書,A服務(wù)器在對(duì)接口處理請(qǐng)求的時(shí)候,會(huì)向B端發(fā)請(qǐng)求,如此客戶端需要將A域名證書和B域名證書都添加進(jìn)來,所以只要把文件夾路徑設(shè)置成ca_certs參數(shù)的值即可。

另外,如果不確定SSL的版本,則需要嘗試多個(gè)不同的SSL版本:ssl.PROTOCOL_TLSv1、ssl_version=ssl.PROTOCOL_SSLv2、ssl_version=ssl.PROTOCOL_SSLv23、ssl_version=ssl.PROTOCOL_SSLv3。

get請(qǐng)求的話,就將httpsConn.request("POST", path, body, headers)中的"POST"換成"GET"就好了,然后body設(shè)置為None即可。

3、ssl建立的過程中需要使用的證書(證書格式、證書生成、證書轉(zhuǎn)換)、什么是服務(wù)器端/客戶端校驗(yàn)?私鑰公鑰的概念

服務(wù)器端會(huì)有私鑰和公鑰,公鑰會(huì)拿出來提供給客戶端,在python的具體程序中,分別是key_file和cert_file,其中cert_file要提供給客戶端。

python-cookbook中對(duì)建立ssl的連接的講解見:http://python3-cookbook.readthedocs.io/zh_CN/latest/c11/p10_add_ssl_to_network_services.html :

以下是服務(wù)器端代碼:

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))

之后是客戶端連接服務(wù)器端的例子:

>>> 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就是需要在客戶端指定的證書,這個(gè)是服務(wù)器給的公鑰證書。

證書的格式:一般有der格式、pem格式,且格式不能單純通過后綴名去進(jìn)行判定,比如一個(gè)后綴名是crt,就認(rèn)為其不是pem的格式是錯(cuò)誤的。

證書轉(zhuǎn)換:講解證書轉(zhuǎn)換的url地址:http://netkiller.github.io/cryptography/openssl/format.html

可以通過OpenSSL來生成證書、以及進(jìn)行證書的格式轉(zhuǎn)換,比如將der轉(zhuǎn)成pem格式,或者將pem轉(zhuǎn)成der格式的。如果你不確定你的證書的格式,可以將兩種轉(zhuǎn)換都嘗試一下,因?yàn)槿绻揪褪莗em格式的,希望通過der轉(zhuǎn)成pem格式的命令調(diào)用之后,會(huì)有錯(cuò)誤產(chǎn)生。

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“python中如何實(shí)現(xiàn)請(qǐng)求服務(wù)器”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI