Mittwoch, 20. März 2013

Mit scp eine Datei mit Leerschlägen im Dateinamen kopieren

Das geht ganz einfach — sobald man herausgefunden hat, wie man den Pfad aufschreiben muss:

scp user@server:"'/Movies/Documentaries/The Power Of Nightmares/Documentary.The.Power.Of.Nightmares.01.Baby.Its.Cold.Outside.avi'" ~/Desktop

Konkret: Man schliesst den Pfad in Anführungszeichen und Apostrophs ein: „‚[pfad]'“ (Achtung: WordPress wandelt die Anführungszeichen in diesem Paragraphen in typographische ansprechende Zeichen um — was aber leider zur Folge hat, dass Copy & Paste der Zeichen in ein Shell-Fenster nicht mehr funktioniert).

Via Spaces and SCP

Tags: , , ,
Labels: Linux

Keine Kommentare | neuen Kommentar verfassen

Freitag, 15. März 2013

Photos eines Instagram-Benutzers als RSS-Feed abonnieren

Obwohl Google seinen (RSS-)Reader bald dicht macht, heisst das noch lange nicht, dass RSS-Feeds eine überholte Technologie sind.

Wer die Photos eines Instagram-Benutzers abonnieren möchte, abonniert folgende URL:

http://ink361.com/feed/user/[instagramusername]

Tags: , , , ,
Labels: Web

1 Kommentar | neuen Kommentar verfassen

Sonntag, 10. März 2013

Thunderbird effizient mit einem IMAP-Server und zehntausenden E-Mails verwenden

Neben der lokalen Ablage der Logs meines Linux-Servers unter /var/log leite ich Resultate von Cron-Läufen auch an eine E-Mail-Adresse auf einen in einem Rechenzentrum stehenden IMAP-Server weiter. Einerseits stelle ich so sicher, dass ich im Falle eines verheerenden Hacks auf dem IMAP-Server möglicherweise Informationen extrahieren kann, welche mir bei der Forensik helfen. Ob diese Lösung wirklich das Gelbe des Eis ist sei hier dahingestellt (heute würde ich mich wohl für eine Lösung wie Splunk entscheiden).

Nach langer Zeit habe ich heute wieder einmal per Web-Mail in diesen Account hineingeschaut und musst feststellen, dass sich in zwei IMAP-Ordnern je über 50’000 Mails angesammelt haben. Ich entschied mich, diese Ordner zu säubern. Hierzu verwendete ich aber nicht die vom Hoster angebotene Web-Mail-Lösung Roundcube, sondern Mozilla Thunderbird.

Nachfolgend einige Tipps, wie man mit der riesigen Flut an Mails umgeht:

Alle IMAP-Nachrichte herunterladen

Einerseits habe ich unter

  1. Thunderbird
  2. Preferences
  3. Advanced
  4. General
  5. Config Editor

den Wert von mail.server.default.check_all_folders_for_new auf true gesetzt. Ob dies etwas bewirkt, kann ich leider nicht sagen.

Andererseits habe ich auch bemerkt, dass man alle E-Mails in einem Ordner mittels Command-A auswählt und dann mittels Rechtsklick über die Option „Get Selected Messages“ herunterladen kann.

IMAP-Nachrichten auf dem Server durchsuchen

Unter

  1. Edit
  2. Find
  3. Search Messages…
  4. [x] Run search on server

In meinem Fall funktionierte diese Methode aber nicht; es wurden nur die Bodies derjenigen Mails durchsucht, welche sich bereits lokal auf meiner Festplatte befanden.

Zehntausende E-Mails rasch löschen

Nachfolgende Option verhindert, dass Thunderbird während Minuten nicht mehr reagiert, einen Spinning Beachball anzeigt und in Mac OS X‘ Activity Monitor rot als „not responding“ markiert wird:

  1. Tools
  2. Account Settings…
  3. Server Settings
  4. When I delete a message: (x) Remove it immediately

Dies verhindert, dass zu löschende Nachrichten von Thunderbird im Hintergrund in den Ordner „Trash“ verschoben werden, aus welchem man diese danach noch mittels „Empty Trash“ löschen muss.

