Verwendung eines PHP-Crawlers zur Analyse der Immobilienpreise in Nanjing
Ich habe vor ein paar Tagen einen Artikel über csdn gesehen, in dem mit Python ein Crawler zur Analyse der Immobilienpreise in Shanghai geschrieben wurde. Es fühlt sich ziemlich interessant an. Zufälligerweise habe ich kürzlich über die Artikelsammlung geschrieben, als ich über das Snake-Backend schrieb. Ich werde auch den PHP-Crawler verwenden, um die Immobilienpreise in Nanjing zu analysieren. Fangen wir an.
Die Abhängigkeitsdateien dieses Crawlers: Die erste ist die CURL-Klasse von ares333. Ich verwende eine frühe Version. Dies ist die Github-Projektadresse von https://github.com/ares333/php-curlmulti. Der Curl, den er geschrieben hat, ist wirklich großartig!
Die Sammlung verwendet phpQuery. Wenn Sie diese Kategorie nicht kennen, können Sie sie selbst auf Baidu durchsuchen.
Als Datenquelle habe ich mich für Anjuke entschieden. Die Datenmenge ist immer noch akzeptabel. Ich habe den Anjuke-Kanal nach Nanjing geöffnet. Beginnen Sie mit der Analyse ihrer Seitenstruktur. Was die Verwendung von phpQuery zur Analyse der Seitenstruktur-Erfassungsmethode betrifft, werde ich hier keine detaillierte Einführung geben. Nachdem Sie die Struktur analysiert haben, beginnen Sie mit dem Erstellen der Datentabelle. Erstellen Sie zunächst eine Bereichstabelle. Die Abschnittstabellenstruktur ist wie folgt: NULL AUTO_INCREMENT,CREATE TABLE `area` (<br> `id` int(11) NOT NULL AUTO_INCREMENT,<br> `name` varchar(155) NOT NULL COMMENT '南京市区',<br> `url` varchar(155) NOT NULL COMMENT '房源区域连接',<br> `pid` int(2) NOT NULL COMMENT '分类',<br> PRIMARY KEY (`id`)<br>) ENGINE=MyISAM DEFAULT CHARSET=utf8;
`name` varchar(155) NOT NULL COMMENT 'Stadtgebiet Nanjing',
`url` varchar(155) NOT NULL COMMENT 'Hausbereichsverbindung', `pid` int(2 ) NOT NULL COMMENT 'Classification',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8; Ich habe zuerst selbst einige regionale Serverdaten hinzugefügt. Tatsächlich kann ich diese sammeln weil es so ist Diese Bereiche hatten begrenzte Adressen, also wurden sie direkt hinzugefügt. 14 Daten wurden hinzugefügt:
<?php // +---------------------------------------------------------------------- // | 采集区域脚本 // +---------------------------------------------------------------------- // | Author: NickBai <1902822973@qq.com> // +---------------------------------------------------------------------- set_time_limit(0); require 'init.php'; //根据大区信息前往抓取 $sql = "select * from `area`"; $area = $db->query( $sql )->fetchAll( PDO::FETCH_ASSOC ); foreach($area as $key=>$vo){ $url = $vo['url']; $result = $curl->read($url); $charset = preg_match("/<meta.+?charset=[^\w]?([-\w]+)/i", $result['content'], $temp) ? strtolower( $temp[1] ) : ""; phpQuery::$defaultCharset = $charset; //设置默认编码 $html = phpQuery::newDocumentHTML( $result['content'] ); $span = $html['.items .sub-items a']; $st = $db->prepare("insert into area(name,url,pid) values(?,?,?)"); foreach($span as $v){ $v = pq( $v ); //为方便分页抓取,先加入分页规则 $href = trim( $v->attr('href') ) . 'p*/#filtersort'; $st->execute([ trim( $v->text() ), $href, $vo['id']]); } }
Nachdem die ersten Daten vorliegen, können Sie mit der Erfassung aller regionalen Blockeingangsadressen beginnen. Fügen Sie den Code area.php
百家湖 http://nanjing.anjuke.com/sale/baijiahu/p*/#filtersort
Das einzelne erfasste Datenelement ist wie folgt:
CREATE TABLE `hdetail` ( `id` int(11) NOT NULL AUTO_INCREMENT, `pid` int(5) NOT NULL COMMENT '区域id', `square` int(10) DEFAULT NULL COMMENT '面积', `housetype` varchar(55) DEFAULT '' COMMENT '房屋类型', `price` int(10) DEFAULT '0' COMMENT '单价', `allprice` int(10) DEFAULT '0' COMMENT '总价', `name` varchar(155) DEFAULT '' COMMENT '小区名称', `addr` varchar(155) DEFAULT '' COMMENT '小区地址', PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
Das Datenadresse ist verfügbar, und ich habe der Seitenadresse ein * hinzugefügt, damit es ersetzt werden kann. Wenn Sie das Programm öffnen, können Sie mit dem Sammeln von Dokumenten auf anderen Seiten unter jedem Modul beginnen. Das wichtigste Hauptprogramm beginnt gleich
Erstellen Sie eine neue HD-Detailtabelle, um die gesammelten Hausnummerninformationen aufzuzeichnen:<?php // +---------------------------------------------------------------------- // | 采集各区具体房源信息 // +---------------------------------------------------------------------- // | Author: NickBai <1902822973@qq.com> // +---------------------------------------------------------------------- set_time_limit(0); require 'init.php'; //查询各板块数据 $sql = "select * from `area` where id > 14"; $allarea = $db->query($sql)->fetchAll( PDO::FETCH_ASSOC ); ////m.sbmmt.com/页面不存在时,会跳转到首页 foreach($allarea as $key=>$vo){ $url = $vo['url']; $i = 1; while ( true ){ $urls = str_replace( "*" , $i, $url); $result = $curl->read( $urls ); if( "http://nanjing.anjuke.com/sale/" == $result['info']['url'] ){ break; } $charset = preg_match("/<meta.+?charset=[^\w]?([-\w]+)/i", $result['content'], $temp) ? strtolower( $temp[1] ) : ""; phpQuery::$defaultCharset = $charset; //设置默认编码 $html = phpQuery::newDocumentHTML( $result['content'] ); $p = $html['#houselist-mod li .house-details']; $isGet = count( $p->elements ); //未采集到内容跳出,视为结束 if( 0 == $isGet ){ break; } foreach($p as $v){ $sql = "insert into hdetail(pid,square,housetype,price,allprice,name,addr) "; $pid = $vo['id']; $square = rtrim( trim( pq($v)->find("p:eq(1) span:eq(0)")->text() ), "平方米"); $htype = trim( pq($v)->find("p:eq(1) span:eq(1)")->text() ); $price = rtrim ( trim( pq($v)->find("p:eq(1) span:eq(2)")->text() ), "元/m²"); $area = explode(" ", trim( pq($v)->find("p:eq(2) span")->text() ) ); $name = str_replace( chr(194) . chr(160), "", array_shift($area) ); //utf-8中的空格无法用trim去除,所以采用此方法 $addr = rtrim( ltrim (trim( array_pop($area) ) , "["), "]" ); $allprice = trim( pq($v)->siblings(".pro-price")->find("span strong")->text() ); $sql .= " value( ". $pid .",". $square .", '". $htype ."' ,". $price .",". $allprice .", '". $name ."' ,'". $addr ."' )"; $db->query($sql); } echo mb_convert_encoding($vo['name'], "gbk", "utf-8") . " PAGE : ". $i . PHP_EOL; $i++; } }
house.php
$sql = "select * from `area` where id > 14";
Wenn Sie das Gefühl haben, dass es langsam ist, können Sie einige Teile der Datei house.php kopieren,
<?php require "init.php"; $data = unserialize( file_get_contents('./data/nj.data') ); if( empty( $data ) ){ //全南京 $sql = "select avg(price) price from hdetail"; $nanjing = intval( $db->query($sql)->fetch( PDO::FETCH_ASSOC )['price'] ); //其余数据 $data = [ $nanjing, getOtherPrice('2,3,4,5,6,7,8,10'), getOtherPrice('1'), getOtherPrice('2'), getOtherPrice('3'), getOtherPrice('4'), getOtherPrice('5'), getOtherPrice('6'), getOtherPrice('7'), getOtherPrice('8'), getOtherPrice('9'), getOtherPrice('10'), getOtherPrice('11'), getOtherPrice('12'), getOtherPrice('13') ]; //添加缓存 file_put_contents('./data/nj.data', serialize( $data )); } //均价最高TOP10 $sql = "select avg(price) price,name from hdetail GROUP BY name ORDER BY price desc limit 10"; $res = $db->query($sql)->fetchAll( PDO::FETCH_ASSOC ); $x = ""; $y = ""; foreach($res as $vo){ $x .= "'" . $vo['name'] . "',"; $y .= intval( $vo['price'] ). ","; } //均价最低TOP10 $sql = "select avg(price) price,name from hdetail GROUP BY name ORDER BY price asc limit 10"; $res = $db->query($sql)->fetchAll( PDO::FETCH_ASSOC ); $xl = ""; $yl = ""; foreach($res as $vo){ $xl .= "'" . $vo['name'] . "',"; $yl .= intval( $vo['price'] ). ","; } //交易房型数据 $sql = "select count(0) allnum, housetype from hdetail GROUP BY housetype order by allnum desc"; $res = $db->query($sql)->fetchAll( PDO::FETCH_ASSOC ); $htype = ""; foreach($res as $vo){ $htype .= "[ '" . $vo['housetype'] . "', " .$vo['allnum']. "],"; } $htype = rtrim($htype, ','); //交易的房屋面积数据 $square = ['50平米以下', '50-70平米', '70-90平米', '90-120平米', '120-150平米', '150-200平米', '200-300平米', '300平米以上']; $sql = "select count(0) allnum, square from hdetail GROUP BY square"; $squ = $db->query($sql)->fetchAll( PDO::FETCH_ASSOC ); $p50 = 0; $p70 = 0; $p90 = 0; $p120 = 0; $p150 = 0; $p200 = 0; $p250 = 0; $p300 = 0; foreach($squ as $key=>$vo){ if( $vo['square'] < 50 ){ $p50 += $vo['allnum']; } if( $vo['square'] >= 50 && $vo['square'] < 70 ){ $p70 += $vo['allnum']; } if( $vo['square'] >= 70 && $vo['square'] < 90 ){ $p90 += $vo['allnum']; } if( $vo['square'] >= 90 && $vo['square'] < 120 ){ $p120 += $vo['allnum']; } if( $vo['square'] >= 120 && $vo['square'] < 150 ){ $p150 += $vo['allnum']; } if( $vo['square'] >= 150 && $vo['square'] < 200 ){ $p200 += $vo['allnum']; } if( $vo['square'] >= 200 && $vo['square'] < 300 ){ $p250 += $vo['allnum']; } if( $vo['square'] >= 300 ){ $p300 += $vo['allnum']; } } $num = [ $p50, $p70, $p90, $p120, $p150, $p200, $p250, $p300 ]; $sqStr = ""; foreach($square as $key=>$vo){ $sqStr .= "[ '" . $vo . "', " .$num[$key]. "],"; } //根据获取ids字符串获取对应的均价信息 function getOtherPrice($str){ global $db; $sql = "select id from area where pid in(" . $str . ")"; $city = $db->query($sql)->fetchAll( PDO::FETCH_ASSOC ); $ids = ""; foreach($city as $v){ $ids .= $v['id'] . ","; } $sql = "select avg(price) price from hdetail where pid in (".rtrim($ids, ",").")"; $price = intval( $db->query($sql)->fetch( PDO::FETCH_ASSOC )['price'] ); return $price; } ?> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>南京房价分析</title> <link rel="shortcut icon" href="favicon.ico"> <link href="css/bootstrap.min.css?v=3.3.6" rel="stylesheet"> <link href="css/font-awesome.min.css?v=4.4.0" rel="stylesheet"> <link href="css/animate.min.css" rel="stylesheet"> <link href="css/style.min.css?v=4.1.0" rel="stylesheet"> </head> <body class="gray-bg"> <p class="wrapper wrapper-content"> <p class="row"> <p class="col-sm-12"> <p class="row"> <p class="col-sm-12"> <p class="ibox float-e-margins"> <p class="ibox-title"> <h5>全南京以及各区二手房均价</h5> <p class="ibox-tools"> <a class="collapse-link"> <i class="fa fa-chevron-up"></i> </a> <a class="close-link"> <i class="fa fa-times"></i> </a> </p> </p> <p class="ibox-content"> <p id="container"></p> </p> </p> </p> </p> </p> </p> <p class="row"> <p class="col-sm-6"> <p class="row"> <p class="col-sm-12"> <p class="ibox float-e-margins"> <p class="ibox-title"> <h5>均价最高的小区TOP10</h5> <p class="ibox-tools"> <a class="collapse-link"> <i class="fa fa-chevron-up"></i> </a> <a class="close-link"> <i class="fa fa-times"></i> </a> </p> </p> <p class="ibox-content"> <p id="avgpriceh"></p> </p> </p> </p> </p> </p> <p class="col-sm-6"> <p class="row"> <p class="col-sm-12"> <p class="ibox float-e-margins"> <p class="ibox-title"> <h5>均价最低的小区TOP10</h5> <p class="ibox-tools"> <a class="collapse-link"> <i class="fa fa-chevron-up"></i> </a> <a class="close-link"> <i class="fa fa-times"></i> </a> </p> </p> <p class="ibox-content"> <p id="avgpricel"></p> </p> </p> </p> </p> </p> </p> <p class="row"> <p class="col-sm-6"> <p class="row"> <p class="col-sm-12"> <p class="ibox float-e-margins"> <p class="ibox-title"> <h5>交易房型比例</h5> <p class="ibox-tools"> <a class="collapse-link"> <i class="fa fa-chevron-up"></i> </a> <a class="close-link"> <i class="fa fa-times"></i> </a> </p> </p> <p class="ibox-content"> <p id="htype"></p> </p> </p> </p> </p> </p> <p class="col-sm-6"> <p class="row"> <p class="col-sm-12"> <p class="ibox float-e-margins"> <p class="ibox-title"> <h5>交易房屋面积比例</h5> <p class="ibox-tools"> <a class="collapse-link"> <i class="fa fa-chevron-up"></i> </a> <a class="close-link"> <i class="fa fa-times"></i> </a> </p> </p> <p class="ibox-content"> <p id="square"></p> </p> </p> </p> </p> </p> </p> </p> <script type="text/javascript" src="js/jquery.min.js?v=2.1.4"></script> <script type="text/javascript" src="js/bootstrap.min.js?v=3.3.6"></script> <script type="text/javascript" src="http://cdn.hcharts.cn/highcharts/highcharts.js"></script> <script type="text/javascript"> $(function () { $('#container').highcharts({ chart: { type: 'column' }, title: { text: '全南京以及各区二手房均价' }, subtitle: { text: '来源于安居客8.16的数据' }, xAxis: { categories: ['全南京','江南八区','江宁区','鼓楼区','白下区','玄武区','建邺区','秦淮区','下关区','雨花台区','浦口区','栖霞区','六合区', '溧水区','高淳区','大厂'], crosshair: true }, yAxis: { min: 0, title: { text: '元/m²' } }, tooltip: { headerFormat: '<span style="font-size:10px">{point.key}</span><table>', pointFormat: '<tr><td style="color:{series.color};padding:0">{series.name}: </td>' + '<td style="padding:0"><b>{point.y:.1f} 元/m²</b></td></tr>', footerFormat: '</table>', shared: true, useHTML: true }, plotOptions: { column: { pointPadding: 0.2, borderWidth: 0, dataLabels:{ enabled:true// dataLabels设为true } } }, series: [{ name: '平均房价', data: [<?php echo implode(',', $data); ?>] }] }); //均价最高top10 $('#avgpriceh').highcharts({ chart: { type: 'column' }, title: { text: '均价最高的小区TOP10' }, subtitle: { text: '来源于安居客8.16的数据' }, xAxis: { categories: [<?=$x; ?>], crosshair: true }, yAxis: { min: 0, title: { text: '元/m²' } }, tooltip: { headerFormat: '<span style="font-size:10px">{point.key}</span><table>', pointFormat: '<tr><td style="color:{series.color};padding:0">{series.name}: </td>' + '<td style="padding:0"><b>{point.y:.1f} 元/m²</b></td></tr>', footerFormat: '</table>', shared: true, useHTML: true }, plotOptions: { column: { pointPadding: 0.2, borderWidth: 0, dataLabels:{ enabled:true// dataLabels设为true } } }, series: [{ name: '平均房价', data: [<?=$y; ?>] }] }); //均价最低top10 $('#avgpricel').highcharts({ chart: { type: 'column' }, title: { text: '均价最低的小区TOP10' }, subtitle: { text: '来源于安居客8.16的数据' }, xAxis: { categories: [<?=$xl; ?>], crosshair: true }, yAxis: { min: 0, title: { text: '元/m²' } }, tooltip: { headerFormat: '<span style="font-size:10px">{point.key}</span><table>', pointFormat: '<tr><td style="color:{series.color};padding:0">{series.name}: </td>' + '<td style="padding:0"><b>{point.y:.1f} 元/m²</b></td></tr>', footerFormat: '</table>', shared: true, useHTML: true }, plotOptions: { column: { pointPadding: 0.2, borderWidth: 0, dataLabels:{ enabled:true// dataLabels设为true } } }, series: [{ name: '平均房价', data: [<?=$yl; ?>] }] }); // Radialize the colors Highcharts.getOptions().colors = Highcharts.map(Highcharts.getOptions().colors, function (color) { return { radialGradient: { cx: 0.5, cy: 0.3, r: 0.7 }, stops: [ [0, color], [1, Highcharts.Color(color).brighten(-0.3).get('rgb')] // darken ] }; }); //房型类型 $('#htype').highcharts({ chart: { plotBackgroundColor: null, plotBorderWidth: null, plotShadow: false }, title: { text: '交易的二手房型比例' }, tooltip: { pointFormat: '{series.name}: <b>{point.percentage:.1f}%</b>' }, plotOptions: { pie: { allowPointSelect: true, cursor: 'pointer', dataLabels: { enabled: true, format: '<b>{point.name}</b>: {point.percentage:.1f} %', style: { color: (Highcharts.theme && Highcharts.theme.contrastTextColor) || 'black' }, connectorColor: 'silver' } } }, series: [{ type: 'pie', name: 'Browser share', data: [ <?=$htype; ?> ] }] }); //房型面积类型 $('#square').highcharts({ chart: { plotBackgroundColor: null, plotBorderWidth: null, plotShadow: false }, title: { text: '交易的二手房面积比例' }, tooltip: { pointFormat: '{series.name}: <b>{point.percentage:.1f}%</b>' }, plotOptions: { pie: { allowPointSelect: true, cursor: 'pointer', dataLabels: { enabled: true, format: '<b>{point.name}</b>: {point.percentage:.1f} %', style: { color: (Highcharts.theme && Highcharts.theme.contrastTextColor) || 'black' }, connectorColor: 'silver' } } }, series: [{ type: 'pie', name: 'Browser share', data: [ <?=$sqStr; ?> ] }] }); }); </script> </body> </html>
Jetzt heißt es warten. Ich habe es am 16.8. gesammelt und insgesamt 311.226 Daten gesammelt. Okay, jetzt, da wir die Zahlen haben, können wir mit der Analyse beginnen. Der von mir analysierte Code lautet wie folgt:
Der Seiteneffekt ist wie folgt: