Websites besuchen

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
MarcelF6
User
Beiträge: 226
Registriert: Samstag 3. März 2012, 21:30

Hallo miteinander

Ich stehe vor folgendem Problem: Mein Python-Programm muss eine Reihe Websites nacheinander besuchen, die sich jeweils in der URL nur in Ziffern unterscheiden. Der Start liegt bei: http://www.test.xyz/0000?action=source und die letzte zu besuchende Seite ist (beispielsweise) http://www.test.xyz/9999?action=source.
Ursprünglich wollte ich das Problem via Integers lösen, nur: 0000 wird zu einer "simplen" 0, d.h. so funktioniert das ganze nicht.
Hat jemand eine Idee, wie man das hinbekommen könnte?

Besten Dank für jeden Input.
Marcel
mcdwerner
User
Beiträge: 113
Registriert: Donnerstag 7. Juli 2011, 14:27

hi,
versuch's mit string Formatierung:
http://docs.python.org/2/library/string ... matstrings
Dami123
User
Beiträge: 225
Registriert: Samstag 23. Februar 2013, 13:01

Code: Alles auswählen

if __name__=="__main__":
    url = ("http://www.test.xyz/", "?action=source")
    for i in range(9999):
        if len(str(i)) == 1:
            i = "000"+str(i)
        elif len(str(i)) == 2:
            i = "00"+str(i)
        elif len(str(i)) == 3:
            i = "0"+str(i)
        elif len(str(i)) == 4:
            i = str(i)
            
        name = "%s%s%s" % (url[0], i, url[1])
    
        print name
BlackJack

@Dami123: Das ist wohl so das komplizierteste was machen machen kann (bevor es absurd wird). mcdwerner hat doch schon Zeichenkettenformatierung vorgeschlagen.
MarcelF6
User
Beiträge: 226
Registriert: Samstag 3. März 2012, 21:30

mcdwerner, danke vielmals für den Tipp!
Ich habe eine einfache Funktion erstellen können, die genau nun genau das macht, was ich wollte :)

Dami123, danke für das Beispiel. In der Zwischenzeit habe ich halt schon ein eigenes erstellt, aber es ist immer schön zu wissen, dass einem hier geholfen wird :)

Eine andere Frage hätte ich noch, sie betrifft BeautifulSoup:
BS4 ordnet die Attribute im XML automatisch alphabetisch. Gibt es eine Möglichkeit, das zu unterdrücken? Zudem fügt BS4 Endknoten (</table>) automatisch an vorherige Elemente an, obwohl ich eigentlich lieber einen Zeilenumbruch hätte.
Klar, das sind alles optische Details, die python selbst nicht stören. Aber trotzdem wolle ich die Frage hier kurz stellen :)
Dami123
User
Beiträge: 225
Registriert: Samstag 23. Februar 2013, 13:01

@BlackJack
Ja das stimmt :lol: Habs aus nem alten Script kopiert.
Das funktioniert um einiges besser.

Code: Alles auswählen

    for i in range(9999):
        print "%04d" % i
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Dami123 hat geschrieben:@BlackJack
Ja das stimmt :lol: Habs aus nem alten Script kopiert.
Hui... das hättest Du Dir überlegen sollen... das ist glatt etwas für The Daily WTF ;-)

Bitte benutze doch in Zukunft für Python-Code die Python-Code-Tags [ python ] Code [ /python ] oder allgemein (für viele andere Sprachen) [ code=python ] Code [ /code ] (Ohne die Leerzeichen).
MarcelF6 hat geschrieben: BS4 ordnet die Attribute im XML automatisch alphabetisch. Gibt es eine Möglichkeit, das zu unterdrücken?
Da die Reihenfolge von Attributen in XML keiner Semantik unterliegt, wage ich das zu bezweifeln. Intern wird BS das auch sicher nicht ordnen, sondern erst bei der Ausgabe. Wenn BS API da einen Hook anbietet, bei dem man sich in die Formatierung einhängen kann, dann kannst Du das sicher ändern, wenn nein, dann nicht, außer Du schreibst Deine eigene Serialisierungs-Engine.