Tags: , , , , ,
Labels: IT

Keine Kommentare | neuen Kommentar verfassen

Sonntag, 10. März 2013

Web-Sites unter Mac OS X mit Internet Explorer testen

Obwohl ich kaum noch Web-Sites entwickle und seit ein, zwei Jahren auch nie mehr Web-Sites mit HTML und CSS gelayoutet habe, finde ich folgende Anleitung äusserst nützlich:

Internet Explorer for Mac the Easy Way: Run IE 7, IE8, & IE9 Free in a Virtual Machine

Zusammen mit der quelloffenen VirtualBox, welche mittlerweile Oracle gehört, und dem Shell-Script des Github-Benutzers xdissent richtet man sich unter Mac OS X oder Linux insgesamt vier Virtual Machines ein, welche Microsoft Internet Explorer 6 bis 9 bereitstellen.

Tipp: Das Passwort für die Konten Admin und Administrator lautet … Password1.

Tags: , , , ,
Labels: Web

Keine Kommentare | neuen Kommentar verfassen

Mittwoch, 6. März 2013

Wenn Subversion zwar updaten, aber nicht committen kann

Heute habe ich eines meiner Web-Projekte aktualisiert. Ich arbeitete dabei direkt auf dem produktiven Web-Server. Ein svn up klappte problemlos. Doch als ich die direkt auf dem Server neu erstellte robots.txt in das Repository einchecken wolle, kam es zu folgender Fehlermeldung:

$ svn ci -m "robots.txt fehlte"
svn: Commit failed (details follow):
svn: At least one property change failed; repository is unchanged
svn: Server sent unexpected return value (400 Bad Request) in response to PROPPATCH request for '//!svn/wbl/56ef405d-3812-4a6b-a62d-edae457cb0b0/1057'

Die Log-Daten meines Apache-Servers, welcher mittels WebDAV auch den Zugang zum SVN-Repository bereitstellt, vermeldeten folgendes:

access.log:

...
[SOURCE IP] - - [06/Mar/2013:20:08:56 +0100] "OPTIONS /repository HTTP/1.1" 401 772 "-" "SVN/1.6.17 (r1128011) neon/0.28.2"
[SOURCE IP] - [USER] [06/Mar/2013:20:08:56 +0100] "OPTIONS /repository HTTP/1.1" 200 1039 "-" "SVN/1.6.17 (r1128011) neon/0.28.2"
[SOURCE IP] - [USER] [06/Mar/2013:20:08:56 +0100] "PROPFIND /repository HTTP/1.1" 207 741 "-" "SVN/1.6.17 (r1128011) neon/0.28.2"
[SOURCE IP] - [USER] [06/Mar/2013:20:08:56 +0100] "MKACTIVITY /!svn/act/74bcecab-e942-418b-84e4-822ad8581d59 HTTP/1.1" 201 758 "-" "SVN/1.6.17 (r1128011) neon/0.28.2"
[SOURCE IP] - [USER] [06/Mar/2013:20:08:57 +0100] "CHECKOUT /!svn/vcc/default HTTP/1.1" 201 777 "-" "SVN/1.6.17 (r1128011) neon/0.28.2"
[SOURCE IP] - [USER] [06/Mar/2013:20:08:57 +0100] "PROPPATCH //!svn/wbl/74bcecab-e942-418b-84e4-822ad8581d59/1058 HTTP/1.1" 400 478 "-" "SVN/1.6.17 (r1128011) neon/0.28.2"
[SOURCE IP] - [USER] [06/Mar/2013:20:08:57 +0100] "DELETE /!svn/act/74bcecab-e942-418b-84e4-822ad8581d59 HTTP/1.1" 204 302 "-" "SVN/1.6.17 (r1128011) neon/0.28.2"
...

error.log:

[Wed Mar 06 19:48:10 2013] [error] [client [SOURCE IP]] Digest: uri mismatch -  does not match request-uri 

Unterwegs vom Produktionssystem zum Repository-Server kam es zu einem Problem, in welchem die Domain meines Servers aus der Request-URI entfernt wurde. Ich muss hierbei noch erwähnen, dass zwischen den beiden Servern ein (nicht transparenter) Proxy-Server mit Squid werkelt. Inwiefern dieser Server in den Vorgang gepfuscht hat, kann ich nicht sagen.

Nach langem Pröbeln habe ich es irgendwie hingekriegt. Die Lösung des Problems erscheint mir immer noch mysteriös — aber so sei es wohl manchmal halt: Nachdem ich einerseits folgende zwei Konfigurationsdateien umbenannt …

  • /etc/subversion/servers
  • ~/.subversion/servers

… und andererseits den Inhalt des Verzeichnisses

~/.subversion/auth/

komplett gelöscht hatte, klappte es mit dem Commit plötzlich problemlos.

Eine Vermutung habe ich: Da ich mich seit mehr als einem Jahr nicht mehr auf dem Server eingeloggt hatte, könnten unter auth noch Überreste der damals verwendeten HTTP Basic-Authentifizierung gespeichert gewesen sein. Mittlerweile bin ich auf HTTP Digest-Authentifizierung umgestiegen, um Passwörter im schlimmsten Fall nicht mehr im Klartext über ein öffentliches WiFi zu jagen. Indiz dafür ist, dass ich heute nach der Löschaktion gefragt wurde, ob ich das Passwort im Klartext auf dem Server speichern möchte (was ich abgelehnt habe).

Oder aber eine oder beide Konfigurationsdateien enthielten eine Option, welche die Übertragung der Informationen störte — bspw. der hardkodierte Proxy. Wieso die HTTP-Verbindung zum Repository-Server auch nach der Umbenennung der zwei Konfigurationsdateien weiter funktionierte, ist und bleibt schleierhaft. Wobei, jetzt kommt mir in den Sinn, dass die Admins dieses Netzwerks kurz vor meinem Weggang planten, einen transparenten Proxy einzurichten … Das könnte die Lösung sein.

Tags: , , , , , , ,
Labels: IT

Keine Kommentare | neuen Kommentar verfassen

Sonntag, 3. März 2013

Zeitlos aktuell: Boiler Room (2000)

Gestern habe ich Zeit gefunden, den Film Boiler Room aus dem Jahr 2000 anzuschauen. Ich bin über den Artikel What it’s like to uncover a $1 billion fraud auf den Film aufmerksam geworden, welcher wie der Film das berühmt-berüchtigte Pump and Dump-Schema mit Penny Stocks porträtiert.

Meine Überlegungen in Form von Stichworten:

  • Der Rote Faden reicht von diesem Film direkt bis zur Finanzkrise, in der wir immer noch stecken
  • Wer in einem amerikanischen oder amerikanisch geprägten Unternehmen arbeitet wird die alles umfassende „Sell, Sell, Sell“-Mentalität wiedererkennen
  • Lasse dir niemals Investitionen im fünf- oder sechsstelligen Bereich von jemandem andrehen, der dich nur per Telefon kontaktiert, dessen Gesicht du nie gesehen hast und dessen Büro nicht in deiner Umgebung angesiedelt ist
  • Im Film tauchen verschiedene Schauspieler auf, die es in den seither vergangenen 13 Jahren zu Prominenz gebracht haben (Giovanni Ribisi, Vin Diesel, Ben Affleck). Mir war es auch, als hätte ich zu Beginn des Films bei der Information der potentiellen Broker-Kandidaten durch Ben Affleck auch Jonah Hill gesehen (der leicht dickliche Typ mit Rucksack).
  • Jetzt weiss ich, was sich ein ehemaliger Arbeitskollege zu Schulden kommen liess

Tags: , , ,
Labels: Medien

Keine Kommentare | neuen Kommentar verfassen

Sonntag, 3. März 2013

Arte-Videos herunterladen

Vor ein paar Tagen hat arte die Dokumentation Staatsgeheimnis Bankenrettung veröffentlicht. Man kann die Reportage unter dem genannten Link live anschauen.

Wer sich die Dokumentation aber auf die lokale Festplatte laden möchte, um sie danach beispielsweise mit Air Video Server und Air Video.app unter iOS auf den Apple TV zu streamen, muss auf der Kommandozeile aktiv werden:

  1. # port install rtmpdump
  2. Download von Florian Crouzats Shell-Script mittels
    $ wget http://files.floriancrouzat.net/ripArte-v0.9.sh
  3. $ chmod 755 ripArte-v0.9.sh
  4. $ ripArte-v0.9.sh "http://videos.arte.tv/de/videos/staatsgeheimnis-bankenrettung--7340782.html"
  5. Auswahl des vierten RTMP Streams (German High-definition RTMP, rtmp://artestras.fcod.llnwd.net/a3903/o35/mp4:geo/videothek/ALL/arteprod/A7_SGT_ENC_04_048116-000-A_PG_HQ_DE?h=24fceb3242c5803955ac67b8b44ce499)

Und das Resultat sieht folgendermassen aus:

...
340386.493 kB / 3129.09 sec (99.9%)
Download complete

Tags: , , , , ,
Labels: IT, Medien

3 Kommentare | neuen Kommentar verfassen

Dienstag, 19. Februar 2013

cld zwecks Spracherkennung in Python-Scripts integrieren

CLD ist der Compact Language Detector welcher in Google-Projekten zum Einsatz kommt — primär einmal in Googles Web-Browser Google Chrome. Mit diesem Detektor ist es möglich, die Sprache eines Textes zu erkennen.

Der Code des Detektors ist quelloffen, weshalb findige Entwickler den Code aus dem Google-Projekt herausgelöst haben und als eigenständige Linux-Library bereitstellen:

CLD – Compact Language Detector

Ich habe diesen Detektor in ein Projekt integriert und dabei die PHP-basierte Lösung Package Information: Text_LanguageDetect abgelöst, weil ich dessen Erkennungsgenauigkeit nicht als herausragend empfunden habe.

Download und Kompilation der Library

Zuerst klonte ich den Quellcode auf das Entwicklungssystem:

$ cd /tmp
$ git clone https://github.com/mzsanford/cld.git

Anschliessend versuchte ich die Kompilierung und Installation, doch dies schlug fehl:

$ cd /tmp/cld
$ ./configure
configure: error: cannot find install-sh or install.sh in "." "./.." "./../.."

Zuerst musste ich mir folgende Ubuntu/Debian-Pakete herunterladen:

# apt-get install build-essential libtool autoconf automake pkg-config

Via: run autoreconf -f -i -Wall,no-obsolete to fix install-sh problem

Anschliessend musste ich die Kompilierungsinformationen des Projektes neu generieren:

$ cd /tmp/cld
$ autoreconf -f -i -Wall,no-obsolete

Nach diesem Schritt klappte es auch mit der Kompiliererei:

$ cd /tmp/cld
$ ./configure

Installation der Library

Nun musste die Library noch installiert werden, was mit folgendem Befehl erfolgte:

# cd /tmp/cld
# make install

Installation der Python-Bindings

Noch war ich nicht am Ziel. Nachdem die Shared Library installiert war, fehlten noch die Python-Bindings, mit welchen die Library mittels eines simplen import cld in ein Python-Script importiert werden kann:

# cd /tmp/cld/ports/python
# make install
...
pycldmodule.cc:5:20: Schwerwiegender Fehler: Python.h: Datei oder Verzeichnis nicht gefunden
Kompilierung beendet.

Sorry, aber den Laptop habe nicht ich aufgesetzt — Linux läuft bei mir immer unter der englischen Sprache …

Es fehlte der Python-Quellcode, welchen ich folgendermassen nachrüstete:

# apt-get install python-dev

Anschliessend funktionierte auch der folgende Befehl sauber:

# cd /tmp/cld/ports/python
# make install

Die Originalversion dieses Artikels liess das Paket pkg-config nicht installieren. Ist dieses Paket nicht vorhanden, erscheint bei der Kompilierung folgende Fehlermeldung:

/tmp/cld/ports/python# make
python -u setup.py build
Traceback (most recent call last):
  File "setup.py", line 12, in 
    **pkgconfig('cld'))
TypeError: __init__() keywords must be strings
make: *** [build] Error 1

Nachdem pkg-config nachinstalliert wird, klappt es mit der Installation der Python-Bindings problemlos.

Via: Error when installing python bindings

Integration in Python-Scripts

Leider funktionierte die Integration der Library in ein Python-Script mittels import cld nicht:

Traceback (most recent call last):
File "", line 1, in 
ImportError: libcld.so.0: cannot open shared object file: No such file or directory

Zuerst musste noch die Shell-Variable LD_LIBRARY_PATH mit dem Pfad auf cld angepasst werden (sowohl in .bashrc als auch in .profile, weil ich immer noch nicht begriffen habe, welche Datei bei einem Shell-Login geladen wird — ich sollte wohl mal Shell startup scripts lesen, doch diese Grafik sagt wohl schon alles zum Thema aus):

...
export LD_LIBRARY_PATH="/usr/local/lib:/usr/local/lib/cld"

Via: Language detection with Google’s Compact Language Detector

Alternativ kann das Verzeichnis über die Verzeichnisstruktur /etc/ld.so.conf.d auch systemweit gesetzt werden (Via: How to define LD_LIBRARY_PATH for all applications).

Anschliessend konnte ich mit wenigen Zeilen Python-Code die Spracherkennung für Texte aktivieren:

import cld
...
detectedLangName, detectedLangCode, isReliable, textBytesFound, details = cld.detect(plaintext, pickSummaryLanguage=False, removeWeakMatches=False)

Tags: , , , , , ,
Labels: Programmierung

Keine Kommentare | neuen Kommentar verfassen

Donnerstag, 14. Februar 2013

Data Mining mit Ubuntu, MySQL, PHP und Python

Beruflich setze ich mich derzeit mit der Analyse von Inhalten von Web-Sites auseinander. Nachfolgend habe ich einige Erfahrungen aufgelistet, welche ich dabei gemacht habe.

Wir verarbeiten in diesem Projekt Web-Sites, welche wir mit entsprechenden Tools aus dem Web auf den lokalen Rechner gespiegelt haben. Die Web-Site Assets liegen im Dateisystem. Zur Weiterverarbeitung der Daten wurden die HTML-, JS-, CSS- und XML-Rohdaten in eine MySQL-Tabelle gespitzt (12GB) und anschliessend mit Meta-Daten ergänzt.

Ich persönlich bin nicht sicher, ob die Ablage von HTML-Code in der Datenbank die sinnvollste und performanteste Lösung ist, aber dies war nunmal der Stand des Projektes als ich dazu gestossen bin — und daran liess sich nichts mehr rütteln.

Da wir unter anderem auch Volltextsuchen auf die Grunddaten anwenden, hätte ich mir Apache Solr genauer angeschaut und darauf mittels PHP und JSON zugegriffen.

Kommandozeile

Da der Ubuntu-Server aus mir unerfindlichen Gründen mit LAMPP aufgesetzt wurde, befinden sich die Binaries wie php nicht in den Standardpfaden und werden von bash ohne absolute Pfadangabe nicht gefunden. Da man den Interpreter täglich dutzende, wenn nicht gar hunderte Male aufruft, ist es ratsam, das Verzeichnis sofort in die $PATH-Variable des Shells aufzunehmen.

Hier findet man sich in der Shell Startup File Hell wieder. Je nachdem, ob man lokal arbeitet oder sich per SSH einloggt muss der Befehl an einem anderen Ort stehen. Schlussendlich habe ich die nachfolgende Zeile …

...
PATH="$PATH:/opt/lampp/bin"

… ans Ende folgender zwei Dateien angefügt:

  • ~/.profile
  • ~/.bashrc

Datenbank (MySQL)

Cache aktivieren

Wir führen PHP-Scripts über die Linux-Shell aus. Gerade bei der Entwicklung neuer Scripts sind verschiedene Anläufe nötig, bis alle Bugs und nicht beabsichtigten Funktionen ausgemerzt sind. Da wir oftmals an einem Datenset von 60’000+ Seiten arbeiten, ist es unabdingbar, dass wir eine Cacheing-Lösung anwenden, um Datenbankabfragen im Kurzspeicher zwischenzulagern.

Folgende Parameter in my.cnf aktivieren den in MySQL vorhandenen Cache:

