首頁 > 軟體

python如何實現API的呼叫詳解

2022-05-17 13:00:44

前言

在日常工作中,可能需要結合網上現在的一些API或者公司提供的資料介面來得到相應的資料或者實現對應的功能。

因此API的呼叫和資料介面的存取都是做資料分析的一個常用操作,如何快速實現API和資料介面的呼叫,網上一般提供很多語言版本,但追根溯源採取的方式都是利用HTTP請求的方式來進行實現的。由於本人常用Python,本篇文章也主要採用Python來作為工具來進行介紹如何操作。

API

API:簡單來說就是一組協定、一個工具或者一組規則,定義不同應用程式之間的通訊方法,把具體實現的過程隱藏起來,只暴露必須呼叫的部分給開發者使用。

上面的定義比較官方,這裡簡單舉個例子來說明一下,比如在現在外面的麥當勞等快餐店都採取手機線上下單,前臺拿餐的過程。而在這個過程,作為消費者通常在手機上選好對應的餐品然後點選下單付款,然後等待前臺叫號拿餐即可。而這個過程具體怎麼實現的我們並不知道,整個過程有對應的app或者小程式通訊到廚房資料,然後廚師製作出餐。而這個APP和小程式就充當對應的API功能。

舉一個簡單的例子,一個社交平臺它每天收到各種語言的評論資訊,而作為對應的分析人員面對複雜的語言資料處理,是一個大難題,有人可能說開發一個模型來實現翻譯整合功能,這個方法雖然聽起來可行但是成本代價高,其次為了解決一個問題,而又去開發一個比較困難的問題。這個與原本的目標偏離越來越遠,這個時候就可以藉助國內比較成熟的翻譯平臺API,直接處理現有的資料。這樣相對而言成本代價小,更便捷也更能快速實現現有的目標。而API的作用在這裡就毋庸置疑了。

資料介面

資料介面: 簡單來說就是一組封裝的資料集口令,就是按照對應的規則傳送相應的引數,然後返回對應的相關資料資訊。API的呼叫和資料介面的這兩個在日常呼叫時很類似的,相對而言API的範圍更寬廣,實現的功能也比較多,而資料介面日常充當的就是一個取數工具比較多。

就比如說大型電商公司公司一般用統一的SKU來對商品進行管理,而比如這家公司是作為一個品牌商,它會在不同平臺上面進行售賣,而在這些平臺上面對映的商品標識ID就不同於公司的SKU。因為公司的SKU不僅基於商品而且還考慮各個地方倉庫以及產品的各個型號,而這個對映相對而言就比較複雜。

而在處理不同平臺的資料人員一般也不能直接使用公司的資料庫來對商品進行分析,因為顆粒度太細,分析起來比較複雜困難,這個時候就可以根據對應功能的要求讓開發在現有系統是開發一個單獨的資料介面提供相應的公司,避免直接請求資料庫過程複雜等相應資訊。但資料介面相對實時的資料庫存在一定的延遲。

API的呼叫和資料介面的呼叫

API和資料介面通過前面的舉例論述,大致理解起來也比較簡單,而具體怎麼實現API的呼叫和資料介面的呼叫這裡簡單介紹一下。

簡單來說API的呼叫和介面的呼叫都是類似一個HTTP請求,而呼叫最主要就是根據相應的規則將請求方式、請求頭、URL、以及請求體封裝好然後傳送請求,就可以實現相應的呼叫。

但資料介面和API兩個的呼叫相比較而言來,一般資料介面比較簡單,很多情況下資料介面是在公司內網資料存取所以請求資訊比較簡單,而API大多是第三方企業開發對外的服務屬於一種商業服務,相對而言為了保證請求的安全,考慮的更為全面,加入了AK、SK、簽名、時間戳等資訊比較複雜。

而追本溯源這兩個呼叫都是類似HTTP請求,具體呼叫大致差不多,主要就是API呼叫中包含的請求引數的資訊更多。而具體怎麼實現下面將簡單的介紹一下。

呼叫的基礎-請求方法

