Posts Tagged ‘Python’

Freitag, 23. März 2018

macOS Python kennt das Modul yaml nicht

Wird man mit folgender Fehlermeldung konfrontiert …

Traceback (most recent call last):
  File "/Users/mario/test.py", line 6, in 
    import yaml
ImportError: No module named yaml

… kann man entweder versuchen, das yaml-Modul unter der macOS Python-Installation nachzuinstallieren, oder man wechselt auf das (bei mir bereist installierte) MacPorts und verbastelt damit nicht macOS:

$ sudo port install libyaml
$ sudo port select --set python python27

Weil ich letzteren Befehl nicht auf Anhieb ausgeführt hatte, biss ich mir noch einige Minuten die Zähne aus, da das Terminal (bash) weiterhin das macOS python unter /usr/bin/python mit Version 2.7.10 ausführte und nicht dasjenige unter /opt/local/bin/python mit Version 2.7.14. Nachdem ich den port select-Befehl ausgeführt hatte, lag unter /opt/local/bin dann das Executable python (respektive der Symlink darauf) (vorher gab es dort nur python2.6, python2.7 und python3.4)

Tags: , , , ,
Labels: Apple

Keine Kommentare | neuen Kommentar verfassen

Dienstag, 13. Dezember 2016

mitmproxy: Server-Antworten umschreiben (oder: M6 Geoblocker umgehen)

Heute durfte ich für einen Kollegen in den USA eine im Internet als Stream verfügbare M6 News-Sendung rippen.

Geoblocker

Damit ich an die m3u8-Datei des Streams herankam, musste ich aber zuerst die Geo-Sperre umgehen. Die Web-Site frägt nämlich beim Aufruf (gleich zweimal) ab, in welchem Land sich der Surfer befindet. Hierzu ruft der JavaScript-Code der Web-Site folgende zwei URLs auf:

Als Antwort erhält der Browser folgenden JSON-Code zurück (IP anonymisiert):

{"offset":"2","isp":"Andreas Fink","areas":[10],"country_code":"CH","asn":"AS6775","ip":"85.195.0.0"}

Diese Antwort bewegt den Player dazu, folgende Fehlermeldung anzuzeigen:

M6 6play Geoblock
image-7099

mitmproxy installieren und konfigurieren

Der erste Task war somit klar: Ich musste mit dem quelloffenen Tool mitmproxy alle HTTP(S)-Requests meines iPads proxyifizieren und dann obige JSON-Antwort mit einer validen Antwort ersetzen, so wie sie über einen französischen ISP erfolgen würde.

mitmproxy hatte ich bereits auf meinem lokalen Linux-Server konfiguriert, weshalb ich auf dem iPad in den Netzwerkeinstellungen den Proxy nur noch erfassen musste: 10.0.X.Y:8888.

Auf dem Linux-Server startete ich mitmproxy folgendermassen:

# mitmproxy -p 8888

Als nächstes musste ich noch das mitmproxy-Zertifikat auf dem iPad installieren, damit HTTPS-Verbindungen entschlüsselt und umgeschrieben werden können. Hierzu reicht es, auf dem iPad die folgende URL aufzurufen …

mitm.it

… und den Anweisungen zu folgen (ACHTUNG: Sicherheitssensitive Personen löschen das Zertifikat nach der Verwendung wieder vom iPad. Es findet sich unter Settings > General > Profiles).

Inline-Script zum Umschreiben von Antworten

Damit ich vom Proxy-Server empfangene Antworten der M6-Server umschreiben konnte, musste ich nun noch ein mitmproxy Inline-Script einbauen, welches Aufrufe auf die oben genannten URLs abfängt und nach meinem Gusto umschreibt.

Hierzu legte ich unter /tmp/m6-geoip.py folgendes Python-Script ab:

import json

