Auto-Login auf Webseite

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.
Antworten
kickdoll
User
Beiträge: 9
Registriert: Mittwoch 24. September 2008, 17:38

Hallo allerseits,

ich möchte mir ein Skript schreiben welches sich auf einer Webseite Einloggt und dort eine Datei runterlädt.
Das mit dem Auto-login habe ich bereits fertig, leider habe ich keine Idee wie das mit dem download gemacht wird.
Auf der Webseite befindet sich ein Formular, die Einträge werden mit POST übergeben, aber es könnte sein, dass dort erst ein javascript die Datei erstellt. Ich hoffe jemand kann mir da "unter die Tasten greifen" ;)

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import cookielib
import urllib
import urllib2

cj = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
resp = opener.open('http://stooq.pl') # save a cookie

theurl = 'http://stooq.pl/logred.htm' # an example url that sets a cookie, try different urls here and see the cookie collection you can make !
body={'in':'1','url':'/','login':'########','haslo':'########'}
txdata = urllib.urlencode(body) # if we were making a POST type request, we could encode a dictionary of values here - using urllib.urlencode
txheaders =  {'User-agent' : 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'} # fake a user agent, some websites (like google) don't like automated exploration


try:
    req = urllib2.Request(theurl, txdata, txheaders) # create a request object
    handle = opener.open(req) # and open it to return a handle on the url
    HTMLSource = handle.read()
    f = file('home.html', 'w')
    f.write(HTMLSource)
    f.close()
# http://stooq.pl/bazy/waluty.html
    theurl = 'http://stooq.pl/bazy/waluty.prn'
    body   = {'data':'20080922','granica':'0','waluty_4':'1','beztrans':'1','prn':'1'}
    txdata = urllib.urlencode(body)
    req    = urllib2.Request(theurl,txdata, txheaders) # create a request object
    handle = opener.open(req) # and open it to return a handle on the url
    HTMLSource = handle.read()
    f = file('pobur.html', 'w')
    f.write(HTMLSource)
    f.close()
    
except IOError, e:
    print 'We failed to open "%s".' % theurl
    if hasattr(e, 'code'):
        print 'We failed with error code - %s.' % e.code
    elif hasattr(e, 'reason'):
        print "The error object has the following 'reason' attribute :", e.reason
        print "This usually means the server doesn't exist, is down, or we don't have an internet connection."
        sys.exit()

else:
    print 'Here are the headers of the page :'
    print handle.info() # handle.read() returns the page, handle.geturl() returns the true url of the page fetched (in case urlopen has followed any redirects, which it sometimes does)
    print handle.read()
    print handle.geturl()
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Alles wegwerfen, ``mechanize`` nutzen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
kickdoll
User
Beiträge: 9
Registriert: Mittwoch 24. September 2008, 17:38

Vielen Dank für den Tip, werde mich gleich einarbeiten.

EDIT: nach Einarbeitung in mechanize

Der folgende Code ist schon übersichtlicher.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import mechanize
import ClientForm
from ClientForm import ParseResponse
# Call html document and store cookie
request  = mechanize.Request("http://www.stooq.pl/")
response = mechanize.urlopen(request)
# Read the form here form[2]
forms = ParseResponse(response, backwards_compat=False)
form  = forms[2]
# Set login and password
form.set_value("######", name="login")
form.set_value("######", name="haslo")
# Send form
request  = form.click()
response = mechanize.urlopen(request)

request  = mechanize.Request("http://stooq.pl/bazy/waluty.html")
response = mechanize.urlopen(request)
forms    = ParseResponse(response, backwards_compat=False)
form     = forms[3]

# Set values for file
...
# Hier wird die Datei erzeugt die ich speichern möchte
request  = form.click()
#response = mechanize.urlopen(request)
print request

response.close()
Leider bin ich wieder an der gleichen Stelle überfragt. Wenn ich das Formular abschicke weiss ich nicht wie ich die Datei downloaden kann. Im Browser kann man sie dann irgendwo abspeichern. Kann mir jemand sagen wie ich das hiermit mache?
kickdoll
User
Beiträge: 9
Registriert: Mittwoch 24. September 2008, 17:38

Hallo,

leider ist mein Problem noch nicht aus der Welt.
Ich habe mit mechanize die Seite aufgerufen und mich dort erfolgreich angemeldet.
Nun fülle ich ein weiteres Formular aus und möchte damit eine Datei anfordern, da klappt es noch nicht.
Ich befinde mich also auf der Seite ...bazy/waluty.html und möchte hier ein Formular ausfüllen.

Code: Alles auswählen

<form method=post action=bazy/waluty.prn name=prn>
Das bedeutet ja ich soll das Formular an bazy/waluty.prn absenden ?

Code: Alles auswählen

request  = mechanize.Request("http://www.stooq.pl/bazy/waluty.prn")
Nun bekomme ich als respose aber:

Code: Alles auswählen

<closeable_response at 0xbc93f8 whose fp = <socket._fileobject object at 0  x7f6669a2f848>>
Jetzt die Frage woran scheitert das, muss ich um eine Datei anzufordern noch einen Trick anwenden?

Ich würde mich über jede Hilfe freuen.
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ungetestet:

Code: Alles auswählen

import mechanize

br = mechanize.Browser()
br.open('http://www.stooq.pl/')
br.select_form(nr=2)
br.set_value("######", name="login")
br.set_value("######", name="haslo")
br.submit()
br.open('http://stooq.pl/bazy/waluty.html')
# Kram den du hier machen willst, siehe oben
result = br.submit().read()
submit() drückt sozusagen den Standard-Knopf für "Absenden".

read() dient dir zum Auslesen des Inhalts, falls du den anzeigen möchtest. Willst du ihn später an einen Parser oder sowas weiterleiten, kann das read() wegfallen.

select_form() übernimmt das Parsen und Auswählen mit ClientForm, und du wendest dann einfach die ClientForm-Befehle auf das br-Objekt an. ;)
kickdoll
User
Beiträge: 9
Registriert: Mittwoch 24. September 2008, 17:38

Vielen Dank für die schnelle Hilfe.

Irgendwas scheint jetzt beim login nicht zu klappen, denn ich bekomme nach

Code: Alles auswählen

br.submit().read()
Nicht autorisiert zu sehen. Wird auf diese Weise auch ein cookie gespeichert?
Was ist mit den hidden values muss man die auch setzen?
Was mir eben aufgefallen ist:

Code: Alles auswählen

<form method=post action=bazy/waluty.prn name=prn>
Hier steht ein name=prn, wenn ich mir das Formular ausgeben lasse erscheint:

Code: Alles auswählen

<prn POST http://stooq.pl/bazy/waluty.prn application/x-www-form-urlencoded
jetzt steht ja am Anfang prn und nicht form, macht das was aus?

Sorry für eventuell banale Fragen, aber Python benutze ich erst seit 3 Tagen ;)
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Was willst du eigentlich auslesen? Das HTML was dort ist, ist ja mal ziemlich grottig, reicht es nicht die Kurse aus der XML-Dateider EZB herauszuparsen?
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Falls es wirklich nur um die Kurse geht:

Code: Alles auswählen

from __future__ import with_statement

from contextlib import closing
from itertools import izip
from urllib import urlopen

from lxml.html import soupparser


def main():
    url = 'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml'
    with closing(urlopen(url)) as f: 
        site = soupparser.parse(f)
    nodes = site.findall('.//cube[@rate]')
    curs = (elem.get('currency') for elem in nodes)
    rates = (elem.get('rate') for elem in nodes)
    for cur, rate in izip(curs, rates):
        print '%s : %s' % (cur, rate)


if __name__ == '__main__':
    main()
kickdoll
User
Beiträge: 9
Registriert: Mittwoch 24. September 2008, 17:38

Danke für eure Hilfe.
Es geht nicht nur um die Tageskurse. Es geht um weitere quotes auch für Futures. Ich habe dort einen Account und kann mir also die Datei mit den Quotes runterladen, aber es ist mir zu umständlich jedes mal selbst zu klicken also wollte ich das automatisieren. Die Datei waluty.prn wird erstellt wenn ich auf den Submit button klicke, danach erscheint ein Dialog wo ich diese gerne Speichern möchte.
Die Kurse sind nicht in dem HTML sondern in der Datei waluty.prn.
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ausgehend von meinem weiter oben geschriebenen Code:

