• Das Erstellen neuer Accounts wurde ausgesetzt. Bei berechtigtem Interesse bitte Kontaktaufnahme über die üblichen Wege. Beste Grüße der Admin

[PHP] Flugplandaten

paul schmitz

New member
Hallo,
bei meinem aktuellen Projekt mit PHP benötige ich etwas Hilfe.
Es geht darum, die aktuellen Flugplandaten von einigen Flughäfen automatisch zu extrahieren und zu speichern.
Die Daten sind:

Flugnummer: 2-3 Großbuchstaben + 2-4 Zahlen (ich habe eine Tabelle der möglichen Großbuchstabenkombinationen)
Startort: String, min. 3 Zeichen, keine Zahlen
Zielort: String, min. 3 Zeichen, keien Zahlen
Planzeit: 2 Zahlen + : + 2 Zahlen, vor "Erwartete Zeit" platziert, erste Zahl von 0-2, zweite von 0-9, nur wenn erste auf 2 ist, 0-3, dritte 0-5, vierte 0-9, aber meist 0 oder 5
Erwartete Zeit: 2 Zahlen + : + 2 Zahlen, Zahlen siehe "Planzeit"
Terminal: 1-2 Zeichen, manchmal davor ein "T" oder ein "Terminal " oder ein <span> mit "Terminal " drin
Status: "Verspätet", "Gestartet", "Aufgerufen", "Boarding", "Rollt", "Annulliert", (Hamburg: "Gestrichen"->"Annulliert"), manchmal kleingeschrieben, manchmal großgeschrieben

Im Script gibt es eine Funktion zu jedem Flughafen, benannt nach der Abkürzung für diesen, die diese Daten herausfindet. Sie teilt den Seitenquelltext nach einem eindeutigem Text vor und nach den Daten und teilt die Daten dann nocheinmal nach jedem Zellentrenner. Die Daten werden dann an die Funktion plane_log() übergeben, die diese in die csv einträgt. Die Funktion checkI() nimmt dann Doppeleinträge heraus und schreibt alle 5 Minuten eine Zwischendatei. Die Daten werden ebenfalls als JS-Array ausgegeben, um einer Live-Seite mit JS und AJAX die Daten zu bieten.

Jetzt zu meinen Fragen:
1.: Die Funktionen für die einzelnen Flughäfen müssen für jeden einzeln geschrieben werden, da alle unterschiedlich Programmiert sind. Ist es möglich, anhand der oben angegebenen Datenstrukturen für die einzelnen Felder diese aus dem gesamten Quelltext der Seite herauszufiltern (d.H. eine Funktion, der man nur die Url übergibt und die Daten zurückgibt)? Die Schwierigkeit ist außerdem, dass die richtigen Daten den richtigen Flügen zugeordnet werden und nicht vertauscht werden dürfen.

2.:Bei mehreren Flughäfen in einer Stadt schreiben die Flughäfen das unterschiedlich hin. München schreibt "FRANKFURT(FRA)", Hamburg "LONDON/HEATHROW" oder "TENERIFFA-SÜD". Ich habe eine csv-Tabelle mit allen Flughäfen (zumindest 47542), in der Name, Land, IATA-Code, Region, Webseite, ... drin stehen. Bei Name steht immer die Stadt dabei z.B. "London Heathrow Airport". Ist damit eine Umwandlung und Vereinheitlichung in der Datenspeicherung möglich?

3.: Wie speichern man diese Daten am besten, sodass später
  • ein Flugplan für einen frei wählbaren Zeitraum erstellt
  • die Dauer der einzelnen Schritte (Status) berechnet
werden kann?

4.: Gibt es noch weitere sinnvolle Codeoptimierungen?