def response(context, flow):
	f = open('/tmp/mitm.log','a+')
	url = flow.request.get_url()
	f.write('URL "' + url  + '"' + "\n")

	if url.endswith('geoInfo') or url.endswith('geoInfos'):
		f.write('    matched' + "\n")
		data = json.loads(flow.response.content)
		# {"offset":"2","isp":"Andreas Fink","areas":[10],"country_code":"CH","asn":"AS6775","ip":"85.195.0.0"}
		# {"offset":"2","isp":"QSC AG","areas":[1,2,3,10,11],"country_code":"FR","asn":"AS20676","ip":"92.222.83.58"} via http://www.franceproxy.net
		#data["country_code"] = "FR"
		#jsonOut = json.dumps(data)
		jsonOut = '{"offset":"2","isp":"QSC AG","areas":[1,2,3,10,11],"country_code":"FR","asn":"AS20676","ip":"92.222.83.58"}'
		flow.response.content = jsonOut
		f.write("    " + jsonOut + "\n")
	f.close()

Tipp 1: Da selten Leute vom Himmel gefallen sind, welche ein Script auf Anhieb richtig hingekriegt haben, hilft es zum Debugging, statt mitmproxy mitmdump zu verwenden:

# mitmdump -e -p 8888 -s "/tmp/m6-geoip.py"

Bei Python-Problemen mit dem Inline-Script wird der Stack Trace und die Fehlermeldung direkt auf der Kommandozeile ausgegeben.

Tipp 2: Die Ausgabe aller URLs in eine Log-Datei resultiert in Performance-Einbussen, kann für das Debugging aber sehr nützlich sein.

Anschliessend startete ich den mitmproxy neu:

# mitmproxy -p 8888 -s "/tmp/m6-geoip.py"

JSON-Quelle

Doch von wo hatte ich den JSON-String?

Nach etwas Googeln fand ich folgenden Proxy-Service:

www.franceproxy.net

Gibt man dort eine der obigen URLs ein, erhält man als Antwort den JSON-Code, welchen ich dann auch für die Umgehung des Geoblockers verwendete.

Kostenloser Tipp an die Web-Entwickler von M6: Die Geo-Location eines Aufrufs prüft man nicht im JavaScript auf dem Client, sondern auf dem Server.

Der erneute Versuch im Web-Browser

Ich lud die Web-Seite auf dem iPad erneut — erhielt zwar eine Fehlermeldung, dass trotzdem irgendetwas schief gelaufen war, doch im mitmproxy-Log fand sich die sehnlichst gesuchte URL zur m3u8-Datei:

lb.cdn.m6web.fr

Daraus — und mit Informationen der aktuellsten Sendung, welche sich auch für Schweizer streamen lässt — leitete ich die tatsächliche URL zur Master-Playlist ab:

e112.cdn.m6web.fr

Von dieser Datei war es dann nur noch ein Katzensprung zur m3u8-Datei mit dem Stream mit der höchsten Bit-Rate:

e112.cdn.m6web.fr

Und so lud ich den Stream dann mit ffmpeg herunter:

$ ffmpeg -protocol_whitelist file,http,https,tcp,tls -i "http://e112.cdn.m6web.fr/usp/mb_sd3/2/8/1/Le-1945_c11636016_19-45-du-dimanche-11/Le-1945_c11636016_19-45-du-dimanche-11_unpnp.ism/Le-1945_c11636016_19-45-du-dimanche-11_unpnp-audio_fra=93468-video_eng=1501000.m3u8" video.mp4

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

Keine Kommentare | neuen Kommentar verfassen

Samstag, 27. August 2016

Auf dem Laptop einen Web-Server aktivieren, um Dateien auf ein iPad und iPhone herunterzuladen

In den Ferien stand ich vor dem Problem, eine ca. 50 MB grosse Datei (Teneriffa-Reiseführer von Lonely Planet) von meinem Laptop auf mein iPad und mein iPhone zu kopieren.

