开发那点事(六)php抓取北京实时公交数据
2019-06-02 本文已影响1人
极客简讯
开发背景
自己开发一款北京实时公交的小程序,奈何在网上苦苦寻找api接口无果,最后只得爬取网上数据
项目构思
1 选定爬虫框架—QueryList
2 数据源选定—北京公交网
3 根据需求将div格式化成json数据
开发实践
1 QueryList安装,利用composer直接进行安装,点我进官网查看示例
composer require jaeger/querylist
在控制器中引用
<?php
namespace app\index\controller;
use QL\QueryList;
class Index
{
public function index()
{
//采集某页面所有的图片
$data = QueryList::get('http://cms.querylist.cc/bizhi/453.html')->find('img')->attrs('src');
//打印结果
print_r($data->all());
}
}
2 数据源接口确定
获取行驶方向
http://www.bjbus.com/home/ajax_rtbus_data.php?act=getLineDir&selBLine=428
返回内容
<a href="javascript:;" data-uuid="4907320871547002333">428(天通北苑-地铁龙泽站)</a>
<a href="javascript:;" data-uuid="5415569149649522461">428(地铁龙泽站-天通北苑)</a>
获取行驶情况
http://www.bjbus.com/home/ajax_rtbus_data.php?act=busTime&selBLine=1&selBDir=
返回内容
{
"html": "<div class=\"inquiry_header\"><div class=\"left fixed\"><h3 id=\"lh\">428路</h3></div><div class=\"inner\"><h2 id=\"lm\">天通北苑-地铁龙泽站</h2><article><p>天通北苑 5:30-23:00 分段计价 所属客一分公司</p><p>车辆均已过站</p></article></div></div><div id=\"cc_stop\" class=\"inquiry_main\" unselectable=\"on\" onselectstart=\"return false;\"><ul class=\"fixed\"><li><div id=\"1\"><i></i><p class=\"sicon\"></p><span title=\"天通北苑\">天通北苑</span></div></li><li><div id=\"2m\"><i ></i></div></li><li><div id=\"2\"><i></i><p class=\"sicon\"></p><span title=\"天通东苑三区西门\">天通东苑三区<br/>...</span></div></li><li><div id=\"3m\"><i ></i></div></li><li><div id=\"3\"><i></i><p class=\"sicon\"></p><span title=\"天通北苑三区南门\">天通北苑三区<br/>...</span></div></li><li><div id=\"4m\"><i ></i></div></li><li><div id=\"4\"><i></i><p class=\"sicon\"></p><span title=\"天通北苑三区南\">天通北苑三区<br/>...</span></div></li><li><div id=\"5m\"><i ></i></div></li><li><div id=\"5\"><i></i><p class=\"sicon\"></p><span title=\"天通北苑二区东门\">天通北苑二区<br/>...</span></div></li><li><div id=\"6m\"><i ></i></div></li><li><div id=\"6\"><i></i><p class=\"sicon\"></p><span title=\"狮子营西门\">狮子营西门</span></div></li><li><div id=\"7m\"><i ></i></div></li><li><div id=\"7\"><i></i><p class=\"sicon\"></p><span title=\"天通北苑二区北门\">天通北苑二区<br/>...</span></div></li><li><div id=\"8m\"><i ></i></div></li><li><div id=\"8\"><i></i><p class=\"sicon\"></p><span title=\"天通北苑一区北门\">天通北苑一区<br/>...</span></div></li><li><div id=\"9m\"><i class=\"busc\" clstag=\"\"></i></div></li><li><div id=\"9\"><i></i><p class=\"sicon\"></p><span title=\"地铁天通苑北站南\">地铁天通苑北<br/>...</span></div></li><li><div id=\"10m\"><i ></i></div></li><li><div id=\"10\"><i></i><p class=\"sicon\"></p><span title=\"东三旗\">东三旗</span></div></li><li><div id=\"11m\"><i ></i></div></li><li><div id=\"11\"><i></i><p class=\"sicon\"></p><span title=\"半截塔村东站\">半截塔村东站</span></div></li><li><div id=\"12m\"><i ></i></div></li><li><div id=\"12\"><i></i><p class=\"sicon\"></p><span title=\"半截塔村北站\">半截塔村北站</span></div></li><li><div id=\"13m\"><i ></i></div></li><li><div id=\"13\"><i></i><p class=\"sicon\"></p><span title=\"魏窑村\">魏窑村</span></div></li><li><div id=\"14m\"><i ></i></div></li><li><div id=\"14\"><i></i><p class=\"sicon\"></p><span title=\"绿野福苑\">绿野福苑</span></div></li><li><div id=\"15m\"><i ></i></div></li><li><div id=\"15\"><i></i><p class=\"sicon\"></p><span title=\"小辛庄东\">小辛庄东</span></div></li><li><div id=\"16m\"><i ></i></div></li><li><div id=\"16\"><i></i><p class=\"sicon\"></p><span title=\"小辛庄\">小辛庄</span></div></li><li><div id=\"17m\"><i ></i></div></li><li><div id=\"17\"><i></i><p class=\"sicon\"></p><span title=\"小辛庄西\">小辛庄西</span></div></li><li><div id=\"18m\"><i ></i></div></li><li><div id=\"18\"><i></i><p class=\"sicon\"></p><span title=\"龙锦苑东二区北门\">龙锦苑东二区<br/>...</span></div></li><li><div id=\"19m\"><i ></i></div></li><li><div id=\"19\"><i></i><p class=\"sicon\"></p><span title=\"上坡路口西\">上坡路口西</span></div></li><li><div id=\"20m\"><i ></i></div></li><li><div id=\"20\"><i></i><p class=\"sicon\"></p><span title=\"和谐家园一区北门\">和谐家园一区<br/>...</span></div></li><li><div id=\"21m\"><i ></i></div></li><li><div id=\"21\"><i></i><p class=\"sicon\"></p><span title=\"龙锦苑二区\">龙锦苑二区</span></div></li><li><div id=\"22m\"><i ></i></div></li><li><div id=\"22\"><i></i><p class=\"sicon\"></p><span title=\"田园风光雅苑\">田园风光雅苑</span></div></li><li><div id=\"23m\"><i ></i></div></li><li><div id=\"23\"><i class=\"buss\" clstag=\"-1\"></i><p class=\"sicon\"></p><span title=\"龙锦苑四区\">龙锦苑四区</span></div></li><li><div id=\"24m\"><i ></i></div></li><li><div id=\"24\"><i></i><p class=\"sicon\"></p><span title=\"马连店南口\">马连店南口</span></div></li><li><div id=\"25m\"><i ></i></div></li><li><div id=\"25\"><i></i><p class=\"sicon\"></p><span title=\"龙禧苑三区北门\">龙禧苑三区北<br/>...</span></div></li><li><div id=\"26m\"><i ></i></div></li><li><div id=\"26\"><i></i><p class=\"sicon\"></p><span title=\"龙禧苑三区路口西\">龙禧苑三区路<br/>...</span></div></li><li><div id=\"27m\"><i ></i></div></li><li><div id=\"27\"><i></i><p class=\"sicon\"></p><span title=\"回龙观公交场站\">回龙观公交场<br/>...</span></div></li><li><div id=\"28m\"><i ></i></div></li><li><div id=\"28\"><i></i><p class=\"sicon\"></p><span title=\"风雅园北\">风雅园北</span></div></li><li><div id=\"29m\"><i ></i></div></li><li><div id=\"29\"><i></i><p class=\"sicon\"></p><span title=\"三合庄园\">三合庄园</span></div></li><li><div id=\"30m\"><i ></i></div></li><li><div id=\"30\"><i></i><p class=\"sicon\"></p><span title=\"龙华园\">龙华园</span></div></li><li><div id=\"31m\"><i ></i></div></li><li><div id=\"31\"><i></i><p class=\"sicon\"></p><span title=\"龙华园南区\">龙华园南区</span></div></li><li><div id=\"32m\"><i ></i></div></li><li><div id=\"32\"><i></i><p class=\"sicon\"></p><span title=\"地铁龙泽站\">地铁龙泽站</span></div></li></ul></div><div class=\"inquiry_footer\"><section><div class=\"inner\"><span class=\"buss\">途中车辆</span><span class=\"busc\">到站车辆</span></div></section></div>",
"w": 1532,
"seq": "1"
}
3 将div格式化成json
获取公交线路比较简单,只有两个a标签,我们只需获取其中的text以及uuid就可以,以下是代码
public function getBusLine($busName)
{
$rules = [
// 公交车名称
'name' => ['a', 'text'],
// 公交车uuid
'uuid' => ['a', 'data-uuid']
];
//采集某页面所有的图片
$data = QueryList::get('http://www.bjbus.com/home/ajax_rtbus_data.php?act=getLineDir&selBLine=' . $busName
)->rules($rules)->query()->getData();;
//打印结果
$result = $data->all();
if (count($result) === 0) {
throw new ParameterException(['msg' => '暂无公交信息']);
}
for ($i = 0; $i < count($result); $i++) {
$array = explode('(', $result[$i]['name']);
$result[$i]['name'] = $array[0];
$result[$i]['busLine'] = substr($array[1], 0, strlen($array[1]) - 1);
}
return $result;
}
获取公交行驶情况比较复杂,需要注意span标签与i标签的关系
public function getBusInfo($uuid)
{
$rules = [
// 公交车名称
'status' => ['div>i', 'class'],
'name' => ['div>span', 'title'],
'headInfo' => ['article>p', 'text']
];
$result = get('http://www.bjbus.com/home/ajax_rtbus_data.php?act=busTime&selBLine=1&selBDir=' . $uuid . '&selBStop=1');
$result = json_decode($result, true);
if (!array_key_exists('html', $result)) {
throw new ParameterException(['msg' => '查询公交信息失败']);
}
$data = QueryList::html($result['html'])->rules($rules)->query()->getData();
$busResult = $data->all();
$result = [
'time' => '',
'busInfo' => [],
'busc' => 0
];
$result['time'] = $busResult[0]['headInfo'];
//for循环是精髓
for ($i = 0; $i < count($busResult) / 2; $i++) {
$resultItem = [];
$resultItem['busName'] = $busResult[$i]['name'];
if ($i == 0) {
$resultItem['buss'] = $busResult[$i]['status'] == 'buss';
$resultItem['busc'] = $busResult[$i + 1]['status'] == 'busc';
if ($resultItem['buss']) {
$result['busc'] = $result['busc'] + 1;
}
if ($resultItem['busc']) {
$result['busc'] = $result['busc'] + 1;
}
} else {
$resultItem['buss'] = $busResult[($i * 2)]['status'] == 'buss';
if ($resultItem['buss']) {
$result['busc'] = $result['busc'] + 1;
}
if ($i !== (count($busResult) - 1) / 2) {
$resultItem['busc'] = $busResult[($i * 2 + 1)]['status'] == 'busc';
if ($resultItem['busc']) {
$result['busc'] = $result['busc'] + 1;
}
}
}
array_push($result['busInfo'], $resultItem);
}
return $result;
}
4 最后格式化完成后的json数据
公交线路
{
"responseCode": 0,
"responseMessage": "查询成功",
"data": [
{
"name": "428",
"uuid": "4907320871547002333",
"busLine": "天通北苑-地铁龙泽站"
},
{
"name": "428",
"uuid": "5415569149649522461",
"busLine": "地铁龙泽站-天通北苑"
}
]
}
公交信息(其中busc表示途中车辆,buss表示到站车辆)
{
"responseCode": 0,
"responseMessage": "查询成功",
"data": {
"time": "天通北苑 5:30-23:00 分段计价 所属客一分公司",
"busInfo": [
{
"busName": "天通北苑",
"buss": false,
"busc": false
},
{
"busName": "天通东苑三区西门",
"buss": false,
"busc": false
},
{
"busName": "天通北苑三区南门",
"buss": false,
"busc": false
},
{
"busName": "天通北苑三区南",
"buss": false,
"busc": false
},
{
"busName": "天通北苑二区东门",
"buss": false,
"busc": false
},
{
"busName": "狮子营西门",
"buss": false,
"busc": false
},
{
"busName": "天通北苑二区北门",
"buss": false,
"busc": false
},
{
"busName": "天通北苑一区北门",
"buss": false,
"busc": false
},
{
"busName": "地铁天通苑北站南",
"buss": false,
"busc": false
},
{
"busName": "东三旗",
"buss": false,
"busc": false
},
{
"busName": "半截塔村东站",
"buss": false,
"busc": false
},
{
"busName": "半截塔村北站",
"buss": false,
"busc": false
},
{
"busName": "魏窑村",
"buss": false,
"busc": false
},
{
"busName": "绿野福苑",
"buss": false,
"busc": false
},
{
"busName": "小辛庄东",
"buss": false,
"busc": false
},
{
"busName": "小辛庄",
"buss": false,
"busc": false
},
{
"busName": "小辛庄西",
"buss": false,
"busc": false
},
{
"busName": "龙锦苑东二区北门",
"buss": false,
"busc": false
},
{
"busName": "上坡路口西",
"buss": false,
"busc": false
},
{
"busName": "和谐家园一区北门",
"buss": false,
"busc": false
},
{
"busName": "龙锦苑二区",
"buss": false,
"busc": false
},
{
"busName": "田园风光雅苑",
"buss": false,
"busc": false
},
{
"busName": "龙锦苑四区",
"buss": false,
"busc": false
},
{
"busName": "马连店南口",
"buss": false,
"busc": false
},
{
"busName": "龙禧苑三区北门",
"buss": false,
"busc": false
},
{
"busName": "龙禧苑三区路口西",
"buss": false,
"busc": false
},
{
"busName": "回龙观公交场站",
"buss": false,
"busc": false
},
{
"busName": "风雅园北",
"buss": false,
"busc": false
},
{
"busName": "三合庄园",
"buss": false,
"busc": false
},
{
"busName": "龙华园",
"buss": false,
"busc": true
},
{
"busName": "龙华园南区",
"buss": false,
"busc": false
},
{
"busName": "地铁龙泽站",
"buss": false
}
],
"busc": 1
}
}
总的来说,比较简单,需要注意的就是两个接口关联的地方uuid,以及span与i标签的关系