百度AI開放平台- API實戰調用
一、 前言
首先說一下項目需求。
兩個用戶,分別上傳了兩段不同的文字,要計算兩段文字相似度有多少,匹配數據庫中的符合條件的數據,初步估計列出來會有60-100條左右,不會更多,只會更少。最終的需求是:從這些匹配結果中找到相似度較高的那些條目。
自己編寫算法來實現是一個很大的工程,涉及到自然語言處理的一些方面,比較復雜。於是上網搜了搜,發現百度開放平台的自然語言處理可以免費調用,而且每天有10W的調用次數,對我的小項目來說正好滿足。但是,在往下翻的時候,發現百度給了提示,不保證並發,也就是說,在我調用的時候很容易出現返回錯誤結果的情況,這部分需要進行適當的處理。
既然是實戰,我們從頭開始說。
二、 准備工作
從哪里開始說呢,從在百度開放平台創建一個應用說起。創建應用之后(下面的開發環境是PHP,所以在選擇應用使用類型的時候填寫HTML),會有應用的ID,key,secret。后兩個參數接下來會有用。
我使用的是短文本相似度API,其他類型的功能與此基本一模一樣(不接受抬杠)。先去看開發文檔http://ai.baidu.com/docs#/NLP-API/top可以看到提供了兩種調用方式,這里我們以調用方式一為例-向API服務地址使用POST發送請求,這種方式來實現上述功能。看描述,需要一個參數叫做access_token,原文也提供了其獲取方式。以獲取短文本相似度的access_token為例子:
https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=MzRN******Txgske3QRf5Yj69&client_secret=a30CAbc*****bDuuGLdHLeyRaZk1tq5&
其中一共涉及三個參數,第一個固定為client_credentials不要改變。
第二第三個分別為前面獲取到的key,secret。
把這一串地址放在地址欄中,回車即可返回json格式的字符串,找到access_token復制存在一個文件中,留着備用。
注意:復制地址的時候,中間可能會帶有空格,一定要刪去。特別是復制開發文檔中的那段代碼的時候,帶有空格,是得不到返回結果的。可以復制我的這一段地址則沒有問題。
三、 動手
下面開始編寫PHP代碼。整個過程我們只需一個文件就好。
PHP可以使用curl來請求url參數。在開發文檔中,說明了要傳參access_token以及要把請求文本以json格式傳過去(編碼是GBK)。下面是代碼:
$access_token = "24.a810b4be2b5683a4d6af2f47b420877f.2592000.1507883636.282335-10044457"; $url = "https://aip.baidubce.com/rpc/2.0/nlp/v2/simnet?access_token=" . $access_token; $body=array( "text_1"=>"我在二舍B門口看到一床被子,應該是哪位同學忘記收走了,記得來取哦。", "text_2"=>"信息A門口有一輛自行車,黃色的,沒上鎖,請失主前去認領。" ); $json_data=json_encode($body);
這段代碼及時實現了上述功能。在把$body轉為json格式的時候,已經默認把中文從UTF8轉到GBK了,無需另外的操作。
$curl=curl_init(); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_POST, true); curl_setopt($curl, CURLOPT_POSTFIELDS, $dataArray[$i]); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);//取消SSL鑒權驗證
最后這個取消SSL鑒權驗證是必須的,否則會報錯!
$result = curl_exec($ch); //var_dump($result); $json = iconv("gb2312", "UTF-8", $result);//返回格式是中文GBK編碼,需要轉為UTF8
輸出$json則可以看到返回結果。
但是上述過程僅僅實現了一條記錄的相似度匹配,怎么實現多條呢?使用循環???NO!NO!NO!自己去測試一下如果同時10條url請求,會發生什么事情。好吧答案是:結果正確返回。。但是返回時間非常慢,因為10條請求是順序執行的,不會並行。所以下面的問題就是要解決並行問題。
四、 解決並行問題以及QPS並發問題
說一個大家都知道的不好的消息,就是PHP本身是不支持多線程的。有木有想死的感覺??
再說一個好消息,curl是可以並行處理多個url請求來模擬多線程的,這點非常好,啪啪!30條url請求同時發送,最終時間取決於最慢的那個請求。但是這個結果也非常棒了好不好。
下面是先是代碼(我做了20條數據(文字內容都是一樣的,其實一不一樣無所謂)):
<?php header('content-type:text/html;charset=utf8'); $localtime=date('y-m-d H:i:s',time()); echo "開始時間:".$localtime; $access_token = "24.a810b4be2b5*******************507883636.282335-10044457"; $url = "https://aip.baidubce.com/rpc/2.0/nlp/v2/simnet?access_token=" . $access_token; $body=array( "text_1"=>"我在二舍B門口看到一床被子,應該是哪位同學忘記收走了,記得來取哦。", "text_2"=>"信息A門口有一輛自行車,黃色的,沒上鎖,請失主前去認領。" ); $json_data=json_encode($body); $dataArray=array(); for($i=0;$i<160;$i++){ array_push($dataArray,$json_data); } $jsonResultArray=array(); mFunction($url,$dataArray,$jsonResultArray); /*$jsonResultArray=func($url,$json_data);//存儲返回的json數組*/ function mFunction($url,$dataArray,&$jsonResultArray){ $multicurl=curl_multi_init(); $curls=array();//存放所有的ch對象 for($i=0;$i<count($dataArray);$i++){ $curl=curl_init(); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_POST, true); curl_setopt($curl, CURLOPT_POSTFIELDS, $dataArray[$i]); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);//取消SSL鑒權驗證 curl_multi_add_handle($multicurl,$curl); array_push($curls,$curl); } /* $running = null; do { $mrc = curl_multi_exec($multicurl, $running); } while ($mrc == CURLM_CALL_MULTI_PERFORM); while ($running && $mrc == CURLM_OK) { if (curl_multi_select($multicurl) != -1) {//$mh批處理中還有可執行的$ch句柄,curl_multi_select($mh) != -1程序退出阻塞狀態。 do { //繼續執行需要處理的$ch句柄。 $mrc = curl_multi_exec($multicurl, $running); } while ($mrc == CURLM_CALL_MULTI_PERFORM); } }*/ $running = null; // 執行批處理句柄 do { usleep(10000); curl_multi_exec($multicurl, $running); } while ($running > 0); $failArray=array(); for($i=0;$i<count($dataArray);$i++){ $temp=iconv("gb2312", "UTF-8", curl_multi_getcontent($curls[$i]));//得到的是返回結果的json格式字符串 $resultarray=json_decode($temp);//得到一個數組 if(array_key_exists("error_msg",$resultarray)){//出錯則重新發送請求,最后得到的結果要賦值給temp array_push($failArray,$dataArray[$i]); curl_multi_remove_handle($multicurl, $curls[$i]); }else{ array_push($jsonResultArray,$temp); curl_multi_remove_handle($multicurl, $curls[$i]); } } if($failArray!=null){//如果$failArray數組不為空,繼續調用func() curl_multi_close($multicurl); mFunction($url,$failArray,$jsonResultArray);//$url,$json_data,&$jsonResultArray }else{//如果$failArray數組為空,return 即可退出函數。 curl_multi_close($multicurl); return; } } for($i=0;$i<count($jsonResultArray);$i++){ var_dump($jsonResultArray[$i]); } $localtime=date('y-m-d H:i:s',time()); echo "結束時間:".$localtime;
上面這段代碼,沒錯就是全部代碼都貼出來了,注釋也可以看。代碼中一道都把並發帶來的問題都解決了。
curl_multi_init(); 的用法不多說,上網查到處都是,可以去菜鳥教程看。下面說一說處理QPS的問題。
因為並行提交速度太快,服務器容易達到QPS限制,就會返回錯誤代碼:
- '{"error_msg":"Open api qps request limit reached","error_code":18}' (length=66)
沒什么好的解決方式,要想免費使用,我的方案是,對返回結果檢測是否存在error_msg只要存在,就得重新發送請求。返回結果正常呢,就把返回結果現存人數組中。在處理重新發送請求部分,使用了函數迭代,直到沒有錯誤信息才結束調用。最終所有的正確結果都存在數組中了。
五、 測試結果
經測試:
測試數據條目以及對應的響應時間,從結果來看,還是可以接受的。
/* * 20條=>1s * *40條=>3s * * 80條=>7s * * 160條=>16s * */
文章是昨晚上熬夜寫的,沒想到學校突然斷網斷電,斷電不可怕,重要的是電沒了,手機移動網絡也跟着消失……消失……失……
早上起來重新發嘍~
歡迎關注微信公眾號“ **IT客**“ ,投稿郵箱 itkeyy@163.com