Code: Alles auswählen

[...]
result = br.submit().read()
try:
    f = open('waluty.prn', 'w')
    f.write(result)
finally:
    f.close()
Das legt die Datei waluty.prn im aktuellen Verzeichnis an und schreibt dort den Inhalt von result rein.

Wenn du nen Ordner für diese Dateien anlegen möchtest und sie dann täglich abrufst, bietet sich das Abspeichern nach Datum an:

Code: Alles auswählen

from time import strftime, localtime
filename = strftime('%Y-%m-%d', localtime()) + '.prn'
Ergibt:

Code: Alles auswählen

'2008-09-26.prn'
Zuletzt geändert von snafu am Freitag 26. September 2008, 20:24, insgesamt 3-mal geändert.
kickdoll
User
Beiträge: 9
Registriert: Mittwoch 24. September 2008, 17:38

Die Datei wird erstellt, aber drinn steht nur, dass die autorisierung fehlgeschlagen ist. Bei diesem Vorgehen scheint der Login nicht zu klappen.
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Zeig mal bitte deinen aktuellen Code...
kickdoll
User
Beiträge: 9
Registriert: Mittwoch 24. September 2008, 17:38

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import mechanize

br = mechanize.Browser()
br.open("http://www.stooq.pl/bazy/waluty.html")

br.select_form(nr=2)
br.set_value("#####", name="login")
br.set_value("#####", name="haslo")
result = br.submit().read()
print result
br.select_form(nr=3)
print br
result = br.submit().read()

try:
    f = open('waluty.prn', 'w')
    f.write(result)
finally:
    f.close()
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Änder mal Zeile 10-11 so:

Code: Alles auswählen

br['login'] = 'hier dein username'
br['haslo'] = 'hier das passwort'
Und lösche Zeile 13-16...
Zuletzt geändert von snafu am Freitag 26. September 2008, 20:52, insgesamt 2-mal geändert.
kickdoll
User
Beiträge: 9
Registriert: Mittwoch 24. September 2008, 17:38

Klappt noch nicht ganz. Wenn Zeilen 13-16 gelöscht sind dann wird das HTML gespeichert und man kann es sich im Browser anschauen ich bin eingeloggt. Um aber die Datei .prn anzufordern muss ich das Formular nr=3 absenden also Zeilen 14 und 16, dann steht in der Datei wieder nicht autorisiert.
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Achso, ich verstehe...

Hast du schon geprüft ob nach dem Login immer noch nr=3 für das gewünschte Formular steht? Vielleicht hat sich ja alles um eine Stelle verschoben, weil kein Login-Formular mehr da ist. Nur ne Vermutung. Ich kenne die Seite ja nicht nach dem Login...

Also nochmal zur Verdeutlichung:

Code: Alles auswählen

import mechanize

br = mechanize.Browser()
br.open("http://www.stooq.pl/bazy/waluty.html")

br.select_form(nr=2)
br['login'] = 'dein username'
br['haslo'] = 'dein passwort'
br.submit()

br.select_form(nr=3) # hier vielleicht auf 2 ändern?
# Felder ausfüllen, wenn nötig
result = br.submit().read()

try:
    f = open('waluty.prn', 'w')
    f.write(result)
finally:
    f.close()
kickdoll
User
Beiträge: 9
Registriert: Mittwoch 24. September 2008, 17:38

Das Formularfeld ist nr =3, habe ich eben nochmal geprüft.
Ich denke, dass die Feler nicht ausgefüllt werden müssen weil die Einstellung bereits in meinem Account gespeichert ist.
Check mal dein Postfach.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

snafu, warum nutzt du denn für wohlgeformtes XML einen HTML-Soup-Parser? Eben deswegen habe ich ja das XML verlinkt, weil es für die Weiterverarbeitung gedacht ist.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ich musste bisher noch nie einen XML Parser nutzen und daher ist mir auf die Schnelle nichts besseres eingefallen.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

snafu hat geschrieben:Ich musste bisher noch nie einen XML Parser nutzen und daher ist mir auf die Schnelle nichts besseres eingefallen.

Code: Alles auswählen

lxml.etree.fromstring()
Und raus kommt ein passender ElementTree.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Antworten