python如何實現(xiàn)API的調用詳解
前言
在日常工作中,可能需要結合網(wǎng)上現(xiàn)在的一些API或者公司提供的數(shù)據(jù)接口來得到相應的數(shù)據(jù)或者實現(xiàn)對應的功能。
因此API的調用和數(shù)據(jù)接口的訪問都是做數(shù)據(jù)分析的一個常用操作,如何快速實現(xiàn)API和數(shù)據(jù)接口的調用,網(wǎng)上一般提供很多語言版本,但追根溯源采取的方式都是利用HTTP請求的方式來進行實現(xiàn)的。由于本人常用Python,本篇文章也主要采用Python來作為工具來進行介紹如何操作。
API
API:簡單來說就是一組協(xié)議、一個工具或者一組規(guī)則,定義不同應用程序之間的通信方法,把具體實現(xiàn)的過程隱藏起來,只暴露必須調用的部分給開發(fā)者使用。
上面的定義比較官方,這里簡單舉個例子來說明一下,比如在現(xiàn)在外面的麥當勞等快餐店都采取手機線上下單,前臺拿餐的過程。而在這個過程,作為消費者通常在手機上選好對應的餐品然后點擊下單付款,然后等待前臺叫號拿餐即可。而這個過程具體怎么實現(xiàn)的我們并不知道,整個過程有對應的app或者小程序通信到廚房數(shù)據(jù),然后廚師制作出餐。而這個APP和小程序就充當對應的API功能。
舉一個簡單的例子,一個社交平臺它每天收到各種語言的評論信息,而作為對應的分析人員面對復雜的語言數(shù)據(jù)處理,是一個大難題,有人可能說開發(fā)一個模型來實現(xiàn)翻譯整合功能,這個方法雖然聽起來可行但是成本代價高,其次為了解決一個問題,而又去開發(fā)一個比較困難的問題。這個與原本的目標偏離越來越遠,這個時候就可以借助國內比較成熟的翻譯平臺API,直接處理現(xiàn)有的數(shù)據(jù)。這樣相對而言成本代價小,更便捷也更能快速實現(xiàn)現(xiàn)有的目標。而API的作用在這里就毋庸置疑了。
數(shù)據(jù)接口
數(shù)據(jù)接口: 簡單來說就是一組封裝的數(shù)據(jù)集口令,就是按照對應的規(guī)則發(fā)送相應的參數(shù),然后返回對應的相關數(shù)據(jù)信息。API的調用和數(shù)據(jù)接口的這兩個在日常調用時很類似的,相對而言API的范圍更寬廣,實現(xiàn)的功能也比較多,而數(shù)據(jù)接口日常充當?shù)木褪且粋€取數(shù)工具比較多。
就比如說大型電商公司公司一般用統(tǒng)一的SKU來對商品進行管理,而比如這家公司是作為一個品牌商,它會在不同平臺上面進行售賣,而在這些平臺上面映射的商品標識ID就不同于公司的SKU。因為公司的SKU不僅基于商品而且還考慮各個地方倉庫以及產(chǎn)品的各個型號,而這個映射相對而言就比較復雜。
而在處理不同平臺的數(shù)據(jù)人員一般也不能直接使用公司的數(shù)據(jù)庫來對商品進行分析,因為顆粒度太細,分析起來比較復雜困難,這個時候就可以根據(jù)對應功能的要求讓開發(fā)在現(xiàn)有系統(tǒng)是開發(fā)一個單獨的數(shù)據(jù)接口提供相應的公司,避免直接請求數(shù)據(jù)庫過程復雜等相應信息。但數(shù)據(jù)接口相對實時的數(shù)據(jù)庫存在一定的延遲。
API的調用和數(shù)據(jù)接口的調用
API和數(shù)據(jù)接口通過前面的舉例論述,大致理解起來也比較簡單,而具體怎么實現(xiàn)API的調用和數(shù)據(jù)接口的調用這里簡單介紹一下。
簡單來說API的調用和接口的調用都是類似一個HTTP請求,而調用最主要就是根據(jù)相應的規(guī)則將請求方式、請求頭、URL、以及請求體封裝好然后發(fā)送請求,就可以實現(xiàn)相應的調用。
但數(shù)據(jù)接口和API兩個的調用相比較而言來,一般數(shù)據(jù)接口比較簡單,很多情況下數(shù)據(jù)接口是在公司內網(wǎng)數(shù)據(jù)訪問所以請求信息比較簡單,而API大多是第三方企業(yè)開發(fā)對外的服務屬于一種商業(yè)服務,相對而言為了保證請求的安全,考慮的更為全面,加入了AK、SK、簽名、時間戳等信息比較復雜。
而追本溯源這兩個調用都是類似HTTP請求,具體調用大致差不多,主要就是API調用中包含的請求參數(shù)的信息更多。而具體怎么實現(xiàn)下面將簡單的介紹一下。
調用的基礎-請求方法
一般而言,常見的HTTP請求調用方式有很多,這方面的資源比較多,可以網(wǎng)上自己查閱,這里就簡單說說常見的兩種請求方法。
GET 請求
GET請求簡單來說就是從服務器上獲取資源,可以載入到瀏覽器的緩存中。
POST 請求
POST請求一般而言以表單形式向服務器發(fā)送請求,請求參數(shù)包含在請求體當中可能導致資源的創(chuàng)建和改變。POST請求的信息不能緩存在瀏覽器中。
這兩個請求方法說起來很簡單,但最重要的一點就是了解這兩種請求的區(qū)別,從而為接口的設計和API的使用更加熟悉。
GET和POST請求的區(qū)別
1.GET請求請求長度最多1024kb,POST對請求數(shù)據(jù)沒有限制。這一點原因是很多時候GET請求把對應的信息放在URL中,而URL的長度有限,導致GET請求的長度也受到一定的限制。而POST請求相應的參數(shù)信息放在請求體body中所以一般不受長度限制。
2.POST請求比GET更安全一些,因為GET請求中URL包含了相應的信息,頁面會被瀏覽器緩存,其他人可以看到相應的信息。
3.GET產(chǎn)生一個TCP數(shù)據(jù)包,POST產(chǎn)生兩個TCP數(shù)據(jù)包。
GET請求的時候將header、data一起發(fā)送出去,然后服務器響應返回200。而POST則是先發(fā)送header,等待服務器響應100,然后發(fā)送data,最后服務器響應返回200.但在這里注意,POST請求分為兩次,但是請求體body是緊隨在header之后發(fā)送的,所以這之間時間可以微乎不計。
4.GET請求只支持URL編碼,而POST相對而言有多種編碼方式。
5.GET請求參數(shù)是通過URL傳遞的,多個參數(shù)以&連接,POST請求放在request body中。
6.GET請求只支持ASCII字符,而POST沒有限制。
一般而言瀏覽器輸入網(wǎng)址可以直接訪問的一般是GET請求。
Python實現(xiàn)GET請求和POST請求
上面大篇幅的介紹了一些數(shù)據(jù)接口、API相關知識以及請求方法,使用起來比較簡單,下面可以大致熟悉一下相應的請求方式。一般直接使用Python的request庫就可以。
GET請求
import request # GET請求發(fā)送的參數(shù)一定要是字典的形式,可以發(fā)送多個參數(shù)。 # 發(fā)送格式:{'key1':value1', 'key2':'value2', 'key3', 'value3'} # 樣例不能運行 url ='http://www.xxxxx.com' params = {'user':'lixue','password':111112333} requests.get(url,data = parms)
POST請求
POST請求一般有三種提交形式:application/x-www-form-urlencoded、multipart/form-data、application/json.
具體查看是三種的哪一種請求方式:谷歌瀏覽器檢查 → Network →選擇加載文件 → Headers → Reuqest Headers → Content-Type
具體編碼方式為下面三種,可以了解具體的請求實現(xiàn),一般公司內部的數(shù)據(jù)接口設置了局域網(wǎng)所以有的可以不需要加header。
POST請求的三種提交形式
1.最常見的post提交數(shù)據(jù)以form表單為主:application/x-www-form-urlencoded
import request data={'k1':'v1','k2':'v2'} headers= {'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36'} requests.post(url,headers = headers,data=data)
2.以json格式提交數(shù)據(jù):application/json
data = {'user':'lixue','password':12233} data_json = json.dumps(params) requests.post(url,headers = headers,data = data_json)
3.一般用來傳文件(爬蟲很少用到):multipart/form-data
files = {'files':open('c://xxx.txt','rb')} requests.post(url = url,headers = headers,files = files)
一個簡單API請求的實例
通過上面簡單的介紹,對于具體請求大致了解,這里搜集了一個簡單的API聚合中心,提供了很多挺好用的功能。下面以這個簡單API的作一個簡單的示范API地址。
這個小實例采取的是天氣API接口獲取近15天的天氣。在使用這個API之前記得獲取相應的apiKey和查看具體的使用文檔。這個API網(wǎng)站一般對相應API提供一定的免費次數(shù),可以充當學習使用,而且支持GET和POST請求。剛好可以適合練手。
GET請求
params = { "apiKey":'換成你的apikey', "area":'武漢市', } url = 'https://api.apishop.net/common/weather/get15DaysWeatherByArea' response = requests.get(url,params) print(response.text)
POST請求
這里的POST請求也就是對應上面的最常見的post提交數(shù)據(jù)以form表單為主:application/x-www-form-urlencoded
url = 'https://api.apishop.net/common/weather/get15DaysWeatherByArea' params = { "apiKey":'換成你的apikey', "area":'武漢市武昌區(qū)', } response = requests.post(url,params) print(response.text)
在調用這種API接口一般都需要進行一個狀態(tài)碼等返回信息測試,檢查請求是否正常,可以按照下面的給出參考。
params = { "apiKey":'換成你的apikey, "area":'武漢市', } url = 'https://api.apishop.net/common/weather/get15DaysWeatherByArea' response = requests.post(url,params) print(response.text) if response.status_code != 200: raise ConnectionError(f'{url} status code is {response.status_code}.') response = json.loads(response.content) if 'desc' not in response.keys(): raise ValueError(f'{url} miss key msg.') if response['desc'] != '請求成功': print(11)
數(shù)據(jù)提取
其實API的調用很簡單,但其中最核心的其實是返回信息中數(shù)據(jù)的抽取,一般而言返回的信息都是json形式,需要從中用字典鍵值對方式提取數(shù)據(jù),下面這塊根據(jù)請求的數(shù)據(jù)返回相應的信息并提取出來,獲取信息后面將會展示。
import requests import pandas as pd import numpy as np import json def get_url(area): url = 'https://api.apishop.net/common/weather/get15DaysWeatherByArea' params = { "apiKey":'換成你的apikey', "area":area, } response = requests.get(url,params) if response.status_code != 200: raise ConnectionError(f'{url} status code is {response.status_code}.') response = json.loads(response.content) if 'desc' not in response.keys(): raise ValueError(f'{url} miss key msg.') if response['desc'] != '請求成功': print(11) return response def extract_data(web_data): data= web_data['result']['dayList'] weather_data = pd.DataFrame(columns = ['city','daytime','day_weather','day_air_temperature','day_wind_direction','day_wind_power', 'night_weather','night_air_temperature','night_wind_direction','night_wind_power']) for i in range(len(data)): city = data[i]["area"] daytime = data[i]["daytime"] daytime = daytime[:4]+'-'+daytime[4:6]+'-'+daytime[-2:] day_weather = data[i]["day_weather"] day_air_temperature = data[i]['day_air_temperature'] day_wind_direction = data[i]["day_wind_direction"] day_wind_power = data[i]['day_wind_power'] night_weather = data[i]['night_weather'] night_air_temperature = data[i]["night_air_temperature"] night_wind_direction = data[i]['night_wind_direction'] night_wind_power = data[i]["night_wind_power"] c = {"city": city,"daytime": daytime,"day_weather":day_weather,"day_air_temperature":day_air_temperature, "day_wind_direction":day_wind_direction,"day_wind_power":day_wind_power,"night_weather":night_weather, "night_air_temperature":night_air_temperature,"night_wind_direction":night_wind_direction, "night_wind_power":night_wind_power} weather_data = weather_data.append(c,ignore_index = True) weather_data.to_excel(r"C:\Users\zhangfeng\Desktop\最近十五天天氣.xlsx",index = None) return weather_data if __name__ == '__main__': print("請輸入對應的城市") web_data = get_url(input()) weather_data = extract_data(web_data)
部分結果如下圖:
數(shù)據(jù)接口實例
在日常學習中數(shù)據(jù)接口的使用可能相對較少,數(shù)據(jù)接口的應用場景大多數(shù)情況下是應用在公司內部調取數(shù)據(jù)的情況下比較多,所以一般很少見到,這里展示工作中遇到的兩個數(shù)據(jù)接口的使用,由于工作考慮,展示的代碼屬于樣例,并不能調用??梢詤⒖家幌抡{用實現(xiàn)以及規(guī)范。
POST請求調用數(shù)據(jù)接口
# 銷售狀態(tài)查詢 def id_status(id_dir): id_data = pd.read_excel(id_dir,sheet_name="Sheet1") id_data.columns = ['shop', 'Campaign Name','Ad Group Name','Item Id'] # 方便后期處理更改列名 id_data["Item Id"] = id_data["Item Id"].astype(str) id_list = list(id_data['Item Id']) print(len(id_list)) id_list = ','.join(id_list) if isinstance(id_list, int): id_list = str(id_list) id1 = id_list.strip().replace(',', ',').replace(' ', '') request_url = "http://xxx.com" # 通過item_id查詢id狀態(tài) params = { "item_id":id1, } data_json = json.dumps(params) # 屬于POST第二種請求方式 response = requests.post(request_url, data = data_json) print(response.text) if response.status_code != 200: raise ConnectionError(f'{request_url} status code is {response.status_code}.') response = json.loads(response.content) if 'message' not in response.keys(): raise ValueError(f'{request_url} miss key msg.') if response['message'] != 'ok': print(11) data= response['result'] ad_data = pd.DataFrame(columns = ['Item Id','saleStatusName']) for j in range(len(data)): item_id =data[j]["item_id"] saleStatusName = data[j]['saleStatusName'] c = {"Item Id": item_id, "saleStatusName": saleStatusName, } ad_data = ad_data.append(c,ignore_index = True) total_data = pd.merge(ad_data,id_data,on ='Item Id', how ='left') df_column = ['shop', 'Campaign Name','Ad Group Name','Item Id','saleStatusName'] total_data = total_data.reindex(columns=df_column) return total_data
GET請求調用數(shù)據(jù)接口
### 庫存數(shù)據(jù)查詢 def Smart_investment_treasure(investment_dir): product_data = pd.read_excel(investment_dir,sheet_name="product") if len(product_data)>0: product_data['商品ID']=product_data['商品ID'].astype(str) product_list=list(product_data['商品ID']) product_id = ','.join(product_list) else: product_id='沒有數(shù)據(jù)' return product_id def stock_query(investment_dir): product_data = pd.read_excel(investment_dir,sheet_name="product") if len(product_data)>0: product_data['商品ID']=product_data['商品ID'].astype(str) product_list=list(product_data['商品ID']) product_id = ','.join(product_list) else: product_id='沒有數(shù)據(jù)' if isinstance(product_id, int): product_id = str(id) product_id = product_id.strip().replace(',', ',').replace(' ', '') request_url = "http://xxx.com" # 通過ali_sku查詢erpsku params = { "product_id":product_id, } response = requests.get(request_url, params) #屬于GET請求 if response.status_code != 200: raise ConnectionError(f'{request_url} status code is {response.status_code}.') response = json.loads(response.content) if 'msg' not in response.keys(): raise ValueError(f'{request_url} miss key msg.') if response['msg'] != 'success': print(11) data= response['data']['data'] # requestProductId = id.split(',') id_state=[] overseas_stock=[] china_stock=[] id_list=[] for j in range(len(data)): inventory_data= data[j]['list'] overseas_inventory=0 ep_sku_list=[] sea_test=0 china_inventory=0 test="paused" id_test="" id_test=data[j]['product_id'] for i in range(len(inventory_data)): if inventory_data[i]["simple_code"] in ["FR","DE","PL","CZ","RU"] and inventory_data[i]["erp_sku"] not in ep_sku_list: overseas_inventory+=inventory_data[i]["ipm_sku_stock"] ep_sku_list.append(inventory_data[i]["erp_sku"]) sea_test=1 elif inventory_data[i]["simple_code"] == 'CN': china_inventory+=int(inventory_data[i]["ipm_sku_stock"]) if overseas_inventory>30: test="open" elif overseas_inventory==0 and china_inventory>100: test="open" id_list.append(id_test) overseas_stock.append(overseas_inventory) china_stock.append(china_inventory) id_state.append(test) c={"id":id_list, "id_state":id_state, "海外倉庫存":overseas_stock, "國內大倉":china_stock } ad_data=pd.DataFrame(c) return ad_data
幾種常見API調用實例
百度AI相關API
百度API是市面上面比較成熟的API服務,在大二期間由于需要使用一些文本打標簽和圖像標注工作了解了百度API,避免了重復造輪子,當時百度API的使用比較復雜,參考文檔很多不規(guī)范,之前也寫過類似的百度API調用極其不穩(wěn)定,但最近查閱了百度API參考文檔,發(fā)現(xiàn)目前的調用非常簡單。
通過安裝百度開發(fā)的API第三方包,直接利用Python調包傳參即可使用非常簡單。這里展示一個具體使用,相應安裝第三方庫官方文檔查閱。
''' 第三方包名稱:baidu-aip 百度API """ 你的 APPID AK SK """ APP_ID = '你的 App ID' API_KEY = '你的 Api Key' SECRET_KEY = '你的 Secret Key' 參考文檔:https://ai.baidu.com/ai-doc/NLP/tk6z52b9z ''' from aip import AipNlp APP_ID = 'xxxxxx' API_KEY = '換成你的apikey' SECRET_KEY = '換成你的SECRET_KEY' client = AipNlp(APP_ID, API_KEY, SECRET_KEY) text = "我還沒飯吃" # 調用文本糾錯 client.ecnet(text)
百度地圖API
這個API當時為了設計一個推薦體系引入經(jīng)緯度換算地址,這樣為數(shù)據(jù)計算帶來極大的方便,而且對于一般人來說文本地址相比經(jīng)緯度信息更加直觀,然后結合Python一個第三方包實現(xiàn)兩個地址之間經(jīng)緯度計算得出相對的距離。
# https://lbsyun.baidu.com/ # 計算校驗SN(百度API文檔說明需要此步驟) import pandas as pd import numpy as np import warnings import requests import urllib import hashlib import json from geopy.distance import geodesic location = input("輸入所在的位置\n") # "廣州市天河區(qū)" ak = "ak1111" # 參照自己的應用 sk = "sk111111" # 參照自己的應用 url = "http://api.map.baidu.com" query = "/geocoding/v3/?address={0}&output=json&ak={1}&callback=showLocation".format(location, ak) encodedStr = urllib.parse.quote(query, safe="/:=&?#+!$,;'@()*[]") sn = hashlib.md5(urllib.parse.quote_plus(encodedStr + sk).encode()).hexdigest() # 使用requests獲取返回的json response = requests.get("{0}{1}&sn={2}".format(url, query, sn)) data1=response.text.replace("showLocation&&showLocation(","").replace(")","") data = json.loads(data1) print(data) lat = data["result"]["location"]["lat"] lon = data["result"]["location"]["lng"] print("緯度: ", lat, " 經(jīng)度: ", lon) distance=geodesic((lat,lon), (39.98028,116.30495)) print("距離{0}這個位置大概{1}".format(location, distance))
有道API
在網(wǎng)上查閱了很多API,前面介紹的幾種API,他們攜帶的請求參數(shù)信息相對比較簡單,調用實現(xiàn)和基礎請求沒啥區(qū)別,這里找了一個相對而言比較多的請求參數(shù)的API,相對而言這種API數(shù)據(jù)付費API,它的安全性以及具體的實現(xiàn)都相對復雜,但是更適合商用。下面可以簡單看看。
import requests import time import hashlib import uuid youdao_url = 'https://openapi.youdao.com/api' # 有道api地址 translate_text = "how are you!" input_text = "" # 當文本長度小于等于20時,取文本 if(len(translate_text) <= 20): input_text = translate_text # 當文本長度大于20時,進行特殊處理 elif(len(translate_text) > 20): input_text = translate_text[:10] + str(len(translate_text)) + translate_text[-10:] uu_id = uuid.uuid1() now_time = int(time.time()) app_id = '1111111' app_key = '11111111111' sign = hashlib.sha256((app_id + input_text + str(uu_id) + str(now_time) + app_key).encode('utf-8')).hexdigest() # sign生成 data = { 'q':translate_text, # 翻譯文本 'from':"en", # 源語言 'to':"zh-CHS", # 翻譯語言 'appKey':app_id, # 應用id 'salt':uu_id, # 隨機生產(chǎn)的uuid碼 'sign':sign, # 簽名 'signType':"v3", # 簽名類型,固定值 'curtime':now_time, # 秒級時間戳 } r = requests.get(youdao_url, params = data).json() # 獲取返回的json()內容 print("翻譯后的結果:" + r["translation"][0]) # 獲取翻譯內容
翻譯后的結果:你好!
這個API調用中引用了幾個真正商用中的一些為了安全性等設置的驗證信息,比如uuid、sign、timestamp,這幾個在API調用中也是老生常談的幾個概念,是比較全面的。下面簡單介紹一下。
uuid
uuid碼:UUID是一個128比特的數(shù)值,這個數(shù)值可以通過一定的算法計算出來。為了提高效率,常用的UUID可縮短至16位。UUID用來識別屬性類型,在所有空間和時間上被視為唯一的標識。一般來說,可以保證這個值是真正唯一的任何地方產(chǎn)生的任意一個UUID都不會有相同的值。使用UUID的一個好處是可以為新的服務創(chuàng)建新的標識符。是一種獨特的唯一標識符,python 第三方庫uuid 提供對應的uuid生成方式,有以下的幾種 uuid1(),uuid3(),uuid4(),uuid5()上面采用的是uuid1()生成,還可以使用uuid4()生成。具體的使用方法 可以參考這篇博客。
sign
sign:一般為了防止被惡意抓包,通過數(shù)字簽名等保證API接口的安全性。為了防止發(fā)送的信息被串改,發(fā)送方通過將一些字段要素按一定的規(guī)則排序后,在轉化成密鑰,通過加密機制發(fā)送,當接收方接受到請求后需要驗證該信息是否被篡改過,也需要將對應的字段按照同樣的規(guī)則生成驗簽sign,然后在于后臺接收到的進行比對,可以發(fā)現(xiàn)信息是否被串改過。在上面的例子利用hashlib.sha256()來進行隨機產(chǎn)生一段密鑰,最后使用.hexdigest()返回最終的密鑰。
curtime:引入一個時間戳參數(shù),保證接口僅在一分鐘內有效,需要和客戶端時間保持一致。避免重復訪問。
有道API的sign構造可以到對應的官方文檔查看一下。因為每種API的簽名構造大體都有很多不是相同的。所以一般以官方文檔為主,這里對于簽名的一些原理,個人只是大致了解,有想詳細了解,可以自己查閱資料學習。
常用API分享
上面大致介紹了幾種API的使用,但各有不同,但都是基于構造HTTP請求按照一定規(guī)則來實現(xiàn)的,下面補充一些可以練手也比較好用的API地址,大家可以用來平常數(shù)據(jù)獲取以及學習參考。網(wǎng)上的API特別多,這里只是幾個,有興趣自己可以去了解,然后比較好用可以留言評論區(qū)分享出來。
2.百度大腦
3.apishop
4.百度地圖
5.騰訊地圖
6.有道翻譯
總結
其實最近發(fā)現(xiàn)博客很久沒更新,可能還是因為自己懶以及能力有限不知道該寫什么,但上半年工作以及生活中發(fā)現(xiàn),寫作其實是一個很好的東西,可以把自己的想法記錄下來,雖然可能很不成熟,但是其實對于自己的成長是很有幫助,寫多了也就熟了,而且現(xiàn)在快餐式的互聯(lián)網(wǎng),讓我們中斷了很多思考,尤其是工作后發(fā)現(xiàn),平常還是要多思考,多寫作,也可以沉淀自己,后續(xù)會慢慢更新博客,但是頻率可能不是很高,但是會慢慢多加入自己的思考和邏輯以及一些歸納,因為發(fā)現(xiàn)之前的博客太不成熟了哈哈哈。次也是一次成長吧,然后本人是在學數(shù)據(jù)分析數(shù)據(jù)挖掘的一個菜鳥,博客中可能出現(xiàn)的問題,希望大家積極指出,然后相互學習。