一般而言,常見的HTTP請求呼叫方式有很多,這方面的資源比較多,可以網上自己查閱,這裡就簡單說說常見的兩種請求方法。

GET 請求

GET請求簡單來說就是從伺服器上獲取資源,可以載入到瀏覽器的快取中。

POST 請求

POST請求一般而言以表單形式向伺服器傳送請求,請求引數包含在請求體當中可能導致資源的建立和改變。POST請求的資訊不能快取在瀏覽器中。
這兩個請求方法說起來很簡單,但最重要的一點就是了解這兩種請求的區別,從而為介面的設計和API的使用更加熟悉。

GET和POST請求的區別

1.GET請求請求長度最多1024kb,POST對請求資料沒有限制。這一點原因是很多時候GET請求把對應的資訊放在URL中,而URL的長度有限,導致GET請求的長度也受到一定的限制。而POST請求相應的引數資訊放在請求體body中所以一般不受長度限制。

2.POST請求比GET更安全一些,因為GET請求中URL包含了相應的資訊,頁面會被瀏覽器快取,其他人可以看到相應的資訊。

3.GET產生一個TCP封包,POST產生兩個TCP封包。

GET請求的時候將header、data一起傳送出去,然後伺服器響應返回200。而POST則是先傳送header,等待伺服器響應100,然後傳送data,最後伺服器響應返回200.但在這裡注意,POST請求分為兩次,但是請求體body是緊隨在header之後傳送的,所以這之間時間可以微乎不計。

4.GET請求只支援URL編碼,而POST相對而言有多種編碼方式。

5.GET請求引數是通過URL傳遞的,多個引數以&連線,POST請求放在request body中。

6.GET請求只支援ASCII字元,而POST沒有限制。

一般而言瀏覽器輸入網址可以直接存取的一般是GET請求。

Python實現GET請求和POST請求

上面大篇幅的介紹了一些資料介面、API相關知識以及請求方法,使用起來比較簡單,下面可以大致熟悉一下相應的請求方式。一般直接使用Python的request庫就可以。

GET請求

import request
# GET請求傳送的引數一定要是字典的形式,可以傳送多個引數。
# 傳送格式:{'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

具體編碼方式為下面三種,可以瞭解具體的請求實現,一般公司內部的資料介面設定了區域網所以有的可以不需要加header。

POST請求的三種提交形式

1.最常見的post提交資料以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格式提交資料: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網站一般對相應API提供一定的免費次數,可以充當學習使用,而且支援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提交資料以form表單為主:application/x-www-form-urlencoded

url = 'https://api.apishop.net/common/weather/get15DaysWeatherByArea'
params = {
    "apiKey":'換成你的apikey',
    "area":'武漢市武昌區',
}
response = requests.post(url,params)
print(response.text)

在呼叫這種API介面一般都需要進行一個狀態碼等返回資訊測試,檢查請求是否正常,可以按照下面的給出參考。

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)

資料提取

其實API的呼叫很簡單,但其中最核心的其實是返回資訊中資料的抽取,一般而言返回的資訊都是json形式,需要從中用字典鍵值對方式提取資料,下面這塊根據請求的資料返回相應的資訊並提取出來,獲取資訊後面將會展示。

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:UserszhangfengDesktop最近十五天天氣.xlsx",index = None)
    return weather_data
if __name__ == '__main__':
    print("請輸入對應的城市")
    web_data = get_url(input())
    weather_data = extract_data(web_data)

部分結果如下圖:

資料介面範例

在日常學習中資料介面的使用可能相對較少,資料介面的應用場景大多數情況下是應用在公司內部調取資料的情況下比較多,所以一般很少見到,這裡展示工作中遇到的兩個資料介面的使用,由於工作考慮,展示的程式碼屬於樣例,並不能呼叫。可以參考一下呼叫實現以及規範。

POST請求呼叫資料介面

# 銷售狀態查詢
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狀態
    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請求呼叫資料介面

