BeautifulSoup.tag in String casten

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
pan
User
Beiträge: 9
Registriert: Freitag 11. März 2011, 15:29

Hallo zusammen,
ich bin brandneu in der Pythonwelt.
Zum crawlen einer Webseite benutzt ich das Modul BeautifulSoup.
Da die Webseite relativ unsauber ist, kann ich nicht direkt auf den gewünschen Inhalt verweisen, sondern muss mir erstmal ein etwas größeres "Stück" holen, und muss dann auf diesem nochmal eine Abfrage machen:

Code: Alles auswählen

url = "index.htm"
html = open(url).read()
a_tag = SoupStrainer('a')
span_tag = SoupStrainer('span')
html_atag = BeautifulSoup(html, parseOnlyThese=a_tag)
title_py = html_atag.findAll(attrs={'href' : re.compile("link_to_destination")})


for line in title_py:
	print line
line wäre in diesem Fall das größere Stück. nun würde ich hingehen und die for-Schleife nochmal verschachteln und eine 2. Abfrage auf "line" ausführen. Leider ist line vom Typ BeautifulSoup.tag und kann nicht in ein String gecastet werden, den ich aber brauche.

Könnte mir da jemand helfen? Oder hab ich einen völlig falschen Ansatz?
Auf der Seiten werden sehr oft sehr generische class-Namen verwendet, sodass ich erstmal auf diesem Umweg an die Daten komme..

Vielen Dank



//Edit:
ich erweitere mal meine Frage:
Wenn ich jetzt mehrere Infos holen will, kann ich das dann auch irgendwie so bauen, dass ich in einer for-Schleife alles gleichzeitig crawle? mein Ansatz wäre ja irgendwie, dass ich für jede Info die o.g. Prozedur wiederhole, das macht ja aber nur bedingt Sinn...
deets

Es wuerde helfen, wenn du die Daten, um die es dir geht, zeigst, und was du da genau draus extrahieren willst.

Und laut Doku sollten wahlweise str(tag) oder tag.prettyify() helfen, ausprobiert habe ich das aber nicht.
BlackJack

@pan: Das ist nicht leider vom Typ `Tag` sondern $GOTT sei dank, weil man deshalb darauf weitere Anfragen machen kann.

Theoretisch müsste man Informationen in einem Durchgang extrahieren können. Da kommt es darauf an wie die im Dokument organisiert sind. Man stellt halt an die Ergebnisse immer feinere Unteranfragen bis man bei den gewünschten Daten ist.

`findAll()` kann man übrigens durch den Aufruf des Objekts selbst ersetzen. Also `soup.findAll(...)` kann man auch als `soup(...)` schreiben.

Wenn der Name eines Attributs kein Python-Schlüsselwort ist oder mit einem Schlüsselwort-Argument kollidiert, dann kann man es auch als Schlüsselwort angeben und muss kein Dictionary für das `attrs`-Argument erstellen. Also statt `soup(attrs={'href': something})` kann man auch `soup(href=something)` schreiben.
pan
User
Beiträge: 9
Registriert: Freitag 11. März 2011, 15:29

Hi,

@ deets: danke.
Gegeben sei zB dieses Forum. Meine Ausgabe sollte optimalerweise so ausehen:
---
pan
BeautifulSoup.tag in String casten
TEXT
---
deets
Re: BeautifulSoup.tag in String casten
TEXT
---
BlackJack
Re: BeautifulSoup.tag in String casten
TEXT

@ BlackJack: danke für die Zusatzinfos. Ich hab allerdings noch nicht ganz den Zusammenhang gerafft.. sorry :)


ciao
deets

Also so richtig hilft das nicht weiter, weil zu unkonkret. Ich bin aber mit Marc einer Meinung: du solltest weiterhin mit dem Tag-Objekt arbeiten, und die die unter-Bereiche mittels der angebotenen Methoden "rausfischen".
pan
User
Beiträge: 9
Registriert: Freitag 11. März 2011, 15:29

ok.
konkret möchte ich diese Seite in diesem Format:
Author: el-lutschi
Betreff: Tarifwechsel Data L
Datum: 10.03.2011 23:13
Text: Hallo,mich würde interessieren ob es möglich ist, vom Data L (21,25€/Monat) in den kleineren Datentarif für 15€/Monat zu wechseln. Danke und Gruß, el-lutschi
---
Author:marcono
Betreff:Tarifwechsel Data L
Datum:11.03.2011 16:15
Text:Hallo,vielleicht rufst du einfach mal bei der kostenlosen Kundenbetreuung an, vielleicht können die dir ja weiterhelfen aber ich bin schon der Meinung das du von dem einen in einen anderen Tarif wechseln kannst könnte vielleicht nur aufpreis kosten, aber machbar ist das schon. mfg
ich habe hier im Forum auch schon was ähnliches gefunden, allerdings sind in der o.g. Seite die Strukturen etwas kompliziert / generisch, sodass ich da m.e. nicht so einfach ran komme.