PHP:
Fluege=[<?php
function plane_log($flugnummer,$von,$nach,$plan,$erwartet,$bereich,$status){
	$flugnummer=str_replace("\r","",$flugnummer);
	$flugnummer=str_replace("\n","",$flugnummer);
	$nach=str_replace("\r","",$nach);
	$nach=str_replace("\n","",$nach);
	$plan=str_replace("\r","",$plan);
	$plan=str_replace("\n","",$plan);
	$erwartet=str_replace("\r","",$erwartet);
	$erwartet=str_replace("\n","",$erwartet);
	$bereich=str_replace("\r","",$bereich);
	$bereich=str_replace("\n","",$bereich);
	$status=str_replace("\r","",$status);
	$status=str_replace("\n","",$status);
	$status=str_replace(" ","",$status);
	$plan=str_replace(" ","",$plan);
	$bereich=str_replace(" ","",$bereich);
	$erwartet=str_replace(" ","",$erwartet);
	$flugnummer=str_replace(" ","",$flugnummer);
	$fields=array($flugnummer,$von,$nach,$plan,$erwartet,$bereich,$status,date("ymd"));
	$fp = fopen('fluege.csv', 'a+');
	fputs($fp,"".implode(",",$fields)."\r\n");
	fclose($fp);
}
function checkI(){
	$lines = array();
	if(($handle = fopen("fluege.csv","r"))!==false){
		while(($data=fgetcsv($handle,0,","))!==false){
			$line=join(",",$data);
			if(isset($lines[$line]))continue;
			$lines[$line]=true;
		}
		fclose($handle);
	}
	$contents = '';
	foreach ($lines as $line => $bool) $contents .= $line . "\r\n";
	copy("fluege.csv","fluege2.csv");
	file_put_contents("fluege.csv", $contents);
	if(date("i")%5==0){
		file_put_contents("fluege".date("ymd h:i").".csv", $contents);
	}
}
function MUC(){
	$site=file_get_contents("http://www.munich-airport.de/de/consumer/fluginfo/dep/index.jsp");
	$site=explode('<div id="flight_info_table">',$site);
	$site=$site[1];
	$site=explode('</table>',$site);
	$site=$site[0];
	$site=explode('<td align="left">',$site);
	$jsstring="";
	for($i=0;$i<count($site)-1;){
		$flugnummer=str_replace("</td>","",$site[++$i]);
		$nach=str_replace("</td>","",$site[++$i]);
		$plan=str_replace("</td>","",$site[++$i]);
		$erwartet=str_replace("</td>","",$site[++$i]);
		$bereich=str_replace("</td>","",$site[++$i]);
		$status=str_replace("</td>","",$site[++$i]);
		$flugnummer=explode(">",$flugnummer);
		$flugnummer=str_replace("</a","",$flugnummer[1]);
		$bereich=str_replace("T","",$bereich);
		$nach=str_replace("(TXL)","-Tegel",$nach);
		$erwartet=str_replace("<strong>","",str_replace("</strong>","",$erwartet));
		$status=explode("<",$status);
		$status=$status[0];
		
		$jsstring.="['".$flugnummer."','Munchen','".$nach."','".$plan."','".$erwartet."','".$bereich."','".$status."'],";

		plane_log($flugnummer,"Munchen",$nach,$plan,$erwartet,$bereich,$status);
	}
	$jsstring=str_replace("\r","",$jsstring);
	$jsstring=str_replace("\n","",$jsstring);
	$jsstring=str_replace("\t","",$jsstring);
	$jsstring=str_replace(" ","",$jsstring);
	return $jsstring;
}
function HAM(){
	$site=file_get_contents("http://www.hamburg-airport.de/tools/flightplan/flightplan_detail.php/de/departures/today/");
	$site=str_replace('<tr class="hidden-xs">',"<tr>",$site);
	$site=str_replace('<td class="hidden-xs">',"<td>",$site);
	$site=str_replace('<span class="hidden-xs">Terminal </span>',"",$site);
	$site=str_replace("</tr>","",$site);
	$site=str_replace("<tr>","",$site);
	$site=str_replace("<td>","",$site);
	$site=str_replace('<td data-title="',"",$site);
	$site=str_replace('Flug">',"",$site);
	$site=str_replace('Erwartet">',"",$site);
	$site=str_replace('Terminal">',"",$site);
	$site=str_replace('Status">',"",$site);
	$site=explode('</td>',$site);
	$jsstring="";
	for($i=0;$i<count($site)-1;){
		$plan=$site[++$i];
		$nach=$site[++$i];
		$flugnummer=$site[++$i];
		$bereich=$site[++$i];
		$erwartet=$site[++$i];
		$status=$site[++$i];
		$nach=substr($nach,0,-5);
		$status=str_replace("gestrichen","annulliert",$status);
		
		++$i;
		++$i;
		
		$jsstring.="['".$flugnummer."','Hamburg','".$nach."','".$plan."','".$erwartet."','".$bereich."','".$status."'],";
		
		plane_log($flugnummer,"Hamburg",$nach,$plan,$erwartet,$bereich,$status);
	}
	$jsstring=str_replace("\r","",$jsstring);
	$jsstring=str_replace("\n","",$jsstring);
	$jsstring=str_replace("\t","",$jsstring);
	$jsstring=str_replace(" ","",$jsstring);
	return $jsstring;
}

echo HAM();
echo MUC();
checkI();
?>]
 
