Wie unterschiedlich formatierten Linktext parsen?

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
Benutzeravatar
snafu
User
Beiträge: 5466
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Donnerstag 21. Februar 2008, 18:06

Erstmal hallo zusammen...!

Ich beschäftige mich heute erst den zweiten Tag mit Python (habe vorher etwas hobbymäßig mit Bash gearbeitet) und plane aktuell, die Threadtitel eines Forums auszulesen, bei dem ich angemeldet bin. Die Threads sind jeweils einem eindeutigen Tagindex zugeordnet, so dass der relevante Bereich relativ einfach zu parsen war. Nun stellt sich mir folgendes Problem: Wenn seit dem letzten Login etwas neues in einen Thread gestellt wurde, wird dieser fett angezeigt, ansonsten nicht. Ich zeige das mal an einem Beispiel zur Verdeutlichung:

Code: Alles auswählen

<a href="f.cfm?id=2986439&r=threadview&a=1&t=3272523" tabindex="21">
<b>nach langer zeit neu hier</b>
</a>
Ich weiß zwar, dass sich der Titel mittels BS mit soup.find('a', tabindex='21').b.string auslesen ließe, aber ich möchte ja alle Titel auf einmal haben. Daher habe ich findAll genommen, erhalte aber als Fehlermeldung: "AttributeError: 'ResultSet' object has no attribute 'b'".

Meine Fragen sind also:

1. Wie kann ich trotzdem b.string auslesen?
2. Wie baue ich den Fall ein, wo der Text nicht fett ist? Bietet sich da eher if, while oder for an?

Hier mal das gesamte Script wie es aktuell ist, vielleicht hat ja jemand ne gute Idee:

Code: Alles auswählen

import cookielib, urllib2, re
from BeautifulSoup import BeautifulSoup

# Cookies zur Verwendung vorbereiten
cookie_jar = cookielib.MozillaCookieJar()
cookie_jar.load("/home/sebastian/.mozilla/firefox/3n1jwnxz.default/cookies.txt")
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie_jar))

# Inhalt holen und soupen
seite1 = opener.open("http://www.link-zum-forum.de/f.cfm?id=2986439")
inhalt = seite1.read()
seite1.close()
soup = BeautifulSoup(inhalt)

def threads_holen():
	thread_nummer = re.compile('\d')
	thread_code = soup.findAll('a', tabindex=thread_nummer)
	return thread_code

for threads in threads_holen():
	print threads
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Sonntag 24. Februar 2008, 11:02

Ohne BeautifulSoup zu kennen, würde ich doch stark vermuten, dass ein ResultSet iterierbar ist und du mit einer for-Schleife ans Ziel kommst:

Code: Alles auswählen

for e in soup.findAll('a', tabindex='21'):
  print e.b.string
Unter der selben Annahme kannst du dann die Liste mittels List Compherension erstellen:

Code: Alles auswählen

alles = [e.b.string for e in soup.findAll('a', tabindex='21')]
Stefan
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Sonntag 24. Februar 2008, 11:22

Hallo snafu, willkommen im Forum,

Ich habe mal den Thread verschoben, in Codesnippets hatte der ja nicht wirklich was zu suchen.

Weiterhin stelle ich hier noch die Frage ob "\d" genau das ist was gewünscht ist. Ich würde eher schätzen, dass das ein r"\d+?" sein sollte.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
BlackJack

Sonntag 24. Februar 2008, 11:59

@snafu: Ich würde das `b` ignorieren und einfach alle Texte aus dem `a`-Tag extrahieren. Das hier ``''.join(node.findAll(text=True))`` findet alle Texte unterhalb von `node` und entfernt die Tags, die darin enthalten sind.
Benutzeravatar
snafu
User
Beiträge: 5466
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Mittwoch 27. Februar 2008, 22:06

BlackJack hat geschrieben:@snafu: Ich würde das `b` ignorieren und einfach alle Texte aus dem `a`-Tag extrahieren. Das hier ``''.join(node.findAll(text=True))`` findet alle Texte unterhalb von `node` und entfernt die Tags, die darin enthalten sind.
Nun ja, also XML ist ebenfalls totales Neuland für mich. Ich weiß jetzt aber was ein Knoten und was ein Baum ist. Meine Frage: Sollte ich bei 25 Threads dementsprechend 25 Knoten erstellen (bzw darauf verweisen oder wie auch immer man das ausdrückt) oder was würde sich hier anbieten? Die eindeutige Kennzeichnung ist m.E. wie gesagt das Tabindex-Attribut des a-Elements.

Mit welchem Befehl lege ich eigentlich einen genauen Knoten zur weiteren Verwendung fest?
Benutzeravatar
snafu
User
Beiträge: 5466
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Mittwoch 27. Februar 2008, 23:23