Bin für jeden Tipp dankbar!

Schönes WE!
BlackJack

@pan: Wenn deets meiner Meinung ist, dann schliesse ich mich auch mal seiner an: Es kommt halt immer auf die konkrete Struktur der Eingabedaten an. Wenn es nicht gerade darum geht ein phpBB-Forum zu parsen, ist bei einer Lösung für dieses Problem vielleicht nicht all zu viel dabei, was sich auf Deines übertragen lässt.

Trotzdem ein kleines Beispiel für das Forum hier. Es ist IMHO nicht besonders gut zu verarbeiten. Die beiden markantesten Punkte um einen Beitrag irgend wie "fest zu machen" sind wohl die Klassen 'postauthor' und 'postbody'. Ich suche nach dem 'postauthor' und suche dann von dem Tag ausgehend die Tags der anderen Informationen, die zum selben Beitrag gehören:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from BeautifulSoup import BeautifulSoup


def main():
    with open('test.html') as html_file:
        soup = BeautifulSoup(html_file.read())
    
    for b_tag in soup('b', 'postauthor'):
        author = b_tag.contents[0]
        subject = b_tag.findParent('tr').div.b.nextSibling.strip()
        post = b_tag.findParent('table').find('div', 'postbody')(text=True)
        print '---'
        print author
        print subject
        print ' '.join(post)[:30], '...'


if __name__ == '__main__':
    main()
Was an dem Beispiel vielleicht interessant ist: Man sollte nicht vergessen, dass man von einem Tag ausgehend auch in Richtung Wurzel suchen kann, und dass man die Suche auch auf Geschwisterknoten (in beide Richtungen) beschränken kann.
BlackJack

@pan: Bei der URL bekomme ich eine Fehlerseite mit der Meldung: `Die URL enthält ungültige Parameter.`
BlackJack

Diese Forumseinträge sind doch nur so zugekleistert mit HTML-Klassenattributen -- schöner kann man sich die Daten zum "scrapen" doch gar nicht wünschen.

Code: Alles auswählen

from BeautifulSoup import BeautifulSoup


def main():
    with open('test2.html') as html_file:
        soup = BeautifulSoup(
            html_file.read(),
            convertEntities=BeautifulSoup.HTML_ENTITIES
        )
    
    def make_tag_and_class_check(tag_name, class_name):
        def is_tag_and_class(tag):
            return (
                tag.name == tag_name
                    and class_name in tag.get('class', '').split()
            )
        return is_tag_and_class
    
    is_forum_message_tag = make_tag_and_class_check(
        'div',
        'lia-quilt-forum-message'
    )
    is_user_name_tag = make_tag_and_class_check('span', 'UserName')
    is_subject_tag = make_tag_and_class_check('div', 'lia-message-subject')
    is_message_body_tag = make_tag_and_class_check(
        'div',
        'lia-message-body-content'
    )
    for forum_message_tag in soup(is_forum_message_tag):
        user_name = forum_message_tag.find(is_user_name_tag).span.string
        subject = ''.join(forum_message_tag.find(is_subject_tag).h1(text=True))
        content = '\n'.join(
            ' '.join(p(text=True))
            for p in forum_message_tag.find(is_message_body_tag)('p')
        )
        print '---'
        print user_name
        print subject
        print content


if __name__ == '__main__':
    main()
pan
User
Beiträge: 9
Registriert: Freitag 11. März 2011, 15:29

Besten Dank!
Meine Frage zielte zwar keineswegs auf die fertige Lösung ab, aber so kann ich mich an was Funktionierendem orientieren, wenn es weiter geht ;)



Schönen Tag
pan
User
Beiträge: 9
Registriert: Freitag 11. März 2011, 15:29

Hallo,

ich muss leider nochmal stören :(
In einigen Dokumenten (zB), sind im Titel neue HTML5 Tags verbaut (<wbr></wbr>). Diese erzeugen nach dem crawlen Leerzeichen im Titel.

Wie kann ich diese entfernen? Ich habs schon mit replace und diversen anderen Methoden (die ich per google gefunden habe) probiert. Leider ohne Erfolg :(

Vielen Dank
Antworten