Skip to content

Instantly share code, notes, and snippets.

@ProgerXP
Last active September 6, 2018 08:43
Show Gist options
  • Save ProgerXP/6011495 to your computer and use it in GitHub Desktop.
Save ProgerXP/6011495 to your computer and use it in GitHub Desktop.
Map grabber :: Yandex.Maps + jQuery + PHP/DOMDocument/XPathby Proger_XP :: http://proger.meOriginal article (Russian): http://habrahabr.ru/post/184334/
<?php
// Map grabber :: Yandex.Maps + jQuery + PHP/DOMDocument/XPath
// by Proger_XP :: http://proger.me
// Original article (Russian): http://habrahabr.ru/post/184334/
// In public domain. I appreciate backlinks.
function dl($url) {
require_once 'sqobot/lib/downwind.php';
return new Downwind($url, array(
'User-Agent' => 'Mozilla/5.0 (rv:29.0) Gecko/20100101 Firefox/29.0',
'Accept-Language' => 'ru-ru,ru;q=0.8,en-us;q=0.5,en;q=0.3',
'Accept-Encoding' => 'gzip, deflate',
'Accept-Charset' => 'windows-1251 utf-8 koi8-u big-5 euc-jp shift-jis iso-8859-1',
'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
));
}
function parse($html) {
$html = '<?xml encoding="UTF-8">'.iconv('cp1251', 'utf-8', $html);
$doc = new DOMDocument('1.0', 'utf-8');
@$doc->loadHTML($html) or die('loadHTML: '.$html);
//die($doc->saveHTML());
$xpath = new DOMXPath($doc);
$nodes = $xpath->query('//table[@class="results"]/tr[th[@class="head_kvart"] or td[@width or @class="tooltip"]]');
$results = array();
$roomCount = 1;
foreach ($nodes as $row) {
$cells = array();
$cell = $row->firstChild;
while ($cell) {
$cell->nodeType == XML_ELEMENT_NODE and $cells[] = trim($cell->nodeValue);
$cell = $cell->nextSibling;
}
if (count($cells) == 1) {
$roomCount = (int) reset($cells);
} else {
$cells[0] = $roomCount;
$html = $row->ownerDocument->saveXML($row);
if (count($cells) == 10) {
array_splice($cells, 6, 1, array(0, '', $cells[6], ''));
}
if (preg_match('~<a href="([^"]+)~u', $html, $match)) {
array_unshift($cells, 'http://www.bn.ru'.$match[1]);
} else {
array_unshift($cells, '');
}
$keys = array('url', 'rooms', 'address', 'floors', 'houseType', 'area', 'areaLiving', 'areaKitchen', 'toilet', 'price', 'conditions', 'seller', 'phone', 'notes');
$result[] = array_combine($keys, $cells);
}
}
return $result;
}
$regions = array(
1 => 'Адмиралтейский',
6 => 'Василеостровский',
7 => 'Выборгский',
39 => 'Калининский',
8 => 'Кировский',
271 => 'Колпинский',
40 => 'Красногвардейский',
41 => 'Красносельский',
272 => 'Кронштадтский',
273 => 'Курортный',
42 => ' Московский',
43 => 'Невский',
100 => 'Область',
44 => 'Петроградский',
275 => 'Петродворцовый',
45 => 'Приморский',
276 => 'Пушкинский',
46 => 'Фрунзенский',
47 => 'Центральный',
);
set_time_limit(180);
if (!empty($_REQUEST['find'])) {
$all = $counts = array();
$baseURL = 'http://www.bn.ru/zap_fl.phtml?print=printall&';
$empty = iconv('utf-8', 'cp1251', 'Количество найденных вариантов 0');
foreach ((array) $_REQUEST['region'] as $region) {
$url = $baseURL."region$region=$region&";
foreach (range(0, 10000, 1000) as $price0) {
if ($price0 + 1000 >= $_REQUEST['price0'] and
$price0 - 1000 <= $_REQUEST['price1']) {
$reqURL = $url."price1=$price0&price2=".($price0 + 999);
$data = dl($reqURL)->fetchData();
$counts[$price0] = 0;
if (!strpos($data, $empty)) {
$offers = parse($data);
$counts[$price0] = count($offers);
$all = array_merge($all, $offers);
}
usleep(200000);
}
}
}
}
false and
$all = array( 0 => array ( 'url' => 'http://www.bn.ru/detail/flats/17469.html?from=search', 'rooms' => 1, 'address' => '7 Советская ул. 32/25', 'floors' => '1\\5', 'houseType' => 'СФ', 'area' => '30', 'areaLiving' => '18.3', 'areaKitchen' => '6', 'toilet' => ' ', 'price' => '3100', 'conditions' => ' ', 'seller' => 'АмпиР Недвижимость', 'phone' => '(965) 0165440', 'notes' => 'ПП свободна ХС торг', ), 1 => array ( 'url' => 'http://www.bn.ru/detail/pflats/168605.html?from=psearch', 'rooms' => 1, 'address' => 'Ломоносова ул. 14', 'floors' => '1/6', 'houseType' => 'К', 'area' => '38', 'areaLiving' => '36', 'areaKitchen' => 0, 'toilet' => '', 'price' => '3300 тыс.руб.', 'conditions' => '', 'seller' => 'частное', 'phone' => '(961) 8114432', 'notes' => 'Две 1-комн. Квартиры продаются в...', ), 2 => array ( 'url' => 'http://www.bn.ru/detail/flats/230375.html?from=search', 'rooms' => 1, 'address' => 'Лиговский пр. 259', 'floors' => '1\\5', 'houseType' => 'К', 'area' => '32', 'areaLiving' => '18', 'areaKitchen' => '6', 'toilet' => 'С', 'price' => '3300', 'conditions' => ' ', 'seller' => 'АН Альтаир', 'phone' => '9626023', 'notes' => '(921) 348-4194 комерц расп', ),);
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Map grabber</title>
<script src="//api-maps.yandex.ru/2.0/?load=package.standard&lang=ru-RU" type="text/javascript"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<style>
html, body, .regions, #map { margin: 0; padding: 0; }
form { margin: 1em; }
.regions { -moz-column-width: 11em; list-style-position: inside; }
p[data-marker] { display: inline-block; margin: 3px; }
p[data-marker] img { vertical-align: bottom; height: 24px; }
p input[type=submit]:first-child { font-weight: bold; }
#map { width: 100%; height: 1000px; }
.ymaps-b-balloon__content-body table {
border-collapse: collapse;
}
.ymaps-b-balloon__content-body tr > * {
padding: 0 6px;
text-align: center;
}
</style>
</head>
<body>
<form action="magrabx.php" method="get">
<ul class="regions">
<?foreach ($regions as $id => $title) {?>
<li><label>
<input type="checkbox" name="region[]" value="<?=$id?>"
<?=in_array($id, (array) @$_REQUEST['region']) ? 'checked="checked"' : ''?>>
<?=$title?>
</label></li>
<?}?>
</ul>
<p>
<b>Цена:</b>
от <input name="price0" value="<?=@$_REQUEST['price0'] ?: 1000?>">
до <input name="price1" value="<?=@$_REQUEST['price1'] ?: 4600?>">
</p>
<p>
<input type="submit" name="find" value="Найти">
<button type="button" onclick="postJS()">Обновить маркеры</button>
<?if (isset($all)) {?>
<b><?=count($all)?> квартир</b> (без фильтра по цене)
/ <?=count($counts)?> запросов
<?}?>
</p>
<?foreach (array('twirl#blueDotIcon', 'twirl#orangeDotIcon', 'twirl#darkblueDotIcon', 'twirl#pinkDotIcon', 'twirl#whiteDotIcon', 'twirl#greyDotIcon', 'twirl#darkgreenDotIcon', 'twirl#redDotIcon', 'twirl#darkorangeDotIcon', 'twirl#violetDotIcon', 'twirl#greenDotIcon', 'twirl#yellowDotIcon', 'twirl#lightblueDotIcon', 'twirl#brownDotIcon', 'twirl#nightDotIcon', 'twirl#blackDotIcon') as $marker) {?>
<p data-marker="<?=$marker?>">
<img src="http://api.yandex.ru/maps/doc/jsapi/2.x/ref/images/styles/dot<?=substr($marker, 6, strpos($marker, 'Dot') - 6)?>.png">
<input placeholder="javascript..." name="marker[<?=$marker?>]" value="<?=htmlspecialchars(@$_REQUEST['marker'][$marker], ENT_COMPAT, 'utf-8')?>">
</p>
<?}?>
</form>
<div id="map"></div>
<?if (isset($all)) {?>
<script type="text/javascript">
function MultiGeocoder(options) {
this._options = options || {};
}
MultiGeocoder.prototype.geocode = function (requests, options) {
var self = this,
opts = ymaps.util.extend({}, self._options, options),
size = requests.length,
promise = new ymaps.util.Promise(),
result = [],
geoObjects = new ymaps.GeoObjectArray();
requests.forEach(function (request, index) {
ymaps.geocode(request, opts).then(
function (response) {
var geoObject = response.geoObjects.get(0);
geoObject && (result[index] = geoObject);
--size || (result.forEach(geoObjects.add, geoObjects), promise.resolve({ geoObjects: geoObjects }));
},
function (err) {
promise.reject(err);
}
);
});
return promise;
};
var coords = []
var info = []
<?foreach ($all as $offer) {?>
<?if ($offer['price'] >= @$_REQUEST['price0'] and
$offer['price'] <= @$_REQUEST['price1']) {?>
coords.push('Санкт-Петербург, <?=$offer['address']?>')
info.push(<?=json_encode($offer, JSON_UNESCAPED_UNICODE)?>)
<?}?>
<?}?>
function init() {
var map = new ymaps.Map('map', {
center: [59.932666, 30.329596],
zoom: 13,
behaviors: ['default', 'scrollZoom'],
})
window.map = map
map.controls
.add('zoomControl', {left: 5, top: 5})
.add('mapTools', {left: 35, top: 5});
coder = new MultiGeocoder({boundedBy: map.getBounds()});
var titles = {price: 'Цена', rooms: 'Комнат', area: 'Площадь',
areaLiving: 'Жилая', areaKitchen: 'Кухня',
floors: 'Этаж', houseType: 'Дом'}
coder
.geocode(coords)
.then(
function (res) {
for (var i = 0; i < res.geoObjects.getLength(); i++) {
var cells = info[i]
var text = '<p style="margin-top: 0">' + $('<b>').append($('<a>').attr({href: cells.url, target: '_blank'}).text(cells.address))[0].outerHTML + '</p>'
var th = []
var td = []
for (var key in titles) {
th.push('<th>' + titles[key] + '</th>')
td.push('<td>' + cells[key] + '</td>')
}
text += '<table><tr>' + th.join('') + '</tr>' +
'<tr>' + td.join('') + '</tr></table>';
text += $('<p style="margin-bottom: 0">').text(cells.notes)[0].outerHTML
var geo = res.geoObjects.get(i)
info[i].geo = geo
geo.properties.set('balloonContentBody', text)
geo.events.add('balloonopen', function () {
this.options.set('preset', 'twirl#greyIcon')
}, geo)
}
map.geoObjects.add(res.geoObjects)
setTimeout(postJS, 100)
},
function (err) { alert(err) }
)
}
function postJS() {
var markers = []
$('p[data-marker] input').val(function (i, value) {
var marker = $(this).parent().attr('data-marker')
value = $.trim(value)
if (value) {
markers.push([marker, value])
}
return value
})
$.each(info, function (i, cells) {
var colored = false
for (var i = 0; i < markers.length && !colored; i++) {
var item = markers[i]
var code = ''
$.each(cells, function (key, value) {
code += 'var ' + key + ' = \'' + (value + '').replace(/([\r\n\/'])/g, '\\$1') + '\';'
})
code += 'return ' + item[1]
var func = new Function('cells', code);
if (func(cells)) {
cells.geo.options.set('preset', item[0])
colored = true
}
}
colored || cells.geo.options.set('preset', 'twirl#blueIcon')
})
}
ymaps.ready(init);
</script>
<?}?>
</body>
</html>
@voznyy
Copy link

voznyy commented Jul 22, 2013

Здравствуйте. Как быть, если количество объектов, которые вернул геокодер не будет совпадать с количеством элементов массива с описанием?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment