Domanda

Beautiful Soup per estrarre 'contenuto' dalle pagine web. So che alcune persone hanno chiesto questa domanda prima e furono tutti indicavano Beautiful Soup ed è così che ho iniziato con esso.

Sono stato in grado di ottenere con successo la maggior parte del contenuto, ma io sono in esecuzione in alcuni problemi con i tag che fanno parte del contenuto. (Sto iniziando con una strategia di base di: se ci sono più di x-caratteri in un nodo, allora è il contenuto). Prendiamo il codice html indicato qui come esempio:

<div id="abc">
    some long text goes <a href="/"> here </a> and hopefully it 
    will get picked up by the parser as content
</div>

results = soup.findAll(text=lambda(x): len(x) > 20)

Quando uso il codice di cui sopra per ottenere il testo lungo, si rompe (il testo identificato partirà da 'e, si spera ..') ai tag. Così ho provato a sostituire il tag con testo in chiaro nel seguente modo:

anchors = soup.findAll('a')

for a in anchors:
  a.replaceWith('plain text')

È possibile che questo non funziona perché Beautiful Soup inserisce la stringa come un NavigableString e che causa lo stesso problema quando uso findAll con la len (x)> 20. Posso usare le espressioni regolari per analizzare il codice HTML come testo normale prima, cancellare tutti i tag indesiderati e quindi chiamare Beautiful Soup. Ma vorrei evitare l'elaborazione del contenuto stesso due volte - sto cercando di analizzare queste pagine in modo da poter mostrare un frammento di contenuto per un determinato link (molto simile a Facebook Share) - e se tutto è fatto con Beautiful Soup, presumo che sarà più veloce.

Quindi la mia domanda: c'è un modo per 'tag' chiare e sostituirli con 'solo testo' utilizzando Beautiful Soup. In caso contrario, quale sarà il modo migliore per farlo?

Grazie per i vostri suggerimenti!

Aggiornamento: codice di Alex ha lavorato molto bene per l'esempio del campione. Ho anche provato vari casi limite e tutti hanno funzionato bene (con la modifica seguente). Così ho dato un colpo su un vero e proprio sito web la vita e mi imbatto in problemi che mi puzzle.

import urllib
from BeautifulSoup import BeautifulSoup

page = urllib.urlopen('http://www.engadget.com/2010/01/12/kingston-ssdnow-v-dips-to-30gb-size-lower-price/')

anchors = soup.findAll('a')
i = 0
for a in anchors:
    print str(i) + ":" + str(a)
    for a in anchors:
        if (a.string is None): a.string = ''
        if (a.previousSibling is None and a.nextSibling is None):
            a.previousSibling = a.string
        elif (a.previousSibling is None and a.nextSibling is not None):
            a.nextSibling.replaceWith(a.string + a.nextSibling)
        elif (a.previousSibling is not None and a.nextSibling is None):
            a.previousSibling.replaceWith(a.previousSibling + a.string)
        else:
            a.previousSibling.replaceWith(a.previousSibling + a.string + a.nextSibling)
            a.nextSibling.extract()
    i = i+1

Quando eseguo il codice di cui sopra, ottengo il seguente errore:

0:<a href="http://www.switched.com/category/ces-2010">Stay up to date with 
Switched's CES 2010 coverage</a>
Traceback (most recent call last):
  File "parselink.py", line 44, in <module>
  a.previousSibling.replaceWith(a.previousSibling + a.string + a.nextSibling)
 TypeError: unsupported operand type(s) for +: 'Tag' and 'NavigableString'

Quando guardo il codice HTML, 'Rimani aggiornato .." non ha fratello precedente (non ho come precedente di pari livello ha funzionato fino a quando ho visto il codice di Alex e in base alla mia test sembra che è alla ricerca di . 'testo' prima del tag) Quindi, se non v'è alcun fratello precedente, sono sorpreso che non sta attraversando il se la logica di a.previousSibling è None e un;. nextSibling è Nessuno

La prego di farmi sapere che cosa sto facendo male?

-ecognium

È stato utile?

Soluzione

Un approccio che funziona per il vostro esempio specifico è:

from BeautifulSoup import BeautifulSoup

ht = '''
<div id="abc">
    some long text goes <a href="/"> here </a> and hopefully it 
    will get picked up by the parser as content
</div>
'''
soup = BeautifulSoup(ht)

anchors = soup.findAll('a')
for a in anchors:
  a.previousSibling.replaceWith(a.previousSibling + a.string)

results = soup.findAll(text=lambda(x): len(x) > 20)

print results

che emette

$ python bs.py
[u'\n    some long text goes  here ', u' and hopefully it \n    will get picked up by the parser as content\n']

Naturalmente, avrete probabilmente bisogno di prendere un po 'più di cura, vale a dire, che cosa succede se non c'è a.string, o se a.previousSibling è None - avrete bisogno di idonee dichiarazioni if di prendersi cura di questi casi d'angolo. Ma spero che questa idea generale può aiutare. (In realtà si consiglia di anche unire il Avanti di pari livello se si tratta di una stringa - non so come che gioca con il tuo euristica len(x) > 20, ma diciamo per esempio di avere due stringhe di 9 caratteri con un <a> contenente una stringa di 5 caratteri in mezzo, forse che ci si vuole prendere il sacco come una "stringa di 23 caratteri"? non posso dire perché non capisco la motivazione per il vostro euristica).

Immagino che oltre tag <a> dovrete anche per rimuovere altri, come <b> o <strong>, forse <p> e / o <br>, ecc ...? Credo che anche questo, dipende da ciò che l'idea stessa dietro le euristiche è!

Altri suggerimenti

Quando ho provato a appiattire tag nel documento, in questo modo, tutto il contenuto dei tag sarebbe tirato fino al suo nodo padre in posto (ho voluto ridurre il contenuto di un p con tutti i sotto-paragrafi, elenchi, div e arco , ecc all'interno ma sbarazzarsi della style e < strong> tipo di carattere tag e qualche orribile parola-to-HTML resti generatore), ho trovato piuttosto complicato a che fare con BeautifulSoup stesso dal estratto () rimuove anche il contenuto e ReplaceWith () unfortunatetly non accetta Nessuno come argomento. Dopo alcuni esperimenti ricorsione selvatici, ho finalmente deciso di utilizzare le espressioni regolari, prima o dopo l'elaborazione del documento con BeautifulSoup con il seguente metodo:

import re
def flatten_tags(s, tags):
   pattern = re.compile(r"<(( )*|/?)(%s)(([^<>]*=\\\".*\\\")*|[^<>]*)/?>"%(isinstance(tags, basestring) and tags or "|".join(tags)))
   return pattern.sub("", s)

tag argomento è o un singolo tag o una lista di tag per essere appiattito.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top