首先我得道歉,因为我做了一回标题党。但我想也并没让你失望吧,虽然没有现在成的sql,但亲写的php脚本同样可以生成烫手的sql!
本博客参考中国统计局2017最新数据进行编写的,有必要的话你可以改成对应最新版本即可。
准备:一张表(id,pid,url,level,name)
注意:本脚本默认获取三级,最多可获取到4级,自行调整!由于平台限制,可能得刷新请求大概5次左右才能完全下载完省市区(直接返回空数组,即下载完成)
技术点:运用了递归和正则匹配,特别的名称进行了处理(如:省直辖县级行政区划、市辖区、县),至于数据库看着替换上即可!
以下代码附上:
function curl_get($url){
//初始化
$curl = curl_init();
//设置抓取的url
curl_setopt($curl, CURLOPT_URL, $url);
//设置头文件的信息作为数据流输出
curl_setopt($curl, CURLOPT_HEADER, 1);
//设置获取的信息以文件流的形式返回,而不是直接输出。
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
//执行命令
$data = curl_exec($curl);
if (curl_errno($curl)) {
$curl_error = curl_error($curl);
throw new Exception ($curl_error, 0);
} else {
$httpStatusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if (200 !== $httpStatusCode) {
throw new Exception ($data, $httpStatusCode);
}
}
//关闭URL请求
curl_close($curl);
//显示获得的数据
return $data;
}
function getAddress($url, $pid=0, $level=1, $p_name , $data=array()){
try{
// 拼接请求链接
$html = curl_get($url);
// 以下为节点要点字眼,选择自己要的地区等级字眼则可
// provincetr|citytr|countytr|towntr|villagetr
preg_match_all('/(provincetr|citytr|countytr).*<td(.*)td/i',$html,$res1);
if($res1){
$tr = $res1[0][0];
preg_match_all('/<td.*?>.*?href=[\'|"](.*?)[\'|"]>(.*?)(<\/br>)?<\/a><\/td/',$tr,$res2);
foreach ($res2[2] as $key => $value) {
$curll_val = $res2[1][$key];
$curll_url = substr($url,0, strrpos($url, '/') + 1) . $curll_val;
$select_sql = "select * from test_region where url='{$curll_val}'";
$exist = $GLOBALS['db_separate']->getRow($select_sql);
// 如果已经存在该父级节点,则跳过
if ($exist){
continue;
}
$value = trim(strip_tags($value));
if (!is_numeric($value) && !empty($value)) {
// 转换成数据库的编码
$encode = mb_detect_encoding($value, array('ASCII','UTF-8','GB2312','GBK','BIG5'));
// EUC-CN
$value = iconv( $encode, 'utf-8',$value);
// 湖北仙桃 河南济源 海南五指山 文昌等
if($value == '省直辖县级行政区划'){ // 向上推一级
$data += getAddress($curll_url, $pid, $level, $value); // 可选择性去掉
} else {
// 每个顶级节点做一次事务,从而保障就算出错也不会出现漏数据
$level == 1 && $GLOBALS['db_separate']->beginTransaction();
// 如果是市辖区或只有县,则用低级名称
//县 重庆城口 丰都等
if($value == '市辖区' || $value == '县'){ // 替换成上级的名称
$value = $p_name;
}
// 初始保证变量没被污染
$sub_pid = '';
if($value == '重庆市'){
$sql = "select id from test_region where level='{$level}' and region_name='{$value}'";
$sub_pid = $GLOBALS['db_separate']->getOne($sql);
}
if(empty($sub_pid)){
$row = array('region_name'=>trim(strip_tags($value)), 'url'=>$curll_val, 'level'=>$level, 'pid'=>$pid);
// 向数据库插入一条数据
$GLOBALS['db_separate']->insert('test_region', $row);
// 把当前的id作为自己子级的父ID
$sub_pid = $GLOBALS['db_separate']->lastInsertId();
}
// 递归查找自己的子级
$row['child'] = getAddress($curll_url, $sub_pid, $level +1, $value);
$data[] = $row;
// 如果该顶节点回调完才会提交自己的事务
$level == 1 && $GLOBALS['db_separate']->commit();
}
}
// file_put_contents('res.txt', var_export($data,true));
}
}
}catch (Exception $e){
// 如果帮忙错,则回滚事务再刷新当前页,以此来继续操作插入完成未处理的数据
echo '出错了哦~' . $e->getMessage();
$reload_srcipt = "<script>location.reload()</script>";
try{
$GLOBALS['db_separate']->rollBack(); // 必须先回滚再刷新
echo $reload_srcipt;
}catch (Exception $e_sub){
echo $reload_srcipt;
}
}
// 返回本次处理过的结果集
return $data;
}
set_time_limit(0);
$url = 'http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2017/index.html';
$data = getAddress($url);
var_dump($data);