### 庫存資料查詢
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='沒有資料' 
    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='沒有資料' 
    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的使用比較複雜,參考檔案很多不規範,之前也寫過類似的百度API呼叫極其不穩定,但最近查閱了百度API參考檔案,發現目前的呼叫非常簡單。

通過安裝百度開發的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當時為了設計一個推薦體系引入經緯度換算地址,這樣為資料計算帶來極大的方便,而且對於一般人來說文字地址相比經緯度資訊更加直觀,然後結合Python一個第三方包實現兩個地址之間經緯度計算得出相對的距離。

# 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")  # "廣州市天河區"
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, " 經度: ", lon)
distance=geodesic((lat,lon), (39.98028,116.30495))
print("距離{0}這個位置大概{1}".format(location, distance))

有道API

在網上查閱了很多API,前面介紹的幾種API,他們攜帶的請求引數資訊相對比較簡單,呼叫實現和基礎請求沒啥區別,這裡找了一個相對而言比較多的請求引數的API,相對而言這種API資料付費API,它的安全性以及具體的實現都相對複雜,但是更適合商用。下面可以簡單看看。

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,   # 隨機生產的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位元的數值,這個數值可以通過一定的演演算法計算出來。為了提高效率,常用的UUID可縮短至16位元。UUID用來識別屬性型別,在所有空間和時間上被視為唯一的標識。一般來說,可以保證這個值是真正唯一的任何地方產生的任意一個UUID都不會有相同的值。使用UUID的一個好處是可以為新的服務建立新的識別符號。是一種獨特的唯一識別符號,python 第三方庫uuid 提供對應的uuid生成方式,有以下的幾種 uuid1(),uuid3(),uuid4(),uuid5()上面採用的是uuid1()生成,還可以使用uuid4()生成。具體的使用方法 可以參考這篇部落格

sign

sign:一般為了防止被惡意抓包,通過數位簽章等保證API介面的安全性。為了防止傳送的資訊被串改,傳送方通過將一些欄位要素按一定的規則排序後,在轉化成金鑰,通過加密機制傳送,當接收方接受到請求後需要驗證該資訊是否被篡改過,也需要將對應的欄位按照同樣的規則生成驗籤sign,然後在於後臺接收到的進行比對,可以發現資訊是否被串改過。在上面的例子利用hashlib.sha256()來進行隨機產生一段金鑰,最後使用.hexdigest()返回最終的金鑰。

curtime:引入一個時間戳引數,保證介面僅在一分鐘內有效,需要和使用者端時間保持一致。避免重複存取。

有道API的sign構造可以到對應的官方檔案檢視一下。因為每種API的簽名構造大體都有很多不是相同的。所以一般以官方檔案為主,這裡對於簽名的一些原理,個人只是大致瞭解,有想詳細瞭解,可以自己查閱資料學習。

常用API分享

上面大致介紹了幾種API的使用,但各有不同,但都是基於構造HTTP請求按照一定規則來實現的,下面補充一些可以練手也比較好用的API地址,大家可以用來平常資料獲取以及學習參考。網上的API特別多,這裡只是幾個,有興趣自己可以去了解,然後比較好用可以留言評論區分享出來。

1.聚合資料

2.百度大腦

3.apishop

4.百度地圖

5.騰訊地圖

6.有道翻譯

總結

其實最近發現部落格很久沒更新,可能還是因為自己懶以及能力有限不知道該寫什麼,但上半年工作以及生活中發現,寫作其實是一個很好的東西,可以把自己的想法記錄下來,雖然可能很不成熟,但是其實對於自己的成長是很有幫助,寫多了也就熟了,而且現在快餐式的網際網路,讓我們中斷了很多思考,尤其是工作後發現,平常還是要多思考,多寫作,也可以沉澱自己,後續會慢慢更新部落格,但是頻率可能不是很高,但是會慢慢多加入自己的思考和邏輯以及一些歸納,因為發現之前的部落格太不成熟了哈哈哈。次也是一次成長吧,然後本人是在學資料分析資料探勘的一個菜鳥,部落格中可能出現的問題,希望大家積極指出,然後相互學習。


IT145.com E-mail:sddin#qq.com