mechanize Datei-Download

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
da.dom
User
Beiträge: 114
Registriert: Dienstag 10. Juni 2008, 14:42

Hallo Zusammen,

gibt es eine Möglichkeit mittels mechanize nur Teile der response zu lesen?
Bei "normalen" HTTP Seiten gibt die Funktion "readline" ja Zeilenweise die Response zurück, jedoch ist diese Funktion nicht besonders brauchbar bei Dateien.

Was ich vorhabe: Ich lade mittels Python Script einige Dateien runter, und würde gerne eine Art Download-Anzeige bauen.
So sieht es bisher aus:

Code: Alles auswählen

size=browser.response().info().getheader("Content-Length")
if size==None:
    size=0
else:
   size=int(size)/1024/1024

content=browser.response().read()
filename="oneFile.pdf"
f=open(filename, "wb")
f.write(content)
f.close()
beim "response().read()" taucht die Shell dann natürlich ab, ohne Rückmeldung bis er mit dem Download fertig ist. Da hatte ich eben gedacht ein Subprozess/thread zu starten der mir eine Art Download-Anzeige ausgibt. Dazu müsste ich aber wissen wie weit er beim lesen der response ist.

Viele Grüße und Danke schon mal
Dom
BlackJack

@da.dom: Blockweise in einer Schleife lesen statt alles auf einmal zu lesen hast Du schon versucht?
da.dom
User
Beiträge: 114
Registriert: Dienstag 10. Juni 2008, 14:42

BlackJack hat geschrieben:Blockweise in einer Schleife
Wenn du mir das syntaktisch einmal zeigst versuche ich das :) . Wie gesagt ich habe gegoogelt und in der API nur read und readlines gefunden, wobei readlines nehme ich an nach linefeeds splittet (was in BinärDateien ja vermutlich nichts exisitert)
BlackJack

@da.dom: Syntaktisch ist da nichts besonderes dran. Das sollte bei dem was `response` zurück gibt, genau so gehen wie bei Dateien -- mit der `read()`-Methode.
da.dom
User
Beiträge: 114
Registriert: Dienstag 10. Juni 2008, 14:42

Achso, stand gerade auf dem Schlauch :) . Scheint nicht zu gehen, ich vermutet er ließt beim ersten Zugriff den kompletten Response (der erste Step dauert) und dann dann feuert er ohne ein Ende zu finden durch die Schleife. Hier mein Beispiel

Code: Alles auswählen

import mechanize

br=mechanize.Browser()

resp=br.open("http://www.teamviewer.com/download/TeamViewerQS.exe")
size=br.response().info().getheader("Content-Length")
if size==None:
    size=0
else:
   size=float(size)/1024/1024
   

chunk=resp.read(512)
print "Download (%.2fmb): " % size , 
content=""
index=1
while len(chunk)!=0:
    content=content+chunk
    print "%.1f/%.2f  " % (index*0.5, size), 
    index+=1
    chunk=resp.read(512)


f=open("test.exe",wb)
f.write(content)
f.close()

Oder habe ich da irgendwo noch einen Fehler drin?
BlackJack

@da.dom: Das funktioniert bis auf zwei Kleinigkeiten ganz wunderbar.

1. Die Anzeige ist irreführend weil Du mit ``index * 0.5`` Kilobytes vor dem Schrägstrich anzeigst, die grösse nach dem Schrägstrich aber in Mebibytes angegeben ist. Ist nicht gerade benutzerfreundlich. ;-)

2. Wenn die Datei dann heruntergeladen ist, gibt es einen `NameError` denn der Name `wb` ist nicht definiert. Das sollte wohl eine literale Zeichenkette sein.

Ansonsten ist die ``while``-Schleife IMHO unschön, da würde ich mit `iter()` und ``lambda`` oder `functools.partial` eine ``for``-Schleife über die Blöcke draus machen. Und `content` ist total ineffizient. Für die ca. 3 MB Datei werden durch das Hinzufügen der Datenblöcke insgesamt ca. 18 MB Daten im Speicher herumkopiert. Das hört sich nicht nach viel an, aber je grösser die Datei, desto *deutlich* grösser wird die Menge an Daten die im Speicher bewegt wird. Bei einer 25 MiB-Datei würde schon mehr als 1 GB sinnlos bewegt. Sammel die Blöcke besser in einer Liste oder schreib sie gleich in eine Datei weg.
da.dom
User
Beiträge: 114
Registriert: Dienstag 10. Juni 2008, 14:42

BlackJack hat geschrieben:@da.dom: Das funktioniert bis auf zwei Kleinigkeiten ganz wunderbar.
1. Die Anzeige ist irreführend weil Du mit ``index * 0.5`` Kilobytes vor dem Schrägstrich anzeigst, die grösse nach dem Schrägstrich aber in Mebibytes angegeben ist. Ist nicht gerade benutzerfreundlich. ;-)
*autsch* das verwirrt auch den Entwickler. Ich hab das Programm gar nicht so lange laufen lassen , weil ich mich gewunder habe das er 512KB so schnell durch meine GPRS Leitung quetscht...und die eigentliche Größe scheinbar schon lange überschritten war, dabei waren es ja 512Bytes ^^.

Das sieht doch mal prima aus, soweit so gut wenn du mir jetzt noch zeigst wie das mit dem iterator geht anstatt die while schleife, habe ich wieder was dazu gelernt und kann mich aufs Sofa legen :)

Viele Grüße
Dom
BlackJack

Code: Alles auswählen

    content = list()
    for index, chunk in enumerate(iter(partial(response.read, 512), '')):
        content.append(chunk)
        print '\r%.1f/%.2f  ' % ((index + 1) * 0.5, size),
Wobei hier der Zusammenhang zwischen der 512 und der 0.5 noch unschön ist. Wenn man eines ändert, muss man immer daran denken auch das andere anzupassen.
da.dom
User
Beiträge: 114
Registriert: Dienstag 10. Juni 2008, 14:42

wow... :oops: falls du die Zeit hast, wäre es fein wenn du das mal auseinander bröselst für mich :)

edit:
ah moment:
partial(response.read, 512) = erzeugt eine neue Funktion die wiederum "read" mit dem Parameer 512 aufruft.
iter() = erzeugt einen Iterator, der die Funktion so oft aufruft bis sie einen Leerstring zurückliefert.
enumerate() = stülpt dem Iterator noch zusätzlich beim iterrieren einen index über.

Korrekt? Coole Sache :)

Grüße
Dom
da.dom
User
Beiträge: 114
Registriert: Dienstag 10. Juni 2008, 14:42

Zum Abschluss noch mal das fertige Programm:

Code: Alles auswählen

  size=browser.response().info().getheader("Content-Length")
    if size==None:
        size=0
    else:
       size=int(size)/1024/1024


    print "download (%.2fmb): " % size ,

    # define file to write to
    filename="somefile.exe"
    outputFile=open(filename, "wb")
    resp=browser.response();
    PARTS_PER_MB=2
    for index, chunk in  enumerate(iter(partial(resp.read, 1024*1024/PARTS_PER_MB), "")):
        outputFile.write(chunk)
        print "%.1f/%.2f  " % (index*(1./PARTS_PER_MB), size),
  
    outputFile.close()
Prima Sache. Großes Lob mal wieder an das Forum (und im speziellen natürlich an BlackJack)

Viele Grüße
Dom
Antworten