...
[mysqld]
...
query_cache_type = 1
query_cache_size = 512M
query_cache_limit = 32M

Der Geschwindigkeitsgewinn ist immens — nachdem ein Script mit SELECT-Statements zum ersten Mal ausgeführt wurde und dafür mehrere Minuten benötigte, rauscht es in den folgenden Malen innert Sekunden durch.

Programmierung (PHP und Python)

Helper-Funktionen und -Klassen

Wie aus der Web-Entwicklung gewohnt sollte man bei jedem der mit der Zeit entstehenden Scripts zu Beginn Klassen, allgemeine Einstellungen und Funktionen einbinden.

In der Funktionsbibliothek setze ich beispielsweise folgende wichtigen Parameter in globaler Form:

...
error_reporting(E_ALL);
date_default_timezone_set('Europe/Zurich');

Auch die Datenbankverbindung wird mittels meiner MySQL-Klasse hier erstellt und an alle Scripts weitergegeben, welche die Library einbinden.

Aufbau der Scripts

Als ich zum Projekt stiess herrschten Spaghetti-Code in teils monolithischen Scripts vor. Ich habe meine eigenen Scripts dann aber so entwickelt, dass sie dem Unix-Gedanken folgend normalerweise nur eine bestimmte Funktion ausführen, diese dafür aber ausgezeichnet und in sich abgeschlossen. So steht im Normalfall in jedem Script, welches Daten manipuliert, zuoberst ein SQL-Query, welches die zu verändernden Datenbankdaten auswählt.

Bei der Entwicklung wählt man hierbei ein Query, das einen oder nur wenige Werte aus der Datenbank ausliest und verarbeitet — in dieser Phase hat man keine Zeit, möglicherweise fehlerhafte Manipulationen an 60’000+ Seiten durchzuführen.

HTML mit regulären Ausdrücken parsen?

Nein, besonders nicht dann, wenn man konkret an Eigenschaften des SGML-Markup interessiert ist (bspw. Wohin zeigen Links?). Hierzu verwendet man die in PHP standardmässig enthaltene DOMDocument-Bibliothek.

Wichtig ist, dass man bei der Verwendung von E_ALL die loadHTML()-Funktion mittels @ stummschaltet, weil sonst das Terminal mit Warnungen über fehlerhaften HTML-Code (leider an der Tagesordnung) vollgespamt wird:

...
$dom = new DOMDocument();
@$dom->loadHTML($html);
$elements = $dom->getElementsByTagName('a');

HTML manipulieren — und Fallstricke

Für jede HTML-Seite erstellen wir eine Nur-Text-Version. Hierzu verwenden wir html2text.py von Aaron Swartz selig. Bevor das HTML aber umgewandelt wird, säubern wir die HTML-Datei auf eigene Faust. Auch hier kommt DOMDocument zum Zug.

Wir suchen dabei zuerst einmal Elemente jeglicher Art, deren ID oder Klasse den String nav, menu und breadcrumb enthält. Die Navigation interessiert uns nämlich nicht, und noch schlimmer: Sie verfälscht teilweise die Resultate, weil in der Navigation gesuchte Begriffe vorkommen.

Hierzu lade ich den HTML-Code wieder in eine DOMDocument und iteriere danach über alle Elemente auf der Suche nach den besagten IDs und Klassennamen:

function cleanNavMenuElements($html = null) {
		$dom = new DOMDocument();
		@$dom->loadHTML($html);
		
		$changesMade = false;
		
		$elements = $dom->getElementsByTagName('*');
		foreach($elements as $element) {
			if(preg_match('/^(html|body)$/',$element->nodeName)) {
				// Otherwise we might delete the whole DOM!
				continue;
			}
			
	        if($element->hasAttribute('class') && preg_match('/(nav|menu|breadcrumb)/i',$element->getAttribute('class')) > 0) {
				status('Found element with class ' . $element->getAttribute('class'));
				$element->parentNode->removeChild($element);
				
				$changesMade = true;
				
				// Don't go further if we removed this node already
				continue;
			}
			
			if($element->hasAttribute('id') && preg_match('/(nav|menu|breadcrumb)/i',$element->getAttribute('id')) > 0) {
				status('Found element with id ' . $element->getAttribute('id'));
				$element->parentNode->removeChild($element);
				
				$changesMade = true;
			}
	    }
		
		if(!$changesMade) {
			return $html;
		}
		
		return $dom->saveHTML();
	}

