J'essaie d'extraire "THIS IS MON TEXTE") du code HTML suivant:
<html>
<body>
<table>
<td class="MYCLASS">
<!-- a comment -->
<a hef="xy">Text</a>
<p>something</p>
THIS IS MY TEXT
<p>something else</p>
</br>
</td>
</table>
</body>
</html>
J'ai essayé de cette façon:
soup = BeautifulSoup(html)
for hit in soup.findAll(attrs={'class' : 'MYCLASS'}):
print hit.text
Mais je reçois tout le texte entre toutes les balises imbriquées et le commentaire.
Quelqu'un peut-il m'aider à obtenir simplement "THIS IS MON TEXTE") "?
En savoir plus sur la navigation à travers l’arborescence d’analyse dans BeautifulSoup
. L’arbre d’analyse possède tags
et NavigableStrings
(comme THIS IS A TEXT). Un exemple
from BeautifulSoup import BeautifulSoup
doc = ['<html><head><title>Page title</title></head>',
'<body><p id="firstpara" align="center">This is paragraph <b>one</b>.',
'<p id="secondpara" align="blah">This is paragraph <b>two</b>.',
'</html>']
soup = BeautifulSoup(''.join(doc))
print soup.prettify()
# <html>
# <head>
# <title>
# Page title
# </title>
# </head>
# <body>
# <p id="firstpara" align="center">
# This is paragraph
# <b>
# one
# </b>
# .
# </p>
# <p id="secondpara" align="blah">
# This is paragraph
# <b>
# two
# </b>
# .
# </p>
# </body>
# </html>
Pour vous déplacer dans l'arbre d'analyse, vous avez contents
et string
.
contents est une liste ordonnée des objets Tag et NavigableString contenus dans un élément de page
si une balise n'a qu'un seul nœud enfant et que ce nœud est une chaîne, le nœud enfant est rendu disponible sous la forme tag.string, ainsi que tag.contents [0]
Pour ce qui précède, c'est-à-dire que vous pouvez obtenir
soup.b.string
# u'one'
soup.b.contents[0]
# u'one'
Pour plusieurs nœuds enfants, vous pouvez par exemple avoir
pTag = soup.p
pTag.contents
# [u'This is paragraph ', <b>one</b>, u'.']
donc ici vous pouvez jouer avec contents
et obtenir le contenu à l’index souhaité.
Vous pouvez également parcourir une balise, il s’agit d’un raccourci. Par exemple,
for i in soup.body:
print i
# <p id="firstpara" align="center">This is paragraph <b>one</b>.</p>
# <p id="secondpara" align="blah">This is paragraph <b>two</b>.</p>
Vous pouvez utiliser .contents
:
>>> for hit in soup.findAll(attrs={'class' : 'MYCLASS'}):
... print hit.contents[6].strip()
...
THIS IS MY TEXT
Utilisation .children
à la place:
from bs4 import NavigableString, Comment
print ''.join(unicode(child) for child in hit.children
if isinstance(child, NavigableString) and not isinstance(child, Comment))
Oui, c'est un peu une danse.
Sortie:
>>> for hit in soup.findAll(attrs={'class' : 'MYCLASS'}):
... print ''.join(unicode(child) for child in hit.children
... if isinstance(child, NavigableString) and not isinstance(child, Comment))
...
THIS IS MY TEXT
avec votre propre objet de soupe:
soup.p.next_sibling.strip()
soup.p
* (cela dépend du fait qu'il soit le premier <p> de l'arbre d'analyse)next_sibling
sur l’objet de balise que soup.p
renvoie car le texte souhaité est imbriqué au même niveau de l’arbre d’analyse syntaxique que le <p>.strip()
est juste une méthode Python str pour supprimer les espaces en début et en fin de chaîne* sinon juste trouver l'élément en utilisant votre choix de filtre (s)
dans l'interprète, cela ressemble à quelque chose comme:
In [4]: soup.p
Out[4]: <p>something</p>
In [5]: type(soup.p)
Out[5]: bs4.element.Tag
In [6]: soup.p.next_sibling
Out[6]: u'\n THIS IS MY TEXT\n '
In [7]: type(soup.p.next_sibling)
Out[7]: bs4.element.NavigableString
In [8]: soup.p.next_sibling.strip()
Out[8]: u'THIS IS MY TEXT'
In [9]: type(soup.p.next_sibling.strip())
Out[9]: unicode
Réponse courte: soup.findAll('p')[0].next
Réponse réelle: Vous avez besoin d'un point de référence invariant à partir duquel vous pouvez atteindre votre cible.
Vous avez mentionné dans votre commentaire à la réponse de Haidro que le texte que vous souhaitez ne se trouve pas toujours au même endroit. Trouvez un sens dans lequel il se trouve au même endroit par rapport à un élément. Trouvez ensuite comment faire fonctionner BeautifulSoup dans l’arborescence d’analyse suivant ce chemin invariant.
Par exemple, dans le code HTML que vous fournissez dans la publication d'origine, la chaîne cible apparaît immédiatement après le premier élément de paragraphe et ce paragraphe n'est pas vide. Puisque findAll('p')
trouvera des éléments de paragraphe, soup.find('p')[0]
sera le premier élément de paragraphe.
Vous pouvez dans ce cas utiliser soup.find('p')
mais soup.findAll('p')[n]
est plus général puisque votre scénario actuel nécessite peut-être le cinquième paragraphe ou quelque chose du genre.
L'attribut de champ next
sera le prochain élément analysé dans l'arborescence, y compris les enfants. Donc, soup.findAll('p')[0].next
contient le texte du paragraphe et soup.findAll('p')[0].next.next
renverra votre cible dans le code HTML fourni.
soup = BeautifulSoup(html)
for hit in soup.findAll(attrs={'class' : 'MYCLASS'}):
hit = hit.text.strip()
print hit
Ceci imprimera: CECI IS MON TEXTE Essayez ceci ..
La documentation BeautifulSoup fournit un exemple sur la suppression d'objets d'un document à l'aide de la méthode d'extraction. Dans l'exemple suivant, l'objectif est de supprimer tous les commentaires du document:
Suppression d'éléments
Une fois que vous avez une référence à un élément, vous pouvez l'extraire de l'arbre avec la méthode extract. Ce code supprime tous les commentaires d'un document:
from BeautifulSoup import BeautifulSoup, Comment
soup = BeautifulSoup("""1<!--The loneliest number-->
<a>2<!--Can be as bad as one--><b>3""")
comments = soup.findAll(text=lambda text:isinstance(text, Comment))
[comment.extract() for comment in comments]
print soup
# 1
# <a>2<b>3</b></a>