Seite 1 von 1

Wie unterschiedlich formatierten Linktext parsen?

Verfasst: Donnerstag 21. Februar 2008, 18:06
von snafu
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

Verfasst: Sonntag 24. Februar 2008, 11:02
von sma
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

Verfasst: Sonntag 24. Februar 2008, 11:22
von Leonidas
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.

Verfasst: Sonntag 24. Februar 2008, 11:59
von BlackJack
@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.

Verfasst: Mittwoch 27. Februar 2008, 22:06
von snafu
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?

Verfasst: Mittwoch 27. Februar 2008, 23:23
von snafu
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

Verfasst: Mittwoch 27. Februar 2008, 23:26
von snafu
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.

Verfasst: Donnerstag 28. Februar 2008, 00:40
von Leonidas
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.

Verfasst: Donnerstag 28. Februar 2008, 06:32
von snafu
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.

Verfasst: Donnerstag 28. Februar 2008, 18:03
von snafu
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. ;)