最近公司需要抓取百度地圖中所有藥店數(shù)據(jù),本人比較愚笨纽匙,斷斷續(xù)續(xù)花了2天時間才完成此次任務(wù)。期間遇到的各種小坑插爹,但目前進度良好。以下記錄的是我進行任務(wù)的整個流程请梢。
1赠尾、熟悉百度API,調(diào)用API獲得數(shù)據(jù)毅弧。
既然要拿百度地圖的數(shù)據(jù)气嫁,那自然首選在地圖API入手。經(jīng)過了解够坐,發(fā)現(xiàn)了 place api 可以使用寸宵。
Place API 提供區(qū)域檢索POI服務(wù)崖面、POI詳情服務(wù)與團購信息檢索服務(wù)、商家團購詳情查詢梯影。
區(qū)域檢索POI服務(wù)提供三種區(qū)域檢索方法:
城市內(nèi)檢索(對應(yīng)JavaScriptAPI的Search方法)
矩形檢索(對應(yīng)JavaScript API的SearchInBound方法)
圓形區(qū)域檢索(對應(yīng)JavaScript的SearchNearBy方法)巫员。
調(diào)用此API也非常簡單,只需要用PHP生成一條URL甲棍,并生成get請求即可得到數(shù)據(jù)简识。由于API的每次調(diào)用默認只返回10條信息,所以為了能夠獲得更多的信息感猛,需要設(shè)置page_num參數(shù)進行翻頁操作七扰。這時候使用一個遞歸函數(shù)完成比較好。
class DataCollector{
public function getShopDataByCity($cityName,$keyword){
$this->_getShopDataByCity($cityName,$keyword);
return $this->shopData;
}
private function _getShopDataByCity($cityName,$keyword,$page_num=0){
$url = "http://api.map.baidu.com/place/v2/search?q=$keyword®ion=$cityName&output=json&ak=597a11f21bf9dfd3ab95632271b3832c&page_num=$page_num";
$curl = new \Common\Lib\Curl();
$result = $curl->get($url);
$resultData = json_decode($result);
if($resultData->results != null){
foreach ($resultData->results as $value) {
$value = $this->objectToArray($value);
$this->shopData[] = $value;
}
//繼續(xù)尋找下一頁
$this->_getShopDataByCity($cityName,$keyword,$page_num+1);
}
}
}
運行一下陪白,成功了返回數(shù)據(jù)颈走。
2、發(fā)現(xiàn)數(shù)據(jù)不夠咱士,并填坑
然而當(dāng)我換了幾個城市名調(diào)用的時候立由,發(fā)現(xiàn)API返回的數(shù)據(jù)最多是760條,這遠遠是不夠的司致。一個大城市的藥店總數(shù)能超過5000拆吆,而一些二線城市的藥店總數(shù)也能接近一千≈茫看來百度在API上做了手腳枣耀,強行阻止了用戶扒她的數(shù)據(jù)。于是我繼續(xù)研究了她提供的API庭再,發(fā)現(xiàn)給API支持矩形檢索捞奕。好,那就試試吧拄轻。
public function getShopDataByBounds($bounds,$keyword){
$this->_getShopDataByBounds($bounds,$keyword);
return $this->shopData;
}
private function _getShopDataByBounds(array $bounds,$keyword,$page_num=0){
$url = "http://api.map.baidu.com/place/v2/search?query=$keyword&bounds=$bounds[0],$bounds[1],$bounds[2],$bounds[3]&output=json&ak=".DataCollector::AK."&page_num=$page_num";
$result = $this->curl->get($url);
$resultData = json_decode($result);
$this->msg= $resultData->message;
if($resultData->results != null){
foreach ($resultData->results as $value) {
$value = $this->objectToArray($value);
$this->shopData[] = $value;
}
//繼續(xù)尋找下一頁
$this->_getShopDataByBounds($bounds,$keyword,$page_num+1);
}
}
好了颅围,如果bounds的經(jīng)緯度間隔足夠小,就能夠拿到充足的數(shù)據(jù)恨搓。但是我們的大中國是由無數(shù)個小矩形組成的院促,這意味著獲得全部的數(shù)據(jù),就必須遍歷無數(shù)的矩形斧抱。但顯然沒什么好辦法常拓,那就遍歷下去吧。
3辉浦、內(nèi)存溢出
我先使用上面的函數(shù)將中國這個大公雞的頭部拿下了弄抬,先用矩形框住公雞頭,在百度的拾取系統(tǒng)中找到矩形的左下角和右上角的大概的經(jīng)緯度宪郊,分別是:(26掂恕,95)(31拖陆,122.6)。使用下面的循環(huán)反復(fù)調(diào)用getShopDataByBounds()函數(shù)懊亡,即可得到大公雞頭部的數(shù)據(jù)了依啰。
for($i=26;$i<=31; $i = $i+0.1){
for($j=95;$j<=122.6;$j=$j+0.1){
$shopData = $cotor->getShopDataByBounds(array($i,$j,$i+0.1,$j+0.1),'藥店');
}
}
print_r($shopData);
但是很不幸的是,程序運行了10分鐘左右斋配,爆出了內(nèi)存溢出的錯誤孔飒,函數(shù)被意外終斷了。
4艰争、解決問題
到底是怎么回事呢坏瞄?其實重新審視一遍自己的代碼,很快就發(fā)現(xiàn)了問題甩卓,_getShopDataByBounds()函數(shù)使用到了class中的一個私有數(shù)組resultData鸠匀。每次我調(diào)用_getShopDataByBounds()后,這個數(shù)組并沒有被重置逾柿,以至于它越來越大缀棍,到最后撐爆了內(nèi)存,爆出嚴重錯誤机错。因此爬范,需要在調(diào)用外部函數(shù)getShopDataByBounds()的時候,必須先重置數(shù)組弱匪。
public function getShopDataByCity($cityName,$keyword){
unset($this->shopData);
unset($this->msg);
$this->_getShopDataByCity($cityName,$keyword);
return $this->shopData;
}
再次運行程序青瀑,OK了再也不出現(xiàn)內(nèi)存溢出情況了。
5萧诫、繼續(xù)調(diào)優(yōu)
程序安靜平穩(wěn)的運行了差不多兩個小時斥难,這是突然發(fā)現(xiàn)后面再也不能獲取到數(shù)據(jù)了,而且在這兩個小時的時間里產(chǎn)生的數(shù)據(jù)只有1W多帘饶,效率太慢了哑诊。其實獲取不到數(shù)據(jù),是由于百度限制了每個AK的調(diào)用次數(shù)及刻,當(dāng)AK的調(diào)用次數(shù)達到了上限镀裤,就不再能夠獲取數(shù)據(jù)了。所以程序程序必須要能智能切換AK缴饭,同時為了提高效率暑劝,還應(yīng)該多起幾個進程進行采集任務(wù)。小菜程序還需要花很多精力去完善才能變得健壯起來呢茴扁。
代碼的精益求精能使得自己的能力更上一層樓铃岔,所以抓取數(shù)據(jù)的活在未來慢慢完善汪疮。就寫到這了峭火。