百度AI開放平臺- API實戰調用php
1、 前言html
首先說一下項目需求。算法
兩個用戶,分別上傳了兩段不一樣的文字,要計算兩段文字類似度有多少,匹配數據庫中的符合條件的數據,初步估計列出來會有60-100條左右,不會更多,只會更少。最終的需求是:從這些匹配結果中找到類似度較高的那些條目。數據庫
本身編寫算法來實現是一個很大的工程,涉及到天然語言處理的一些方面,比較複雜。因而上網搜了搜,發現百度開放平臺的天然語言處理能夠免費調用,並且天天有10W的調用次數,對個人小項目來講正好知足。可是,在往下翻的時候,發現百度給了提示,不保證併發,也就是說,在我調用的時候很容易出現返回錯誤結果的狀況,這部分須要進行適當的處理。json
既然是實戰,咱們從頭開始說。api
2、 準備工做數組
從哪裏開始說呢,從在百度開放平臺建立一個應用提及。建立應用以後(下面的開發環境是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複製存在一個文件中,留着備用。
注意:複製地址的時候,中間可能會帶有空格,必定要刪去。特別是複製開發文檔中的那段代碼的時候,帶有空格,是得不到返回結果的。能夠複製個人這一段地址則沒有問題。
3、 動手
下面開始編寫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條請求是順序執行的,不會並行。因此下面的問題就是要解決並行問題。
4、 解決並行問題以及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只要存在,就得從新發送請求。返回結果正常呢,就把返回結果現存人數組中。在處理從新發送請求部分,使用了函數迭代,直到沒有錯誤信息才結束調用。最終全部的正確結果都存在數組中了。
5、 測試結果
經測試:
測試數據條目以及對應的響應時間,從結果來看,仍是能夠接受的。
/* * 20條=>1s * *40條=>3s * * 80條=>7s * * 160條=>16s * */
文章是昨晚上熬夜寫的,沒想到學校忽然斷網斷電,斷電不可怕,重要的是電沒了,手機移動網絡也跟着消失……消失……失……
早上起來從新發嘍~
歡迎關注微信公衆號「 **IT客**「 ,投稿郵箱 itkeyy@163.com