Ein Versand per E-Mail war aus zwei Gründen ausgeschlossen: Mein Mail-Server akzeptiert solch grosse Dateien gar nicht erst, und ausserdem verfügte unser AirBnB nur über ein popeliges ADSL-Modem mit Geschwindigkeiten um die Jahrhundertwende. Das Modem hätte mich dementsprechend zuerst mit einem extrem langen Upload zum Mail-Server und anschliessend mit einem extrem langen Download vom Mail-Server genervt. Zu guter Letzt unterstützt mein MacBook auch kein AirDrop, um Dateien per Bluetooth oder WLAN zwischen iOS und OS X-Geräten auszutauschen.

Was nun? Das standardmässige unter OS X installierte Python eilt einem zur Hilfe: Man öffnet das Terminal, navigiert zum Verzeichnis, in welcher sich die gewünschte Datei befindet, und gibt dann folgenden Befehl ein:

$ python -m SimpleHTTPServer
Serving HTTP on 0.0.0.0 port 8000 ...

Nachdem man mittels ifconfig oder dem Network-Modul unter System Preferences die IP-Adresse des Laptops ausfindig gemacht hat, surft man auf den iOS-Geräten die folgende URL mit Safari an:

http://10.1.2.3:8000/

Ein Klick auf den Dokumentenlink, und innert weniger Sekunden wird das grosse PDF angezeigt und kann in iBooks gespeichert werden.

Python meldet auf der Kommandozeile dabei Folgendes:

10.25.9.220 - - [04/Jul/2016 10:14:18] "GET / HTTP/1.1" 200 -
10.25.9.220 - - [04/Jul/2016 10:14:20] "GET /canary-islands-6-whole-book.pdf HTTP/1.1" 200 -
10.25.9.208 - - [04/Jul/2016 10:15:12] "GET / HTTP/1.1" 200 -
10.25.9.208 - - [04/Jul/2016 10:15:18] "GET /canary-islands-6-whole-book.pdf HTTP/1.1" 200 -
10.25.9.235 - - [04/Jul/2016 10:15:57] "GET / HTTP/1.1" 200 -
10.25.9.235 - - [04/Jul/2016 10:16:03] "GET /canary-islands-6-whole-book.pdf HTTP/1.1" 200 -

Tags: , , , ,
Labels: Apple, Linux

Keine Kommentare | neuen Kommentar verfassen

Mittwoch, 29. April 2015

Mit Python eine URL-enkodierte URL lesbar machen

Ein sehr nützliches kleines Script, welches ich mir aus aktuellem Bedarf zusammengeschuster habe:

import urllib.parse
urlEncoded = input("Enter URL to decode: ")

urlDecoded = urllib.parse.unquote_plus(urlEncoded)

print("")
print(urlDecoded)
print("")

elements = urlDecoded.split("?")

print("Base URL: " + elements[0])
print("")

elements = elements[1].split("&")

for element in elements:
    items = element.split("=")

    print(items[0] + ' = ')
    print('    ' + items[1])
    print("")

Tags: , , ,
Labels: Uncategorized

Keine Kommentare | neuen Kommentar verfassen

Donnerstag, 13. November 2014

Der Python-Paketmanager pip

Dann und wann programmiere ich anstelle mit PHP mit Python – wie gestern, als ich meinen Crawler websta-crawler für das (inoffizielle) Instagram-Frontend websta schrieb.

Für dieses Script greife ich auf die zwei Python-Module requests sowie BeautifulSoup zurück, welche bei einer Mac OS X-Standardinstallation nicht mit dabei sind.

Ein einfacher Weg, diese Module nachzurüsten, ist pip, eine Art Paketmanager für Python. Dieses Tool installiert sich folgendermassen:

$ wget https://bootstrap.pypa.io/get-pip.py

Anschliessend führt man das aus dem Internet heruntergeladene Script aus:

# python get-pip.py

Via: Installation

Sobald der Paketmanager ordnungsgemäss installiert wurde, reicht ein Einzeiler, um die zwei gewünschten Module nachzuinstallieren:

# pip install BeautifulSoup requests

Via: How to install multiple python packages at once using pip

Tags: , , , ,
Labels: Programmierung

Keine Kommentare | neuen Kommentar verfassen

Donnerstag, 10. Juli 2014

Mit Python URLs dekodieren und JSON schön ausgeben

