initial commit
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
config.php
|
||||
angebote.json*
|
||||
15
config.php.sample
Normal file
15
config.php.sample
Normal file
@@ -0,0 +1,15 @@
|
||||
;<?php
|
||||
;die(); // For further security
|
||||
;/*
|
||||
|
||||
[general]
|
||||
# "url" has to contain page indicator (/P-x/). This is not existent on the first search result page but can be added manually
|
||||
url = "https://www.immobilienscout24.de/Suche/S-2/P-1/Wohnung-Miete/Nordrhein-Westfalen/Koeln/70_6_118_39_20_101_2_50_11_56_120/2,00-3,00/-80,00/EURO--900,00/-/-/false/-/-/true/-/-/true"
|
||||
db = "angebote.json"
|
||||
|
||||
[mail]
|
||||
to = "to@example.com"
|
||||
from = "from@example.com"
|
||||
|
||||
;*/
|
||||
;?>
|
||||
386
index.php
Normal file
386
index.php
Normal file
@@ -0,0 +1,386 @@
|
||||
<?php
|
||||
|
||||
$config = parse_ini_file("config.php", true);
|
||||
$db = $config['general']['db'];
|
||||
$queryurl = $config['general']['url'];
|
||||
|
||||
// make backup of database
|
||||
$db_bak = $db . ".bak_" . date('Y-m-d');
|
||||
if (! file_exists($db_bak)) {
|
||||
copy($db, $db_bak);
|
||||
}
|
||||
|
||||
// Called via CLI or web?
|
||||
if (defined('STDIN')) {
|
||||
$do = $argv[1];
|
||||
} else {
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$do = isset($_POST['do']) ? $_POST['do'] : false;
|
||||
} else {
|
||||
$do = isset($_GET['do']) ? $_GET['do'] : false;
|
||||
}
|
||||
}
|
||||
|
||||
// define action via "do" parameter
|
||||
if ($do === "download") {
|
||||
immo_download($db);
|
||||
} else if ($do === "update") {
|
||||
immo_update($db);
|
||||
} else if ($do === "show" || $do == "") {
|
||||
immo_show($db);
|
||||
} else {
|
||||
echo "No valid do action defined";
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
/// do = DOWNLOAD ///
|
||||
/////////////////////
|
||||
function immo_download($db) {
|
||||
global $data;
|
||||
global $queryurl;
|
||||
global $config;
|
||||
read_db($db);
|
||||
|
||||
$site = xsite($queryurl); // load and transform URL (page 1) to queryable $site
|
||||
|
||||
// get amount of available pages
|
||||
$searchPages = $site->query('//div[@id="pageSelection"]/select');
|
||||
$pages = $searchPages->item(0)->childNodes->length;
|
||||
// create a dynamic url in which the current page number can be set in
|
||||
$queryurldyn = str_replace("/P-1/", "/P-%page%/", $queryurl);
|
||||
|
||||
// loop through available pages
|
||||
for ($page = 1; $page <= $pages; $page++) {
|
||||
$queryurlcur = str_replace("%page%", $page, $queryurldyn);
|
||||
echo "Current search page: " . $queryurlcur . "<br />\n";
|
||||
|
||||
$site = xsite($queryurlcur); // load and transform URL of current page
|
||||
// get all links to expose pages
|
||||
$searchResult = $site->query('//a[@class="result-list-entry__brand-title-container"]/@href');
|
||||
|
||||
// loop through web search results
|
||||
foreach($searchResult as $result){
|
||||
global $data;
|
||||
$new = "y"; // is the search entry new?
|
||||
// extract ID of link
|
||||
preg_match("/\d+$/",$result->textContent, $matches);
|
||||
$id = $matches[0];
|
||||
$id = (int)$id; // convert to int value
|
||||
|
||||
$total = count($data);
|
||||
for ($row = 0; $row < $total; $row++) {
|
||||
if ($id === $data[$row]['id']) {
|
||||
$new = "n"; // entry isn't new anymore
|
||||
}
|
||||
}
|
||||
|
||||
if ($new === "y") { // entry is new
|
||||
|
||||
// load and transform expose URL
|
||||
$site = xsite("https://www.immobilienscout24.de/expose/" . $id);
|
||||
|
||||
// description
|
||||
$searchResult = $site->query('//h1[@id="expose-title"]');
|
||||
$desc = trim($searchResult->item(0)->nodeValue);
|
||||
$desc = mb_convert_encoding($desc, 'UTF-8', 'UTF-8'); // remove/replace invalid characters
|
||||
$descs = substr($desc, 0, 15);
|
||||
$descs = mb_convert_encoding($descs, 'UTF-8', 'UTF-8');
|
||||
|
||||
// flat available from
|
||||
$searchResult = $site->query('//dd[@class="is24qa-bezugsfrei-ab grid-item three-fifths"]');
|
||||
$bezug = trim($searchResult->item(0)->nodeValue);
|
||||
|
||||
// warm rent
|
||||
$searchResult = $site->query('//dd[@class="is24qa-gesamtmiete grid-item three-fifths font-bold"]');
|
||||
$mietew = trim($searchResult->item(0)->nodeValue);
|
||||
|
||||
// cold rent
|
||||
$searchResult = $site->query('//dd[@class="is24qa-kaltmiete grid-item three-fifths"]');
|
||||
$mietek = trim($searchResult->item(0)->nodeValue);
|
||||
|
||||
// rooms
|
||||
$searchResult = $site->query('//dd[@class="is24qa-zimmer grid-item three-fifths"]');
|
||||
$zimmer = trim($searchResult->item(0)->nodeValue);
|
||||
|
||||
// size
|
||||
$searchResult = $site->query('//dd[@class="is24qa-wohnflaeche-ca grid-item three-fifths"]');
|
||||
$qm = trim($searchResult->item(0)->nodeValue);
|
||||
|
||||
// location
|
||||
$searchResult = $site->query('//div[@class="address-block"]');
|
||||
$ort = trim($searchResult->item(0)->nodeValue);
|
||||
$ort = str_replace("(zur Karte) ", "", $ort);
|
||||
$ort = str_replace("Die vollständige Adresse der Immobilie erhalten Sie vom Anbieter.", "", $ort);
|
||||
|
||||
// append new array entry
|
||||
$data[] = array("id" => $id,
|
||||
"date" => date('d.m. H:i'),
|
||||
"desc" => $desc,
|
||||
"descs" => $descs,
|
||||
"bezug" => $bezug,
|
||||
"mietew" => $mietew,
|
||||
"mietek" => $mietek,
|
||||
"ort" => $ort,
|
||||
"zimmer" => $zimmer,
|
||||
"qm" => $qm,
|
||||
"rating" => 0,
|
||||
"status" => "NEU",
|
||||
"note" => "");
|
||||
|
||||
// send mail for new item
|
||||
$mailcontent = "*" . $desc . "* \r\n\r\n" .
|
||||
"Bezug: " . $bezug . "\r\n" .
|
||||
"Miete: " . $mietew . "/" . $mietek . "\r\n" .
|
||||
"Größe: " . $qm . "\r\n" .
|
||||
"Zimmer: " . $zimmer . "\r\n" .
|
||||
"Ort: " . $ort . "\r\n" .
|
||||
"Link: https://www.immobilienscout24.de/expose/" . $id . "\r\n\r\n" .
|
||||
"Übersicht: https://" . $_SERVER["SERVER_NAME"] . $_SERVER["PHP_SELF"];
|
||||
mail($config['mail']['to'], "Neues Wohnungsinserat: " . $descs . "...", $mailcontent, "From: " . $config['mail']['from'] . "\r\nMIME-Version: 1.0\r\nContent-Type: text/plain; charset=UTF-8");
|
||||
|
||||
echo $id . " is new and has been downloaded.<br />\n";
|
||||
|
||||
} else { // entry is old
|
||||
echo $id . " already exists.<br />\n";
|
||||
}
|
||||
|
||||
} // END foreach &searchResult
|
||||
} // END for loop through pages
|
||||
|
||||
|
||||
echo "<p><a href='.'>Back to overview</a></p>\n";
|
||||
|
||||
write_db($db, $data);
|
||||
}
|
||||
|
||||
/////////////////
|
||||
/// do = SHOW ///
|
||||
/////////////////
|
||||
function immo_show($db) {
|
||||
global $data;
|
||||
global $queryurl;
|
||||
read_db($db);
|
||||
|
||||
$hide = isset($_GET['hide']) ? $_GET['hide'] : "yes";
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="de-DE">
|
||||
<head>
|
||||
<title>Immobilienscout Search Helper</title>
|
||||
<meta charset="UTF-8" />
|
||||
<style type="text/css">
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table, th, td {
|
||||
border: 1px solid orange;
|
||||
}
|
||||
th, td {
|
||||
padding: 3px 2px;
|
||||
}
|
||||
tr:nth-child(even) {
|
||||
background-color: #f2f2f2
|
||||
}
|
||||
tr:hover {background-color: #FFECBA}
|
||||
textarea, input, select {
|
||||
vertical-align: middle;
|
||||
height: 3em;
|
||||
}
|
||||
input[type="submit"] {
|
||||
border: 1px solid #888;
|
||||
height: 20px;
|
||||
padding: 0;
|
||||
width: 2em;
|
||||
}
|
||||
a.btn {
|
||||
background-color: #98b879;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
padding: 10px;
|
||||
text-decoration: none;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
#message {
|
||||
background-color: #FFE990;
|
||||
margin-bottom: 8px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="message">
|
||||
<?php
|
||||
session_start();
|
||||
if ( ! empty($_SESSION['message'])) {
|
||||
echo $_SESSION['message'];
|
||||
$_SESSION['message'] = NULL;
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Datum</th>
|
||||
<th>Beschreibung</th>
|
||||
<th>Status</th>
|
||||
<th>Bezug</th>
|
||||
<th>Miete warm (kalt)</th>
|
||||
<th>Zi</th>
|
||||
<th>Größe</th>
|
||||
<th>Ort</th>
|
||||
<th style="min-width:140px">Notiz</th>
|
||||
<th style="min-width:140px">Status ändern</th>
|
||||
</tr>
|
||||
<?php
|
||||
|
||||
// Sort for date descending when displaying
|
||||
foreach ($data as $key => $row) {
|
||||
$date[$key] = $row['date'];
|
||||
$id[$key] = $row['id'];
|
||||
}
|
||||
array_multisort($date, SORT_DESC, $id, SORT_DESC, $data);
|
||||
|
||||
$total = count($data);
|
||||
for ($row = 0; $row < $total; $row++) {
|
||||
$id = $data[$row]['id'];
|
||||
$date = $data[$row]['date'];
|
||||
$desc = $data[$row]['desc'];
|
||||
$descs = $data[$row]['descs'];
|
||||
$bezug = $data[$row]['bezug'];
|
||||
$mietew = $data[$row]['mietew'];
|
||||
$mietek = $data[$row]['mietek'];
|
||||
$zimmer = $data[$row]['zimmer'];
|
||||
$qm = $data[$row]['qm'];
|
||||
$ort = $data[$row]['ort'];
|
||||
$status = $data[$row]['status'];
|
||||
$note = $data[$row]['note'];
|
||||
$link = "https://www.immobilienscout24.de/expose/" . $id;
|
||||
|
||||
if ($status !== "del" || $hide === "no") {
|
||||
echo "<tr>";
|
||||
echo "<td>" . $date . "</td>";
|
||||
echo "<td><span title='" . $desc . "'><em><a target='_blank' href='" . $link . "'>" . $descs . "...</a></em></span></td>";
|
||||
echo "<td>" . $status . "</td>";
|
||||
echo "<td>" . $bezug . "</td>";
|
||||
echo "<td>" . $mietew . " (<em>" . $mietek . "</em>)" . "</td>";
|
||||
echo "<td>" . $zimmer . "</td>";
|
||||
echo "<td>" . $qm . "</td>";
|
||||
echo "<td><a target='_blank' href='https://www.openstreetmap.org/search?query=" . $ort . "'>" . $ort . "</a></td>";
|
||||
|
||||
echo "<td>";
|
||||
?>
|
||||
<form action="" method="POST">
|
||||
<input name="do" value="update" type="hidden" />
|
||||
<input name="id" value="<?php echo $id; ?>" type="hidden" />
|
||||
<textarea name="note" rows="1" cols="14"><?php echo $note; ?></textarea>
|
||||
<input type="submit" value="OK">
|
||||
</form>
|
||||
<?php
|
||||
echo "</td>";
|
||||
|
||||
echo "<td>";
|
||||
?>
|
||||
<form action="" method="POST">
|
||||
<input name="do" value="update" type="hidden" />
|
||||
<input name="id" value="<?php echo $id; ?>" type="hidden" />
|
||||
<select name="status">
|
||||
<option value="del">löschen</option>
|
||||
<option value="alt">alt</option>
|
||||
<option value="NEU">NEU</option>
|
||||
<option value="kontaktiert">kontaktiert</option>
|
||||
<option value="abgelehnt">abgelehnt</option>
|
||||
<option value="termin">termin</option>
|
||||
</select>
|
||||
<input type="submit" value="OK">
|
||||
</form>
|
||||
<?php
|
||||
echo "</td>";
|
||||
|
||||
echo "</tr>\n";
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
</table>
|
||||
|
||||
<p>Letztes Update: <?php echo date('d.m.Y H:i:s', filemtime($db)); ?></p>
|
||||
<p>Gelöschte Inserate <a href="?hide=no">einblenden</a> / <a href=".">ausblenden</a></p>
|
||||
<p>Suchlink: <a target="_blank" href="<?php echo $queryurl; ?>">Klick</a></p>
|
||||
<p><a class="btn" href="?do=download">Update</a></p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
///////////////////
|
||||
/// do = UPDATE ///
|
||||
///////////////////
|
||||
function immo_update($db) {
|
||||
global $data;
|
||||
read_db($db);
|
||||
|
||||
$id = isset($_POST['id']) ? $_POST['id'] : false;
|
||||
$status = isset($_POST['status']) ? $_POST['status'] : "NONE";
|
||||
$note = isset($_POST['note']) ? $_POST['note'] : "NONE";
|
||||
|
||||
$key = array_search($id, array_column($data, 'id'));
|
||||
|
||||
if (strlen($key) === 0) {
|
||||
echo $id . " does not exist.<br />";
|
||||
echo "<br />";
|
||||
echo "<a href='.'>Back to overview</a>\n";
|
||||
} else {
|
||||
session_start(); // start Sessions
|
||||
if ($status != "NONE") {
|
||||
$data[$key]['status'] = $status;
|
||||
save_return_page("Changed status of " . $id . " to \"". $status . "\".");
|
||||
}
|
||||
if ($note != "NONE") {
|
||||
$data[$key]['note'] = $note;
|
||||
save_return_page("Changed note of " . $id . " to \"". $note . "\".");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////
|
||||
/// HELPER FUNCTIONS ///
|
||||
////////////////////////
|
||||
// load database from file and decode it to array $data
|
||||
function read_db($db) {
|
||||
global $data; // declare $data a global variable to access it outside this function
|
||||
if (! file_exists($db)) {
|
||||
touch($db);
|
||||
}
|
||||
$file = file_get_contents($db, true);
|
||||
$data = json_decode($file, true);
|
||||
unset($file);
|
||||
}
|
||||
|
||||
// Encode $data array and write database to file
|
||||
function write_db($db, $data) {
|
||||
// encode and write file
|
||||
$encoded = json_encode($data, JSON_PRETTY_PRINT);
|
||||
file_put_contents($db, $encoded, LOCK_EX);
|
||||
}
|
||||
|
||||
// load URL and transform it to a XPath-searchable document
|
||||
function xsite($url) {
|
||||
libxml_use_internal_errors(true); // suppress errors
|
||||
$dl = file_get_contents($url);
|
||||
$site = new DOMDocument();
|
||||
$site->loadHTML($dl);
|
||||
$xpathvar = new Domxpath($site);
|
||||
return $xpathvar;
|
||||
}
|
||||
|
||||
// save database, put message to session, and load refering page which shows the message
|
||||
function save_return_page($output) {
|
||||
global $db;
|
||||
global $data;
|
||||
write_db($db, $data);
|
||||
$_SESSION['message'] = "Update: " . $output;
|
||||
header('Location: ' . $_SERVER['HTTP_REFERER']);
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
Reference in New Issue
Block a user