Wobei dann die Frage ist, in welcher Reihenfolge Du sie haben willst? Ich könnte mir vorstellen, dass BS intern ein Dictionary für die Attribute verwendet; dann ist die ursprüngliche Reihenfolge eh futsch ;-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
MarcelF6
User
Beiträge: 226
Registriert: Samstag 3. März 2012, 21:30

Ok, besten Dank für die Infos!
Das hab' ich mir gedacht - da sich der Aufwand in diesem Fall nicht lohnen würde, lass ich es, wie es ist :)
Danke euch für die Inputs :)
Dami123
User
Beiträge: 225
Registriert: Samstag 23. Februar 2013, 13:01

@Hyperion
So schlimm schien mir die Lösung nicht, als ich sie programmiert hab :D Obs ein ein Artikel wert ist..

Mach ich.
MarcelF6
User
Beiträge: 226
Registriert: Samstag 3. März 2012, 21:30

Ich habe doch noch eine Frage: Wie bekommt man (im HTML-File) inhaltsleere Tags gelöscht?
Beispiel: " <b></b> " soll einfach gelöscht werden.
Ich habe versucht, das html-File zu öffnen und dann nach "<b></b>" zu suchen und bei einem Fund es via re.sub() zu ignorieren.
Allerdings ohne Erfolg. Hätte jemand einen Vorschlag?
BlackJack

@MarcelF6: Du benutzt doch schon BeautifulSoup4 — also einfach die entsprechenden Elemente suchen und mit der `extract()`-Methode entfernen. Wobei man aufpassen muss was man entfernt, denn ein leeres `<a>` mit einem `id`-Attribut sollte man beispielsweise besser nicht entfernen.
MarcelF6
User
Beiträge: 226
Registriert: Samstag 3. März 2012, 21:30

Danke für den Hinweis.
Ich habe momentan diesen Code, und bin eigentlich der Meinung, dass es so klappen müsste. Allerdings habe ich im output-File immernoch die inhaltsleeren Tags <b></b>. Woran könnte das liegen bzw. was ist wie zu ändern?
Danke für alle Inputs :)

Code: Alles auswählen

output = codecs.open("test.html", "a", "utf-8")

for i in range(0, 10):
    nr = add_nulls(i, 4)

    f = urllib2.urlopen('http://blubb.xyz')
    html = f.read()
    f.close()
    soup = BeautifulSoup(html)

    txt = ""

    for text in soup.find_all("table", {'class': 'main'}):
        txt += str(text)

    txt = str(txt)
	
    text = "\n".join(re.sub(r'\[:[\/]?T.*?:\]', '', el) for el in txt.splitlines())

    bs = BeautifulSoup(text)
    empty_tags = bs.find_all(lambda tag: tag.name == 'b' and tag.find(True) is None and (tag.string is None or tag.string.strip()=="")) 
    [empty_tag.extract() for empty_tag in empty_tags]

    output.write(text.decode("utf-8"))
BlackJack

@MarcelF6: Argh, ist das eine gruselige Mischung aus ordentlicher Verarbeitung eines Objektbaums und Zeichenkettenmurks. Und daran liegt es dann letztendlich auch. Du veränderst den Objektbaum, speicherst dann aber die unveränderte Zeichenkette aus welcher der Objektbaum erzeugt wurde.

Und „list comprehensions” sind zum Erzeugen von Listen da die man auch benutzen möchte, und nicht um sinnfreie Listen zu erzeugen die nicht verwendet werden. Das ist keine schicke Syntax um eine ``for``-Schleife in eine Zeile zu schreiben. Wobei man *das* auch ohne „list comprehension” machen könnte, aber aus Gründen der Lesbarkeit nicht sollte.
MarcelF6
User
Beiträge: 226
Registriert: Samstag 3. März 2012, 21:30

