Actuellement, j'ai du code qui fait quelque chose comme ceci:
soup = BeautifulSoup(value)
for tag in soup.findAll(True):
if tag.name not in VALID_TAGS:
tag.extract()
soup.renderContents()
Sauf que je ne veux pas jeter le contenu à l'intérieur de la balise invalide. Comment puis-je me débarrasser de la balise mais garder le contenu à l'intérieur lors de l'appel de soup.renderContents ()?
La stratégie que j'ai utilisée est de remplacer une balise par son contenu si elle est de type NavigableString
et si ce n'est pas le cas, puis de la recréer et de remplacer son contenu par NavigableString
, etc. Essayez ceci :
from BeautifulSoup import BeautifulSoup, NavigableString
def strip_tags(html, invalid_tags):
soup = BeautifulSoup(html)
for tag in soup.findAll(True):
if tag.name in invalid_tags:
s = ""
for c in tag.contents:
if not isinstance(c, NavigableString):
c = strip_tags(unicode(c), invalid_tags)
s += unicode(c)
tag.replaceWith(s)
return soup
html = "<p>Good, <b>bad</b>, and <i>ug<b>l</b><u>y</u></i></p>"
invalid_tags = ['b', 'i', 'u']
print strip_tags(html, invalid_tags)
Le résultat est:
<p>Good, bad, and ugly</p>
J'ai donné cette même réponse sur une autre question. Cela semble beaucoup.
Les versions actuelles de la bibliothèque BeautifulSoup ont une méthode non documentée sur les objets Tag appelée replaceWithChildren (). Vous pouvez donc faire quelque chose comme ceci:
html = "<p>Good, <b>bad</b>, and <i>ug<b>l</b><u>y</u></i></p>"
invalid_tags = ['b', 'i', 'u']
soup = BeautifulSoup(html)
for tag in invalid_tags:
for match in soup.findAll(tag):
match.replaceWithChildren()
print soup
On dirait qu'il se comporte comme vous le souhaitez et qu'il s'agit d'un code assez simple (bien qu'il fasse quelques passages dans le DOM, mais cela pourrait facilement être optimisé.)
Bien que cela ait déjà été mentionné par d'autres personnes dans les commentaires, j'ai pensé publier une réponse complète montrant comment le faire avec Mozilla's Bleach. Personnellement, je pense que c'est beaucoup plus agréable que d'utiliser BeautifulSoup pour cela.
import bleach
html = "<b>Bad</b> <strong>Ugly</strong> <script>Evil()</script>"
clean = bleach.clean(html, tags=[], strip=True)
print clean # Should print: "Bad Ugly Evil()"
J'ai une solution plus simple mais je ne sais pas s'il y a un inconvénient.
MISE À JOUR: il y a un inconvénient, voir le commentaire de Jesse Dhillon. De plus, une autre solution sera d'utiliser Bleach de Mozilla au lieu de BeautifulSoup.
from BeautifulSoup import BeautifulSoup
VALID_TAGS = ['div', 'p']
value = '<div><p>Hello <b>there</b> my friend!</p></div>'
soup = BeautifulSoup(value)
for tag in soup.findAll(True):
if tag.name not in VALID_TAGS:
tag.replaceWith(tag.renderContents())
print soup.renderContents()
Cela imprimera également <div><p>Hello there my friend!</p></div>
comme voulu.
vous pouvez utiliser soup.text
.text supprime toutes les balises et concatène tout le texte.
Vous devrez probablement déplacer les enfants du tag pour qu'ils soient les enfants du parent du tag avant de supprimer le tag - c'est ce que vous voulez dire?
Si c'est le cas, alors, alors que l'insertion du contenu au bon endroit est délicate, quelque chose comme ça devrait fonctionner:
from BeautifulSoup import BeautifulSoup
VALID_TAGS = 'div', 'p'
value = '<div><p>Hello <b>there</b> my friend!</p></div>'
soup = BeautifulSoup(value)
for tag in soup.findAll(True):
if tag.name not in VALID_TAGS:
for i, x in enumerate(tag.parent.contents):
if x == tag: break
else:
print "Can't find", tag, "in", tag.parent
continue
for r in reversed(tag.contents):
tag.parent.insert(i, r)
tag.extract()
print soup.renderContents()
avec l'exemple de valeur, cela imprime <div><p>Hello there my friend!</p></div>
comme voulu.
Utilisez déballer.
Déballer supprimera une occurrence multiple de la balise et conservera toujours le contenu.
Exemple:
>> soup = BeautifulSoup('Hi. This is a <nobr> nobr </nobr>')
>> soup
<html><body><p>Hi. This is a <nobr> nobr </nobr></p></body></html>
>> soup.nobr.unwrap
<nobr></nobr>
>> soup
>> <html><body><p>Hi. This is a nobr </p></body></html>
Aucune des réponses proposées ne semblait fonctionner avec BeautifulSoup pour moi. Voici une version qui fonctionne avec BeautifulSoup 3.2.1, et insère également un espace lors de la jonction de contenu à partir de différentes balises au lieu de concaténer des mots.
def strip_tags(html, whitelist=[]):
"""
Strip all HTML tags except for a list of whitelisted tags.
"""
soup = BeautifulSoup(html)
for tag in soup.findAll(True):
if tag.name not in whitelist:
tag.append(' ')
tag.replaceWithChildren()
result = unicode(soup)
# Clean up any repeated spaces and spaces like this: '<a>test </a> '
result = re.sub(' +', ' ', result)
result = re.sub(r' (<[^>]*> )', r'\1', result)
return result.strip()
Exemple:
strip_tags('<h2><a><span>test</span></a> testing</h2><p>again</p>', ['a'])
# result: u'<a>test</a> testing again'
Voici la meilleure solution sans tracas et code passe-partout pour filtrer les balises en gardant le contenu. Disons que vous souhaitez supprimer toutes les balises enfants dans la balise parent et que vous souhaitez simplement conserver le contenu/texte, vous pouvez simplement le faire:
for p_tags in div_tags.find_all("p"):
print(p_tags.get_text())
C'est tout et vous pouvez être libre avec toutes les balises br ou i b dans les balises parent et obtenir le texte clair.
Voici une python 3 version conviviale de cette fonction:
from bs4 import BeautifulSoup, NavigableString
invalidTags = ['br','b','font']
def stripTags(html, invalid_tags):
soup = BeautifulSoup(html, "lxml")
for tag in soup.findAll(True):
if tag.name in invalid_tags:
s = ""
for c in tag.contents:
if not isinstance(c, NavigableString):
c = stripTags(str(c), invalid_tags)
s += str(c)
tag.replaceWith(s)
return soup
C'est une vieille question, mais juste pour dire de meilleures façons de le faire. Tout d'abord, BeautifulSoup 3 * n'est plus en cours de développement, vous devriez donc plutôt utiliser BeautifulSoup 4 *, appelé bs4 .
De plus, lxml a juste la fonction dont vous avez besoin: classe Cleaner a l'attribut remove_tags
, que vous pouvez définir pour les balises qui seront supprimées lors de l'extraction de leur contenu dans la balise parent.