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格式的命令調用以後,會有錯誤產生。