En utilisant le module Beautiful Soup Python pour remplacer les étiquettes avec le texte brut

StackOverflow https://stackoverflow.com/questions/2061718

  •  20-09-2019
  •  | 
  •  

Question

J'utilise Belle soupe pour extraire le 'contenu' des pages Web. Je sais que certaines personnes ont posé cette question avant et ils ont tous souligné Beautiful Soup et c'est que j'ai commencé avec elle.

J'ai pu obtenir avec succès la plupart du contenu mais je suis en cours d'exécution dans certains défis avec des balises qui font partie du contenu. (Je commence avec une stratégie de base: s'il y a plus de X caractères dans un nœud alors il est contenu). Prenons le code html ci-dessous à titre d'exemple:

<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)

Lorsque j'utilise le code ci-dessus pour obtenir à long texte, il se casse (le texte identifié commencera à partir de "et je l'espère ..) les tags. J'ai donc essayé de remplacer l'étiquette avec le texte brut comme suit:

anchors = soup.findAll('a')

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

ne fonctionne pas au-dessus parce que Beautiful Soup insère la chaîne comme NavigableString et qui provoque le même problème lorsque j'utilise findAll avec le len (x)> 20. Je peux utiliser des expressions régulières pour analyser le code html sous forme de texte d'abord, effacer toutes les balises indésirables puis appeler Beautiful Soup. Mais je voudrais éviter le traitement du même contenu deux fois - je suis en train d'analyser ces pages afin que je puisse montrer un extrait de contenu pour un lien donné (très semblable à Facebook Share) - et si tout est fait avec Beautiful Soup, Je présume que ce sera plus rapide.

Ma question: est-il un moyen de « balises claires » et les remplacer par « texte brut » à l'aide soupe Belle. Dans le cas contraire, ce sera la meilleure façon de le faire?

Merci pour vos suggestions!

Mise à jour: Le code d'Alex a travaillé très bien pour l'exemple échantillon. J'ai aussi essayé divers cas de pointe et ils ont tous travaillé très bien (avec la modification ci-dessous). Alors je lui ai donné un coup de feu sur un site Web de la vie réelle et je rencontre des problèmes qui me casse-tête.

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

Quand je lance le code ci-dessus, je reçois l'erreur suivante:

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'

Quand je regarde le code HTML, « Restez à jour .. » n'a pas de membre de la fratrie (je ne l'ai pas comment frère précédent a travaillé jusqu'à ce que je le code d'Alex et selon mon expérience, il semble que cela est à la recherche de . « text » avant la balise) donc, s'il n'y a pas membre de la fratrie, je suis surpris que cela ne passe pas par la logique de si a.previousSibling est None et;. nextSibling est aucun

Pourriez-vous s'il vous plaît me faire savoir ce que je fais mal?

-ecognium

Était-ce utile?

La solution

Une approche qui fonctionne pour votre exemple spécifique est:

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

qui émet

$ 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']

Bien sûr, vous aurez probablement besoin de prendre un peu plus de soin, à savoir, s'il n'y a pas a.string, ou si a.previousSibling est None - vous aurez besoin des déclarations de if appropriées pour prendre soin de ces cas d'angle. Mais j'espère que cette idée générale peut vous aider. (En fait, vous voudrez peut-être aussi fusionner les suivant frères et soeurs si elle est une chaîne - ne sais pas comment cela joue avec votre heuristiques len(x) > 20, mais disons par exemple que vous avez deux chaînes 9 caractères avec une <a> contenant une des chaînes de 5 caractères au milieu, peut-être vous voudriez ramasser le lot comme? Je ne peux pas « string 23 caractères » dire parce que je ne comprends pas la motivation votre heuristique).

J'imagine qu'en plus des balises <a> vous aussi souhaitez supprimer d'autres, comme <b> ou <strong>, peut-être <p> et / ou <br>, etc ...? Je suppose que cela aussi dépend de ce que l'idée réelle derrière votre heuristiques est!

Autres conseils

Quand j'ai essayé de Aplatir dans le document, de cette façon, tout le contenu de balises seraient arrivés à bord de son nœud parent en place (je voulais réduire le contenu d'un p avec tous les sous-paragraphes, listes, div et durée , etc. à l'intérieur, mais se débarrasser du style et < strong> police et quelques restes horribles de générateur de mot à html), je l'ai trouvé assez compliqué à faire avec BeautifulSoup lui-même depuis extract () supprime également le contenu et replaceWith () ne unfortunatetly acceptent pas Aucun comme argument. Après quelques expériences de récursion sauvages, je me suis finalement décidé d'utiliser des expressions régulières avant ou après le traitement du document avec BeautifulSoup avec la méthode suivante:

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

balises L'argument est soit une seule étiquette ou une liste des balises à aplaties.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top