Ist es möglich, anhand der oben angegebenen Datenstrukturen für die einzelnen Felder diese aus dem gesamten Quelltext der Seite herauszufiltern
vielleicht, aber dann wird es extrem fehleranfällig.

Beachte außerdem, daß du tw. gegen die ToS (Terms of Service) verstößt (z.B. bei München sind die Flugdaten nur zum privaten Gebrauch freigegeben, was aber bei deinem Projekt nicht der Fall zu sein scheint)

Für Flugpläne gibt es APIs (ansonsten könnten Reisebüros nichts buchen), die ein einheitliches Format bieten.

2. ist ein direktes Folgeproblem von 1.

3. ich würde eine Datenbank nehmen

4.
- str_replace() kann auch arrays als input verarbeiten
- PHP hat Funktionen extra für CSV
- PHP hat Funktionen extra für JSON
 
vielleicht, aber dann wird es extrem fehleranfällig.
Beachte außerdem, daß du tw. gegen die ToS (Terms of Service) verstößt (z.B. bei München sind die Flugdaten nur zum privaten Gebrauch freigegeben, was aber bei deinem Projekt nicht der Fall zu sein scheint)
Ich glaube schon, dass es als Privat zähle, das Projekt nur innerhalb der eigenen Familie zu zeigen und die Daten keinem zu geben oder zu veröffentlichen.

Für Flugpläne gibt es APIs (ansonsten könnten Reisebüros nichts buchen), die ein einheitliches Format bieten.
Solche habe ich noch nicht gefunden, zumindest nicht kostenlos und frei testbar.

2. ist ein direktes Folgeproblem von 1.
3. ich würde eine Datenbank nehmen
4.
- str_replace() kann auch arrays als input verarbeiten
Damit meinst du also die Vereinfachung der ganzen str_replace-Zeilen z.B. in plane_log().
PHP:
$stoerzeichen=Array("\r","\n"," ");
$ersatz=Array("","","");
$bereich=str_replace($stoerzeichen,$ersatz,$bereich);
Also so?
- PHP hat Funktionen extra für CSV
Aber keine fürs herausnehmen von doppelten Zeilen.
- PHP hat Funktionen extra für JSON
Was bringt mit JSON hier, wenn ich ein JS-Array ausgeben will?
 
das hat ja auch nichts mit CSV zu tun, sondern mit der Datenvalidierung. Trotzdem sollte man CSV-Daten mit fputcsv() schreiben und nicht mit fputs().

Das hatte bei mir davor aber nicht geklappt - keine Fehlermeldung, aber kein Inhalt in der Datei. Daher diese ebenfalls funktionierende Lösung.

Ein Array in JSON ist ein JS-Array... es heißt ja auch JavaScript Object Notation.
Dann müsste das ja mit PHP so gehen, ich habe mich darüber gerade etwas schlaugelesen.
PHP:
json_encode(array($flugnummer,$start,$ziel,...));
 
Dann müsste das ja mit PHP so gehen, ich habe mich darüber gerade etwas schlaugelesen.
Ja - wobei eigentlich ein Array in JS für deine Daten nicht das beste Format ist. Ein Objekt wäre da viel besser. Also in PHP einen assoziativen Array machen und einfach diesen durch json_encode() jagen. (Wobei du hier aufpassen musst, dass deine Strings UTF-8 encoded sind. Sonst gibt json_encode() gar nichts zurück - ich frage mich, wann die da endlich einen encoding-Parameter einführen.)