sma hat geschrieben:Ohne BeautifulSoup zu kennen, würde ich doch stark vermuten, dass ein ResultSet iterierbar ist und du mit einer for-Schleife ans Ziel kommst:

Code: Alles auswählen

for e in soup.findAll('a', tabindex='21'):
  print e.b.string
Unter der selben Annahme kannst du dann die Liste mittels List Compherension erstellen:

Code: Alles auswählen

alles = [e.b.string for e in soup.findAll('a', tabindex='21')]
Stefan
Danke für den Tipp. Das funktioniert schon sehr gut. Ich erhalte nun zuerst alle fetten Threads und wenn der nächste nicht-fett ist, dann kommt eine Fehlermeldung (AttributeError: 'NoneType' object has no attribute 'string'). Hättest du dafür auch eine Idee? Der Code jetzt:

Code: Alles auswählen

import cookielib, urllib2, re
from BeautifulSoup import BeautifulSoup as bs

# Cookies zur Verwendung vorbereiten
cookie_jar = cookielib.MozillaCookieJar()
cookie_jar.load("/home/sebastian/.mozilla/firefox/3n1jwnxz.default/cookies.txt")
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie_jar))

# Seite 1 auslesen und parsen
f = opener.open("http://www.url.de")
content = f.read()
f.close()
soup = bs(content)

# Threads anzeigen
thread_nummer = re.compile('\d')
for e in soup.findAll('a', tabindex=thread_nummer):
	print e.b.string
Und in dieser Version lasse ich ihn zwar bei einem Fehler nach nicht-fetten Threads suchen, allerdings gibt er mir dann die fetten als None aus, was natürlich auch nicht wirklich gewünscht ist (ich möchte ja die letzten Threads quasi als Ergänzung zu den fetten Threads angezeigt bekommen) :

Code: Alles auswählen

#Threads anzeigen
thread_nummer = re.compile('\d')
try:
	for e in soup.findAll('a', tabindex=thread_nummer):
		print e.b.string

except AttributeError:
	for e in soup.findAll('a', tabindex=thread_nummer):
		print e.string
Zuletzt geändert von snafu am Mittwoch 27. Februar 2008, 23:42, insgesamt 7-mal geändert.
Benutzeravatar
snafu
User
Beiträge: 5466
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Mittwoch 27. Februar 2008, 23:26

Leonidas hat geschrieben:Weiterhin stelle ich hier noch die Frage ob "\d" genau das ist was gewünscht ist. Ich würde eher schätzen, dass das ein r"\d+?" sein sollte.
Weiß ich ehrlich gesagt nicht so genau. Was wäre denn da der Unterschied? Ich möchte einfach, dass eine Zahl vorkommen soll.
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Donnerstag 28. Februar 2008, 00:40

snafu hat geschrieben:
Leonidas hat geschrieben:Weiterhin stelle ich hier noch die Frage ob "\d" genau das ist was gewünscht ist. Ich würde eher schätzen, dass das ein r"\d+?" sein sollte.
Weiß ich ehrlich gesagt nicht so genau. Was wäre denn da der Unterschied? Ich möchte einfach, dass eine Zahl vorkommen soll.
\d heißt genau eine. \d+? bedeutet mindestens eine Zahl, aber durchaus auch mehr.

Ich überlege gerade ob das ? (für non-greedy) in diesem Kontext einen Unterschied macht, aber wie es scheint ist in diesem Fall \d+ und \d+? äquivalent.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Benutzeravatar
snafu
User
Beiträge: 5466
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Donnerstag 28. Februar 2008, 06:32

Leonidas hat geschrieben:\d heißt genau eine. \d+? bedeutet mindestens eine Zahl, aber durchaus auch mehr.

Ich überlege gerade ob das ? (für non-greedy) in diesem Kontext einen Unterschied macht, aber wie es scheint ist in diesem Fall \d+ und \d+? äquivalent.
Hm, für spätere Zwecke möglicherweise interessant, falls ich die Tabindex-Nummer verwende, um bestimmte Threads direkt aufzurufen, indem ich die Zahl als Argument übergebe. Momentan funktioniert es allerdings auch mittels \d bis zum 25ten - und damit letzten - Thread. Werde ich mal im Hinterkopf behalten, falls ich später Probleme kriege.
Benutzeravatar
snafu
User
Beiträge: 5466
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Donnerstag 28. Februar 2008, 18:03

Hat sich erledigt. Ich habe meine Idee verworfen. Kann ja nicht sein, dass ich schon fast ne Woche daran hänge, so ein paar Threads auszulesen. Da geht irgendwann der Spass verloren. Trotzdem danke für eure Tipps, vielleicht bin ich ja irgendwann geübter und wage mich nochmal an dieses Projekt. ;)
Antworten