Aus einem geschützten Bereich (.htaccess) herunterladen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Ich habe es mit einer "doppelten" Authentifizierung probiert, aber auch fehlgeschlagen. Ich veröffentliche mal mein Minimal-Programm, auch wenn der Quelltext totaler quatsch ist:

Code: Alles auswählen

import requests
import shutil
from requests.auth import HTTPBasicAuth

def log_in(user, pwd):
    s = requests.session()
    resp = s.get('http://xarphus.de/protect_folder/', auth=HTTPBasicAuth(user, pwd))
    print "Status: ", resp.status_code
    content_txt_file = resp.text
    print "Cookies: ", requests.utils.dict_from_cookiejar(s.cookies)
    print "Content of txt file: ", content_txt_file
    print "start downloading"
    response = s.get(content_txt_file, stream=True)
    with open('test_rar.rar', 'wb') as out_file:
        shutil.copyfileobj(response.raw, out_file)
    del response

if __name__ == '__main__':
    user_name = 'test_account'
    user_password = 'test_user'
    log_in(user_name, user_password)
Ich dachte, ich arbeite mal mit der Session, um die Sitzung mit den LogIn-Daten beizubehalten. Das Herunterladen wird vollzogen, aber die Datei ist nach wie vor 290 Byte groß.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Wie glaubst du, weiß ein Server ob ein user eingeloggt ist oder nicht?!?

Das Stichwort wäre: session cookie

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Sophus hat geschrieben:Ich ging nämlich davon aus, (...)
Den Ausspruch habe ich jetzt schon mehrfach von dir gelesen und ich glaube, deine Annahme war jedes Mal ein totaler Griff ins Klo. Möglicherweise würde es ja Sinn machen, sich mit Fachliteratur zu befassen und das wilde Herumgerate ein bißchen zurück zu fahren. ;)

In einigen Fällen kann eine gezielte Google-Suche die Fachliteratur auch ersetzen (sofern man gute Online-Artikel nicht schon als Fachliteratur bezeichnen möchte). Aber das Programmieren völlig ins Blaue hinein kann auf diesem Themengebiet einfach nur schiefgehen.

Da hilft einem dann auch keine Bibliothek, wenn es an den Grundlagen schon scheitert. Die Bibliothek erleichtert in aller Regel ja nur die Anwendung. Man sollte als Programmierer trotzdem technisches Hintergrundwissen mitbringen, bei dem, was man tut.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@snafu: Ich weiß, du meinst es gut. Also nimm es mir nicht persönlich was ich jetzt schreiben werde. Würde ich wild herumraten, so stünde da am Ende nur WirrWarr, ein Quelltext, der von der ersten Zeile an falsch ist. Trifft dies hierbei zu? 8) Ich möchte nicht raten, sondern versuchen es zu verstehen. Das ich nicht immer richtig liege oder Dinge nicht auf Anhieb alles richtig verstehe, ist richtig. Wäre ich richtig gut und ein Bill Gates 2.0, dann wär ich bestimmt nicht hier im Forum angemeldet oder? :)

EDIT: Finde ich es schon nahezu übertrieben, zu behaupten, ich mache grundsätzliche alles falsch, und sollte am Besten nochmal mit dem ABC unserer deutschen Muttersprache anfangen. Am Ende ist es vermutlich nur eine einzige Zeile, die ich vermutlich durch zu vieles Nachdenken übersehen habe. Das wäre ja so, als wenn ein Professor wegen einer falschen Zeile behaupten würde, meine ganze wissenschaftliche Arbeit wäre "hinfällig", und ich solle mir mal Literatur besorgen "Das ABC, wie schreibe ich eine wissenschaftliche Arbeit" :shock: Und am Ende habe ich nur eine Zeile falsch gemacht.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Nun, damit wir hier nicht den Eindruck bekommen, dass ich hier wild auf die Tastatur klopfe - in der Hoffnung, es ergeben zufällig paar nützliche Python-Befehle - werde ich mich mal bemühen den Code so zu beschreiben wie ich ihn interpretiere. Es wird sich dann zeigen, ob ich wirklich nur blind rate :)