CRLF ist kein gültiger CSV line delimiter in PHP, da nur ein Zeichen erlaubt wird (steht explizit so in der Doku).
Auch wenn es in der Doku steht, ist das Mist. Ich finde die CSV-Unterstütung in PHP ist so schlecht, dass man das mit gutem Gewissen selber machen (oder ein Framework verwenden) kann.
 
vielleicht, aber dann wird es extrem fehleranfällig.
3. ich würde eine Datenbank nehmen

Alles in eine Tabelle oder Id,Flugnummer,Start,Ziel,Uhr in eine und Id,Flugid,Status,verspätung,timestamp in eine zweite? Hätte den Vorteil keine Daten doppelt zu schreiben.

(Wobei du hier aufpassen musst, dass deine Strings UTF-8 encoded sind. Sonst gibt json_encode() gar nichts zurück - ich frage mich, wann die da endlich einen encoding-Parameter einführen.)
.

Das ist ja dämlich. Aber Danke für den Hinweis - da hätte ich mich garantiert Tagelang halbtotgesucht.

Auch wenn es in der Doku steht, ist das Mist. Ich finde die CSV-Unterstütung in PHP ist so schlecht, dass man das mit gutem Gewissen selber machen (oder ein Framework verwenden) kann.
Hast du da ein bevorzugtes (aus Erfahrung)? Auf die schnelle scheint mir csv.thephpleague.com recht gut zu sein.
 
Bei der Auswahl der Datenbank lohnt es sich ein Blick auf SQLite zu werfen. Hat einen minimalen Administrationsbedarf, hält alle Daten in einer einzigen Datei und die Unterstützung von Seiten PHP (PDO) ist auch gut.

Edit: Anmerkungen zur CSV-Unterstütung in PHP
Praktische Probleme wenn CRLF als Zeilentrenner benutzt wird sind mir nicht bekannt.
Gute Erfahrungen hab ich mit dem SplFileObject gemacht.
 
Zuletzt bearbeitet:
Bei der Auswahl der Datenbank lohnt es sich ein Blick auf SQLite zu werfen. Hat einen minimalen Administrationsbedarf, hält alle Daten in einer einzigen Datei und die Unterstützung von Seiten PHP (PDO) ist auch gut.
Nunja, Mysql ist schon installiert (und funktioniert). Alle Daten in einer Datei brauche ich nicht und verzögert das bei mir nur (Versuch mal unter Windows ne 100MB-Datei "nur" in notepad zu lesen (aber vorher alle Daten sichern, Absturzgefahr)). Schreiben ist noch langsamer, und Linux wird da nicht 500% schneller sein.

Edit: Anmerkungen zur CSV-Unterstütung in PHP
Praktische Probleme wenn CRLF als Zeilentrenner benutzt wird sind mir nicht bekannt.
Gute Erfahrungen hab ich mit dem SplFileObject gemacht.

Wurde nicht (also doch) von kkapsner geschrieben, dass PHP eine schlechte CSV-Unterstützung hat? Gilt dieses Objekt mit zu dieser Aussage oder nicht?

Aber eher zwei Tabellen als eine.

So werde ich es machen, laut den Regeln ist es besser so.
 
Um ehrlich zu sein, weiß ich nicht mehr genau, was exakt mein Problem mit den nativen CSV-Funktionen war. Es hat mich aber so weit gebracht, dass im mir meine eigenen Funktionen geschrieben hab'.

- - - Aktualisiert - - -

Es kann sein, dass das noch in einer älteren PHP Version war und die Probleme jetzt gelöst sind.
 
SplFileObject löst auch nicht alle Probleme. Unabhängig vom speziellen Fall der Zeilentrenner bleibt der Kern deiner Aussage richtig, die CSV-Unterstüzung der Funktionen und Klassen des PHP Kernes könnte besser sein.
Um nur einige Punkte zu nennen:
- Funktion str_getcsv() existiert, ein str_putcsv() jedoch nicht
- Umlautproblematik bei Fremcodeimport (Verarbeitung von Window-CSV-Dateien unter UTF8, hier wird es dann spannend)
- Behandlung unterschiedlicher Dezimaltrennzeichen bei numerischen Werten
 
Zurück
Oben