Ausgehend von dieser haarsträubenden URL der Credit Suisse …

https://www.credit-suisse.com/who_we_are/de/office_locator.jsp#%7B%22fs%22%3A%7B%22cid%22%3Anull%2C%22prid%22%3Anull%2C%22plid%22%3Anull%2C%22sid%22%3Anull%2C%22d%22%3A%5B0%2C0%2C0%2C0%2C0%2C0%2C0%2C0%2C0%2C0%5D%7D%2C%22ms%22%3A%7B%22c%22%3A%7B%22lat%22%3A46.94739121310314%2C%22lng%22%3A7.44410902261734%7D%2C%22z%22%3A18%7D%2C%22mk%22%3A%7B%22id%22%3A4451%7D%2C%22is%22%3A%7B%22id%22%3A%22mapPanel%22%2C%22ps%22%3A%7B%22id%22%3A4451%2C%22sid%22%3A11613%2C%22segid%22%3Anull%2C%22d%22%3A%5B0%2C0%2C0%2C0%2C0%2C0%2C0%2C0%2C0%2C0%5D%7D%7D%7D

… nachfolgend zwei Python Code-Snippets, mit welchen ihr die URL menschenlesbar machen könnt:

URLs dekodieren

urlClean = urllib.unquote(urlRaw).decode('utf8')

JSON schön ausgeben

json.dumps(json.loads(strJson), sort_keys=True, indent=4, separators=(',', ': '))

Tags: , , ,
Labels: Programmierung

Keine Kommentare | neuen Kommentar verfassen

Freitag, 30. August 2013

Google Chrome unter die Haube schauen (und JSON lesbar ausgeben)

Hierzu haben die Google-Entwickler folgende URI erdacht:

chrome://net-internals/

Über diese Benutzeroberfläche lassen sich Daten auch im JSON-Format exportieren. Damit diese JSON-Daten auch für Menschen (einigermassen) lesbar werden, nimmt man Python zu Hilfe. Im nachfolgenden Beispiel gehen wir davon aus, dass der JSON-Dump in der Datei dump.json abgelegt ist:

python -mjson.tool < dump.json > dump-pretty.json

Tags: , , , , , , ,
Labels: Web

Keine Kommentare | neuen Kommentar verfassen

Freitag, 30. August 2013

urllib unter Python 3 Debug-Meldungen entlocken

Im Grunde ganz simpel – vorausgesetzt, man weiss wie (angelehnt an how to debug a urllib.urlopen in python!, wo aber noch mit urllib unter Python 2 gearbeitet wird):

import urllib.request
import http.client

http.client.debuglevel = 1
http.client.HTTPConnection.debuglevel = 1

url = 'http://www.eMeidi.com/?action=hack'
headers = {}

Request = urllib.request.Request(url,None,headers)
response = urllib.request.urlopen(Request)	
html = response.read()

Dies produziert Ausgaben in folgender Form:

send: b'GET http://www.eMeidi.com/?action=hack HTTP/1.1\r\nAccept-Encoding: iden
tity\r\nHost: www.eMeidi.com\r\nUser-Agent: Python-urllib/3.3\r\nConnection: clo
se\r\n\r\n'
reply: 'HTTP/1.0 407 Proxy Authentication Required\r\n'
header: Date header: Proxy-Authenticate header: Proxy-Connection header: Content
-Type header: Content-Length

Tags: , ,
Labels: Programmierung

Keine Kommentare | neuen Kommentar verfassen

Mittwoch, 14. August 2013

URLs mit Python dekodieren

War gestern auf der Arbeit sehr handlich, um URL-kodierte GET-Parameter wieder lesbar zu machen:

$ python
>>> import urllib.parse
>>> urllib.parse.unquote('http://domain.tld/index.php?t=12%2F7%2F2013%2011%3A19%3A40%201%20-120&ce=UTF-8')
'http://domain.tld/index.php?t=12/7/2013 11:19:40 1 -120&ce=UTF-8'

Tags: , , , ,
Labels: Programmierung

Keine 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