Zeile 1: Die Bibliothek requests wird importiert. Diese Bibliothek benötigen wir um später überhaupt mit dem Web-Server kommunizieren zu können
Zeile 2: Hier wird die "Basis Authentifizierung" importiert. Warum? Weil viele Web-Services, die eine Authentifizierung erfordern, in der Regel die HTTPBasicAuth akzeptieren.
Zeile 3: Die Bibliothek shutil wird importiert. Wird später für Dateioperationen benötigt (Kopiervorgang).
Zeile 5: Ich definiere hier eine def get_logged_in_session():-Funktion. Diese Funktion werden zwei Argumente hinzugefügt, einmal user und einmal pwd.
Zeile 7: Hier wird eine Session-Instanz aufgebaut. Mit diesem Session-Objekt wird ermöglicht, dass bestimmte Parameter (z.B. Benutzername und Passwort) bestehen bleiben.
Zeile 8: Nun erstelle ich mit HTTPBasicAuth eine Anfrage. Ich übergebe der get()-Methode eine URL, die geschützt ist, und dann durch einen Komma getrennt die Authentifizierung (auth=HTTPBasicAuth(user,pwd)). user und pwd sind hierbei Argumente. Wenn diese Funktion aufgerufen wird, werden der Funktion Argumente übergeben. Die Argumente werden verarbeitet und für die Anfrage verwendet. Jetzt habe ich ein sogenanntes Response-Objekt (resp). Dort werden die Informationen gespeichert. Dieser Vorgang ist deswegen wichtig, damit ich im späteren Verlauf immer wieder auf das Response-Objekt zugreifen kann.
Zeile 12: Das Response-Objekt verfügt über ein status_code()-Attribut. Ich lasse mir anzeigen, wie der gegenwärtige Status lautet. War die Anmeldung erfolgreich, dann bekommt man die Zahl 200. Bekommt man unter anderem die Zahl 401, dann kann man davon ausgehen, dass die Anmeldung fehlgeschlagen ist.
Zeile 13: Ich lasse mir mit Hilfe des text-Attributes den Inhalt ausgeben. Auf meinem Web-Server wird der entsprechende Anwender auf eine bestimmte nach erfolgreicher Anmeldung auf Text-Datei weitergeleitet. Hier bekomme ich also den Inhalt der Text-Datei angezeigt. In meinem Fall wird dann eine URL angezeigt. Der Inhalt wird in die content_txt_file-Variable gespeichert.
Zeile 14: Hier wird das Objekt cookie über die Print-Anweisung ausgegeben.
Zeile 15: Zur Überprüfung lasse noch einmal den Inhalt per Print-Anweisung ausgeben, um sicher zu gehen, ob ich hier eine URL habe. Also greife ich auf die content_txt_file-Variable zu.
Zeile 18: Hier wird das Herunterladen vorbereitet. Ich greife also auf die content_txt_file-Variable zu. Denn dort ist ja die URL gespeichert, die mir sagt, wo die Datei gespeichert ist. Darüber hinaus verwende ich stream=True. Auf diese Weise bleibt die Verbindung geöffnet und der Header wird heruntergeladen. All diese Information wird in ein Response-Objekt (response) gespeichert.
Zeile 19: Hier beginne ich mit einer with-Anweisung. Mit dieser Anweisung erspare ich mit hinterher das Schließen der Datei. Denn die with-Anweisung macht es quasi für mich. In der with-Anweisung finden wir zwei Parameter. Im ersten Parameter gebe ich einen Namen an. Den kann man beliebig benennen. Hier heißt meine Datei nach dem Herunterladen test_rar.rar. Der zweite Parameter besagt zunächst, dass schreibend (w) auf die Datei zugegriffen werden soll. Warum? Wenn eine Datei heruntergeladen wird, muss schreibend darauf zugegriffen werden. Das b ist ein Modus, hier greife ich also schreibend und binär auf die Datei zu. Innerhalb der with-Anweisung muss man diesen Vorgang mit eigenen Variablen deklarieren. Da es sich hierbei um eine heruntergeladene Datei handelt, die schreibend erstellt wird, deklariere ich es als (as) out_file. Im späteren Verlauf ist also die Variable out_file für die Verwendung wichtig.
Zeile 20: Jetzt kommen wir zum shutil-Modul. Ich bediene mich in diesem Kontext der copyfileobj ()-Methode. Warum? Wir erinnern uns, dass ich während des Herunterladen schreibend auf eine Datei zugreife. Mit diese Methode wird es mir also möglich sein, das Kopieren einer Datei mit dem tatsächlichen Dateiobjekt. Das heißt, die Datei, die auf dem Server liegt, wird schreibend auf dem lokalen Rechner asl Dateiobjekt kopiert. Mit einem Blick in die Methode sehen wir, dass ich response.raw verwende. Damit ich das Attribut raw verwenden kann, muss also stream=True gegeben sein. Ich bekomme hier also eine rohe Antwort vom Webserver. Und in dieser Methode steckt ein weiter Parameter out_file, hier wird gesagt, in welches Dateiobjekt die Datei auf dem Rechner kopiert werden soll, die vom Server herunterladen wird.
Zeile 21: Hier lösche ich das Response-Objekt, sprich ich leere das Objekt. Weil ich es nach dem erfolgreichen Herunterladen nicht mehr gebrauchen kann.
Zeile 22: Und ich schließe auch die erste Anfrage (resp), um die Kommunikation mit dem Web-Server zu unterbrechen. Ich muss ja nicht mehr mit dem Web-Server kommunizieren. Hat sich ja alles erledigt.
Zeile 25: Wird das Modul als Haupt-Modul gestartet, also nicht ein einem anderen Modul aus, dann greift hier eine Bedingung.
Zeile 26 und Zeile 27: : Hier werden Informationen für die Anmeldung in den jeweiligen Variablen gespeichert. Hier werden die Informationen mittels String-Literale angegeben, d. h. direkt im Modul angegeben. Die Angaben werden also nicht erst zur Laufzeit bekannt gegeben, sondern schon vorher festgelegt.
Zeile 28: Nun rufe ich die get_logged_in_session()-Funktion auf. Der Funktion werden dann die zuvor festgelegten Informationen (user_name und user_password) als Argumente übergeben.

