Grab in PHP

  1. 前话
  2. Tool
    1. Snoopy
    2. SimpleHtmlDom
  3. steps
    1. 1、获取环境空气质量标准城市列表
    2. 2、获取单个城市的空气质量数据
    3. 3、获取全部城市的空气质量数据
    4. Result

前话

程序之间的交流用json之类数据交互格式,有API会给我们提供很方便的事情。最近在做空气质量数据统计的小东西的时候,碰到几个网站虽开放了API也是很小气的对每小时内的请求次数进行了限制,没意思,还是去从数据的源头入口搞搞看。

Tool

其中不乏被成为利器的工具:

Snoopy

Snoopy是一个用来模拟浏览器的功能,可以获取网页内容,发送表单,可以用来开发一些采集程序和小偷程序的类。

演示用例:

include "Snoopy.class.php";
$snoopy = new Snoopy;
$snoopy->fetchtext("http://www.php.net/");
print $snoopy->results;
$snoopy->fetchlinks("http://www.phpbuilder.com/");
print $snoopy->results;
$submit_url = "http://lnk.ispi.net/texis/scripts/msearch/netsearch.html";
$submit_vars["q"] = "amiga";
$submit_vars["submit"] = "Search!";
$submit_vars["searchhost"] = "Altavista";
$snoopy->submit($submit_url,$submit_vars);
print $snoopy->results;
$snoopy->maxframes=5;
$snoopy->fetch("http://www.ispi.net/");
echo "<PRE>\n";
echo htmlentities($snoopy->results[0]);
echo htmlentities($snoopy->results[1]);
echo htmlentities($snoopy->results[2]);
echo "</PRE>\n";
$snoopy->fetchform("http://www.altavista.com");
print $snoopy->results;

但是我个人对这个工具还不是太钟爱,至少在解决页面数据抓取的问题上没有下面这个工具来的爽快。

SimpleHtmlDom

这个工具的名字就像他自己写的那样一样低调简单,能想jQuery那样对php获取到的内容选择、操作、替换、删除,只要能获取到相应的数据,还是非常利落的。

<?php
// 包含或引用
include('../simple_html_dom.php');
// file_get_html从URL或者html文件获取DOM
$html = file_get_html('http://www.google.com/');
// 找出所有a链接
foreach($html->find('a') as $e)
echo $e->href . '<br>';
// 获取所有图像
foreach($html->find('img') as $e)
echo $e->src . '<br>';
// 获取img标签的全部内容
foreach($html->find('img') as $e)
echo $e->outertext . '<br>';
// 找到所有id=gbar的div标签
foreach($html->find('div#gbar') as $e)
echo $e->innertext . '<br>';
// 找到所有class=gb1的span标签
foreach($html->find('span.gb1') as $e)
echo $e->outertext . '<br>';
// 找到所有属性为align=center的td
foreach($html->find('td[align=center]') as $e)
echo $e->innertext . '<br>';
// 从表格中获取值,比如获取<td>value</td>中的value
echo $html->find('td[align="center"]', 1)->plaintext.'<br><hr>';
// 移除所有图像
foreach($html->find('img') as $e)
$e->outertext = '';
// 替换所有input
foreach($html->find('input') as $e)
$e->outertext = '[INPUT]';
// 从html中获取所有文本
echo $html->plaintext;
?>

注意
本工具是显得非常简单易用,但是需要注意很容易引起内存泄露(Memory Leak)比如下面的调试情况:

$html = file_get_html("……");
print_r($html);
$html->clear();
unset($html);

因为PHP5循环引用内存泄露,在创建DOM对象之后,你需要调用$dom->clear()以释放因为file_get_dom()多次调用的内存。

steps

1、获取环境空气质量标准城市列表

PM25.IN 提供了获取所有参与空气质量检测的城市,只需要调用其中的api就可以轻松获得一个json格式的cities。

2、获取单个城市的空气质量数据

比如我们获取链接:

http://aqistudy.sinaapp.com/historydata/monthdata.php?city=%E4%B8%8A%E6%B5%B7

中每个月份的AQI、范围、质量等级、PM2.5、PM10、SO2等数据,在载入simple_html_dom.php之后,就可以使用file_get_html()函数获取上面链接的内容,然后在对所有td[align=center]选择器中的数据进行提取,结果中可能有不符合条件的数据,你还需要去除不符合的部分,最后,为了让此json友好地可用,增加数组键名。

/**
* 获取单个城市的空气质量数据
*
* @param $city string 城市名称
*/
public function getOneCityAction($city)
{
$html = file_get_html($this->url.$city);
$array = array();
foreach($html->find('td[align=center]') as $e){
array_push($array, $e->plaintext);
}
// 清除掉末尾的6个
$num = count($array);
foreach($array as $key =>$value){
if($key < ($num-6)){
$cleanData[$key] = $value;
}
}
// 重新分组
foreach($cleanData as $key=>$value){
$data = array_chunk($cleanData, 11);
}
// 修改键名
foreach($data as $key=>$value)
{
foreach ($value as $kk=>$vv) {
switch ($kk){
case 0:
$data[$key]['month'] = $vv;
unset($data[$key][0]);
case 1:
$data[$key]['AQI'] = $vv;
unset($data[$key][1]);
case 2:
$data[$key]['range'] = $vv;
unset($data[$key][2]);
case 3:
$data[$key]['quality'] = $vv;
unset($data[$key][3]);
case 4:
$data[$key]['pm2.5'] = $vv;
unset($data[$key][4]);
case 5:
$data[$key]['pm10'] = $vv;
unset($data[$key][5]);
case 6:
$data[$key]['so2'] = $vv;
unset($data[$key][6]);
case 7:
$data[$key]['co'] = $vv;
unset($data[$key][7]);
case 8:
$data[$key]['no2'] = $vv;
unset($data[$key][8]);
case 9:
$data[$key]['o3'] = $vv;
unset($data[$key][9]);
case 10:
$data[$key]['rank'] = $vv;
unset($data[$key][10]);
}
}
}
// 保存文件
$this->file->write($city.'年度空气质量.json', json_encode($data, JSON_UNESCAPED_UNICODE));
$this->log('debug', var_export($data, true));
return false;
}

3、获取全部城市的空气质量数据

/**
* 获取所有城市的空气质量数据
*
*
*/
public function getAllAction()
{
if(!\Yaf\Loader::import("../../../install/simplehtmldom/simple_html_dom.php")){
echo 'SimpleHtmlDom.php文件没有正确加载进来';
}
foreach(json_decode($this->cities) as $city)
{
foreach($city as $cityname)
{
$this->getOneCityAction($cityname);
}
}
}

Result

script>