Hm ok, ich sehe was du meinst.
Und genau bei der Speicherung der modifizierten Struktur habe ich Mühe: Wie kann man die modifizierte Struktur speichern?
Würdest du eine zusätzliche Variable einführen (=die modifizierte Struktur), und diese via output.write in das File speichern?
Danke für die Tipps!
BlackJack

@MarcelF6: Du hast die Struktur doch schon an einen Namen gebunden. Sonst hättest Du sie ja nicht verändern können.

Edit: Was soll denn eigentlich ``txt = str(txt)`` bewirken? Und wieso benutzt Du `codec.open()` wenn Du `str`-Werte speichern willst, die Du erst mit der gleichen Kodierung dekodierst, die das Dateiobjekt zum schreiben verwendet?
MarcelF6
User
Beiträge: 226
Registriert: Samstag 3. März 2012, 21:30

Ah dumm; klar. Hab den Fehler entdeckt.
txt = str(txt) verwende ich, damit ich nachher die splitlines()-Funktion anwenden kann.
Den Punkt zur Codierung verstehe ich nicht ganz...

Ich habs nun so:

Code: Alles auswählen

    text = BeautifulSoup(text)
    empty_tags = text.find_all(lambda tag: tag.name == 'b' and tag.find(True) is None and (tag.string is None or tag.string.strip()=="")) 
    [empty_tag.extract() for empty_tag in empty_tags]

    output.write(text.decode("utf-8"))
Was mich noch wunder nimmt: Jetzt wird die Ausgabe so gespeichert, dass sie "schön" zu lesen ist, d.h. mit Einzügen, sodass verschiedene Tags leicht auseinander gehalten werden können. Gibt es eine Möglichkeit, die Ausgabe ohne diese Einzüge zu speichern?
BlackJack

@MarcelF6: Wieso solltest Du ohne ``txt = str(txt)`` die `splitlines()`-Methode denn *nicht* anwenden können? Und warum wendest Du die überhaupt an? Das ist völlig unnötig die Zeichenkette an Zeilenenden zu zerlegen, nur um sie nach dem ersetzen in den einzelnen Zeilen wieder mit Zeilenenden zusammen zu setzen.

Deine Lösung bindet jetzt den Namen `text` mal an Zeichenketten und mal an einen Objektbaum. Das ist verwirrend. Schon für erfahrene Programmierer, aber speziell auch für Dich, wo Du anscheinend sowieso schon unsicher bist was die Typen von Werten angeht.

Die falsche Verwendung von einer „list comprehension” hast Du immer noch.

Das Du Kodierungen nicht verstehst, sieht man am Quelltext. Du machst das unnötig umständlich und verwirrend. Du willst anscheinend UTF-8 als Zielkodierung. `str()` auf die `bs4`-Objekte ergibt UTF-8 kodierte Zeichenketten. Also genau das was Du haben willst. Aber Du dekodierst die mit UTF-8 in `unicode`-Objekte um sie einem `codecs`-Dateiobjekt zu übergeben, was die `unicode`-Objekte gleich wieder mit UTF-8 in Bytes umwandelt. Also dass was Du gerade vorher schon mal hattest. Der Schritt ist also völlig unnötig.

Genau wie Du Dir klar machen solltest welcher (Teil)ausdruck welchen Datentyp erzeugt, und damit welche Operationen möglich sind, solltest Du insbesondere bei `str` und `unicode` wissen welches davon vorliegt und bei `str` in welcher Kodierung.

Das die Ausgabe „schön” ist, liegt daran, dass die Eingabe schon „schön” ist. Wenn es kompakter sein soll, müsstest Du Textelemente die nur „whitespace” enthalten und direkt vor oder nach „block level”-Elementen (ausser <pre>) stehen, entfernen.
Antworten