Ein Problem manifestiert sich bei der Manipulation aber: Jegliche Anpassungen erfolgen live, was verwirrende Folgen für Schleifen haben kann.

Da die Suche nach obigen ID- und Klassennamen nicht alle Navigationselemente eliminiert, suche ich in einem zweiten Anlauf Tabellen und Listen, deren Elemente ausschliesslich Links enthalten. Dies ist ein guter Indikator, ein Navigationsblock gefunden zu haben.

Hier ist das Problem des sich bei jeder Iteration veränderndem DOM aber sehr ausgeprägt. Wenn ich deshalb durch td-Elemente und li-Elemente iteriere, verwende ich nicht foreach() sondern eine for()-Schleife, deren Counter $i ich immer dann zurücksetze, wenn ich ein Element entferne. Ansonsten wird aus Erfahrung in der Folge eines (oder mehrere Elemente) übersprungen. Damit dies klappt, arbeite ich den DOM Tabellen- respektive Listenweise ab:

$containers = $dom->getElementsByTagName($containerTag);
	
foreach($containers as $container) {
	$items = $container->getElementsByTagName($itemTag);
	
	for($i = 0; $i < $items->length; $i++) {
		$item = $items->item($i);
		
		if($item === null) {
			continue;
		}
		
		$otherTagsPresent = false;
		foreach($item->childNodes as $child) {
			$tag = $child->nodeName;

			if($tag == '#text') {
				$text = trim($child->nodeValue);
				$len = strlen($text);
				if($len < 1) {
					//#text is empty, thus not relevant
					continue;
				}
			}

			if($tag != 'a') {
				$otherTagsPresent = true;
				continue;
			}
		}

		if(!$otherTagsPresent) {
			$item->parentNode->removeChild($item);
			$i = $i-1;
		}
	}

Weiterverarbeitung der Daten durch Nicht-IT-Profis

Zur Weiterverarbeitung der Auswertungen durch andere, in IT nicht versierte Mitarbeiter habe ich eine Funktion geschrieben, welche eine Pfadangabe sowie CSV-Daten als Argumente übertragen erhält. Die Datei wird geschrieben und gleich anschliessend mittels eines kleinen Python-Scripts (Stichwort: openpyxl) in das bei uns hauptsächlich verwendete XLSX-Format konvertiert.

Tags: , , , , , , , , ,
Labels: Programmierung

Keine Kommentare | neuen Kommentar verfassen

Mittwoch, 13. Februar 2013

Mit PHPExcel eine xlsx-Datei parsen und in MySQL importieren

Die einfachste Methode, die sich nach dem Studium der Dokumentation und Stackoverflow-Posts zum Thema PHPExcel ergeben hat, greift auf die PHPExcel_Worksheet::toArray()-Funktion zurück:

$objReader = PHPExcel_IOFactory::createReader(PHPExcel_IOFactory::identify($path));
$objReader->setReadDataOnly(true);

$objPHPExcel = $objReader->load($path);

$sheets = $objPHPExcel->getSheetNames();	
$sheetsRev = array_flip($sheets);

if(!isset($sheetsRev['Queries'])) {
	die('$sheetsRev[Queries] not set');
}

$objSheet = $objPHPExcel->setActiveSheetIndex($sheetsRev['Queries']);

$data = array();
foreach($objSheet->toArray() as $rowNum=>$row) {
	foreach($row as $cellNum=>$value) {
	...
	}
}

Es gibt eine andere Lösung, welche aber erforderlich macht, dass man die Dimension der einzulesenden Daten kennt; sprich die Anzahl der Spalten und Zeilen müssen vor der Iteration ausfindig gemacht werden. Mit toArray() überspringt man diesen unnötigen Schritt.

Tags: , , , ,
Labels: Programmierung

1 Kommentar | neuen Kommentar verfassen