Code: Alles auswählen

import requests
from requests.auth import HTTPBasicAuth
import shutil

def get_logged_in_session(user, pwd):

    s = requests.session()
    resp = s.get('http://xarphus.de/protect_folder/', auth=HTTPBasicAuth(user,pwd))

    print "Status: ", resp.status_code
    content_txt_file = resp.text
    print "Cookies: ", s.cookies
    print "Content of txt file: ", content_txt_file
    print "start downloading"
   
    response = s.get(content_txt_file, stream=True)
    with open('test_rar.rar', 'wb') as out_file:
        shutil.copyfileobj(response.raw, out_file)
    del response
    resp.close


if __name__ == '__main__':
    user_name = 'test_account'
    user_password = 'test_user'
    get_logged_in_session(user_name, user_password)
Zuletzt geändert von Sophus am Donnerstag 23. Juli 2015, 20:25, insgesamt 2-mal geändert.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Wow Dir ist echt langweilig. Glaubst Du ernsthaft, dass das jemand liest?
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@jerch: Na, mir wird ja ständig unterstellt, das ich wild, und ohne nachzudenken herumrate, und das mir nur durch Zufall einige Python-Befehle auf dem Bildschirm landen. Ich möchte ja nur aufzeigen, dass ich durchaus nachdenke beim Schreiben, und nicht Löcher in die Decke blicke, und einfach wahllos irgendwas tippe :)
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@jens: Du gabs mit die Stichwörter Session und Cookies. Ich benutze gegenwärtig die Version 2.7.0, also die neuste Version. Werden die Cookies nicht mit der Session dauerhaft erzeugt? Sprich, ich muss also nicht mehr künstlich ein Cookie hinzufügen, oder?
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: Du hast die Funktionsweise einer Authentifizierung nicht verstanden. Da hilft es auch nicht, dass Du versuchst, Python-Code zu schreiben, der für Dich sinnvoll erscheint. Du mußt Dich halt an die Regeln des HTTP-Protokolls halten. Cookies spielen dabei übrigens gar keine Rolle. Hier steht übrigens, wie Du Dein Problem mit Sessions lösen kannst. del braucht man so gut wie nie, hier ist es völlig überflüssig. close solltest Du auch aufrufen, damit es einen Effekt hat. response schließt Du im Übrigen auch nicht.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Sirius3: Ich habe deine Anmerkungen zu den Cookies angenommen und es gleich umgesetzt, auch habe ich den Quelltext optisch etwas aufgebesser:

Code: Alles auswählen

def get_logged_in_session(user, pwd):

    url = 'http://xarphus.de/protect_folder'
    auth = HTTPBasicAuth(user, pwd)

    s = requests.session()

    resp = s.get(url=url, auth=auth)

    print "Status: ", resp.status_code
    content_txt_file = resp.text

    print "Cookies: ", s.cookies
    print "Content of txt file: ", content_txt_file

    print "init the download"
    response = s.get(content_txt_file, stream=True)

    print "start downloading"
    with open('test_rar.rar', 'wb') as out_file:
        shutil.copyfileobj(response.raw, out_file)

    print "Closing response"
    response.close
    print "response is closed"

    print "Closing resp"
    resp.close
    print "resp is closed"
Zum Link, den du mir gegeben hast. Ich habe diesen Link mehrmals durchgelesen, denn es war einer der ersten Seite, den ich mir für dieses Thema ausgesucht habe. Jedoch fand ich keine Stelle, wo die Problematik mit der Authentifizierung angesprochen wird. Es wird nur das Konstrukt (HTTPBasicAuth) der Authentifizierung aufgezeigt, den ich ja auch übernommen habe. Wahrscheinlich sehe ich den Wald vor lauter Bäumen nicht und habe mich gedanklich mächtig festgefahren. Ich weiß, zu viele Print-Anweisungen, aber die verschwinden ja am Ende sowieso. Ist nur für den Testzweck.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Lass dir mal die header vom Server ausgeben.

Bzw nimm einfach Firefox und schau dir an was so hin und her über tragen wird...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@jens: Im Header steht folgende Information, die ausgegeben wird:
{'content-length': '64', 'via': '1.1 varnish', 'content-encoding': 'gzip', 'accept-ranges': 'bytes, bytes', 'vary': 'Accept-Encoding', 'server': 'Apache', 'last-modified': 'Thu, 23 Jul 2015 00:07:09 GMT', 'connection': 'keep-alive', 'x-varnish': '336783642', 'etag': '"e627fa31-2e-51b7fab299961"', 'date': 'Thu, 23 Jul 2015 20:59:42 GMT', 'content-type': 'text/plain', 'age': '0'}
Daneben habe ich mir dann HttpFox 0.8.14.1 heruntergeladen, installiert und angewendet.

Das Ergebnis bei der Anmeldung sieht wie folgt aus:
Bild
(Anklicken, Bild vergrößert sich)

Nachdem ich den Link bekomme, kopiere ich den Link, füge es in die Adresszeile, und das Herunterladen beginnt. In der letzten Zeile sieht man es. In der Spalte "Empfangen" steht dann 39M.
Bild
(Anklicken, Bild vergrößert sich)
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Nachdem ich meinen Quelltext mehrmals rauf und runter interpretiert und viele Dokumentationen im Internet darüber angesehen habe, bin ich dann so langsam über eine Stelle gestolpert.

Code: Alles auswählen

def get_logged_in_session(user, pwd):

    url = 'http://xarphus.de/protect_folder'
    auth = HTTPBasicAuth(user, pwd)

    s = requests.session()

    resp = s.get(url=url, auth=auth)
    
    print "Look cookie", resp.text

    print "Status: ", resp.status_code
    content_txt_file = resp.text
    print "Cookies: ", s.cookies
    print "Content of txt file: ", content_txt_file
    print "init the download"
    response = s.get(content_txt_file, stream=True)
    print "start downloading"
    with open('test_rar.rar', 'wb') as out_file:
        shutil.copyfileobj(response.raw, out_file)
    print "Closing response"
    response.close
    print "response is closed"
    print "Closing resp"
    resp.close
    print "resp is closed"
Ich bin über die Zeile 16 gestolpert. Ich greife hier zwar auf die content_txt_file -Variable zu, in der der Link zu der herunterzuladende Datei gespeichert ist. Jedoch greife ich hier ohne Authentifizierung zu. Sirius3 und jens haben mich mehrmals darauf aufmerksam gemacht, nur ist es mir immer wieder entgangen.

Ich habe in Zeile 16 diese Zeile

Code: Alles auswählen

response = s.get(content_txt_file, stream=True)
in diese Zeile

Code: Alles auswählen

response = s.get(url=content_txt_file, auth=auth, stream=True)

umgewandelt.

Die URl ist weiterhin content_txt_file geblieben, denn dort ist ja der Link zu der Datei gespeichert, nur habe ich auth=auth hinzugefügt. Hier wird also beim Herunterladen nochmal authentifiziert.
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: Deine Funktion hat einen seltsamen Namen, für das, was sie macht. Sie benutzt irgendwelche globalen Variablen und referenziert Methoden, die sie dann nicht aufruft. Und in dem Link, den ich geschickt hatte, war gleich im ersten Abschnitt ein Beispiel, man die Authetifizierungsinformation nur einmal in einer Session angeben muß.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Sirius3 hat geschrieben:Sie benutzt irgendwelche globalen Variablen und referenziert Methoden, die sie dann nicht aufruft.
@Sirius3: Welche globalen Variablen meinst du? Die Variablen, die in der Funktion ohne eine self-Referenz stehen gelten doch nicht global? Ich bin mir sicher, dass ich dich falsch verstehe.
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: in der Funktion Deines letzten Posts gibt es kein self.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Sirius3: Stimmt. Keine Ahnung wieso ich das geschrieben habe. Aber dennoch verstehe ich deine Anmerkung nicht, wenn du von globalen Variablen schreibst.
Antworten