requests_html requests library from code python html filter

library - requests python code



Limpiador/depurador/filtro HTML de Python (9)

¡Usa lxml.html.clean ! ¡Es muy fácil!

from lxml.html.clean import clean_html print clean_html(html)

Supongamos el siguiente html:

html = ''''''/ <html> <head> <script type="text/javascript" src="evil-site"></script> <link rel="alternate" type="text/rss" src="evil-rss"> <style> body {background-image: url(javascript:do_evil)}; div {color: expression(evil)}; </style> </head> <body onload="evil_function()"> <!-- I am interpreted for EVIL! --> <a href="javascript:evil_function()">a link</a> <a href="#" onclick="evil_function()">another link</a> <p onclick="evil_function()">a paragraph</p> <div style="display: none">secret EVIL!</div> <object> of EVIL! </object> <iframe src="evil-site"></iframe> <form action="evil-site"> Password: <input type="password" name="password"> </form> <blink>annoying EVIL!</blink> <a href="evil-site">spam spam SPAM!</a> <image src="evil!"> </body> </html>''''''

Los resultados...

<html> <body> <div> <style>/* deleted */</style> <a href="">a link</a> <a href="#">another link</a> <p>a paragraph</p> <div>secret EVIL!</div> of EVIL! Password: annoying EVIL! <a href="evil-site">spam spam SPAM!</a> <img src="evil!"> </div> </body> </html>

Puede personalizar los elementos que desea limpiar y otras cosas.

Estoy buscando un módulo que elimine las etiquetas HTML de una cadena que no se encuentran en una lista blanca.


Aquí hay una solución simple usando BeautifulSoup :

from bs4 import BeautifulSoup VALID_TAGS = [''strong'', ''em'', ''p'', ''ul'', ''li'', ''br''] def sanitize_html(value): soup = BeautifulSoup(value) for tag in soup.findAll(True): if tag.name not in VALID_TAGS: tag.hidden = True return soup.renderContents()

Si también desea eliminar el contenido de las etiquetas no válidas, sustituya tag.extract() por tag.hidden .

También podría considerar usar lxml y Tidy .


Esto es lo que uso en mi propio proyecto. Los elementos / atributos aceptables provienen de feedparser y BeautifulSoup hace el trabajo.

from BeautifulSoup import BeautifulSoup acceptable_elements = [''a'', ''abbr'', ''acronym'', ''address'', ''area'', ''b'', ''big'', ''blockquote'', ''br'', ''button'', ''caption'', ''center'', ''cite'', ''code'', ''col'', ''colgroup'', ''dd'', ''del'', ''dfn'', ''dir'', ''div'', ''dl'', ''dt'', ''em'', ''font'', ''h1'', ''h2'', ''h3'', ''h4'', ''h5'', ''h6'', ''hr'', ''i'', ''img'', ''ins'', ''kbd'', ''label'', ''legend'', ''li'', ''map'', ''menu'', ''ol'', ''p'', ''pre'', ''q'', ''s'', ''samp'', ''small'', ''span'', ''strike'', ''strong'', ''sub'', ''sup'', ''table'', ''tbody'', ''td'', ''tfoot'', ''th'', ''thead'', ''tr'', ''tt'', ''u'', ''ul'', ''var''] acceptable_attributes = [''abbr'', ''accept'', ''accept-charset'', ''accesskey'', ''action'', ''align'', ''alt'', ''axis'', ''border'', ''cellpadding'', ''cellspacing'', ''char'', ''charoff'', ''charset'', ''checked'', ''cite'', ''clear'', ''cols'', ''colspan'', ''color'', ''compact'', ''coords'', ''datetime'', ''dir'', ''enctype'', ''for'', ''headers'', ''height'', ''href'', ''hreflang'', ''hspace'', ''id'', ''ismap'', ''label'', ''lang'', ''longdesc'', ''maxlength'', ''method'', ''multiple'', ''name'', ''nohref'', ''noshade'', ''nowrap'', ''prompt'', ''rel'', ''rev'', ''rows'', ''rowspan'', ''rules'', ''scope'', ''shape'', ''size'', ''span'', ''src'', ''start'', ''summary'', ''tabindex'', ''target'', ''title'', ''type'', ''usemap'', ''valign'', ''value'', ''vspace'', ''width''] def clean_html( fragment ): while True: soup = BeautifulSoup( fragment ) removed = False for tag in soup.findAll(True): # find all tags if tag.name not in acceptable_elements: tag.extract() # remove the bad ones removed = True else: # it might have bad attributes # a better way to get all attributes? for attr in tag._getAttrMap().keys(): if attr not in acceptable_attributes: del tag[attr] # turn it back to html fragment = unicode(soup) if removed: # we removed tags and tricky can could exploit that! # we need to reparse the html until it stops changing continue # next round return fragment

Algunas pruebas pequeñas para asegurarse de que esto se comporte correctamente:

tests = [ #text should work (''<p>this is text</p>but this too'', ''<p>this is text</p>but this too''), # make sure we cant exploit removal of tags (''<<script></script>script> alert("Haha, I hacked your page."); <<script></script>/script>'', ''''), # try the same trick with attributes, gives an Exception (''<div on<script></script>load="alert("Haha, I hacked your page.");">1</div>'', Exception), # no tags should be skipped (''<script>bad</script><script>bad</script><script>bad</script>'', ''''), # leave valid tags but remove bad attributes (''<a href="good" onload="bad" onclick="bad" alt="good">1</div>'', ''<a href="good" alt="good">1</a>''), ] for text, out in tests: try: res = clean_html(text) assert res == out, "%s => %s != %s" % (text, res, out) except out, e: assert isinstance(e, out), "Wrong exception %r" % e


Las soluciones anteriores a través de Beautiful Soup no funcionarán. Es posible que pueda piratear algo con Beautiful Soup por encima y más allá, porque Beautiful Soup proporciona acceso al árbol de análisis sintáctico. Dentro de un tiempo, creo que intentaré resolver el problema correctamente, pero es un proyecto de una semana más o menos, y no tengo una semana gratis pronto.

Solo para ser específico, Beautiful Soup arrojará excepciones para algunos errores de análisis que el código anterior no capta; pero también hay muchas vulnerabilidades XSS muy reales que no se detectan, como:

<<script>script> alert("Haha, I hacked your page."); </</script>script>

Probablemente, lo mejor que puede hacer es quitar el < elemento como &lt; , prohibir todo el HTML, y luego usar un subconjunto restringido como Markdown para renderizar el formateo de forma adecuada. En particular, también puede volver atrás y volver a introducir bits comunes de HTML con una expresión regular. Así es como se ve el proceso, más o menos:

_lt_ = re.compile(''<'') _tc_ = ''~(lt)~'' # or whatever, so long as markdown doesn''t mangle it. _ok_ = re.compile(_tc_ + ''(/?(?:u|b|i|em|strong|sup|sub|p|br|q|blockquote|code))>'', re.I) _sqrt_ = re.compile(_tc_ + ''sqrt>'', re.I) #just to give an example of extending _endsqrt_ = re.compile(_tc_ + ''/sqrt>'', re.I) #html syntax with your own elements. _tcre_ = re.compile(_tc_) def sanitize(text): text = _lt_.sub(_tc_, text) text = markdown(text) text = _ok_.sub(r''</1>'', text) text = _sqrt_.sub(r''&radic;<span style="text-decoration:overline;">'', text) text = _endsqrt_.sub(r''</span>'', text) return _tcre_.sub(''&lt;'', text)

Todavía no probé ese código, por lo que puede haber errores. Pero ves la idea general: tienes que poner en lista negra todo el HTML en general antes de incluir en la lista blanca lo que está bien.


Prefiero la solución lxml.html.clean , como señala . Aquí hay que eliminar algunas etiquetas vacías:

from lxml import etree from lxml.html import clean, fromstring, tostring remove_attrs = [''class''] remove_tags = [''table'', ''tr'', ''td''] nonempty_tags = [''a'', ''p'', ''span'', ''div''] cleaner = clean.Cleaner(remove_tags=remove_tags) def squeaky_clean(html): clean_html = cleaner.clean_html(html) # now remove the useless empty tags root = fromstring(clean_html) context = etree.iterwalk(root) # just the end tag event for action, elem in context: clean_text = elem.text and elem.text.strip('' /t/r/n'') if elem.tag in nonempty_tags and / not (len(elem) or clean_text): # no children nor text elem.getparent().remove(elem) continue elem.text = clean_text # if you want # and if you also wanna remove some attrs: for badattr in remove_attrs: if elem.attrib.has_key(badattr): del elem.attrib[badattr] return tostring(root)


Puede usar html5lib , que usa una lista blanca para desinfectar.

Un ejemplo:

import html5lib from html5lib import sanitizer, treebuilders, treewalkers, serializer def clean_html(buf): """Cleans HTML of dangerous tags and content.""" buf = buf.strip() if not buf: return buf p = html5lib.HTMLParser(tree=treebuilders.getTreeBuilder("dom"), tokenizer=sanitizer.HTMLSanitizer) dom_tree = p.parseFragment(buf) walker = treewalkers.getTreeWalker("dom") stream = walker(dom_tree) s = serializer.htmlserializer.HTMLSerializer( omit_optional_tags=False, quote_attr_values=True) return s.render(stream)


Yo uso esto:

FilterHTML

Es simple y le permite definir una lista blanca bien controlada, restringe las URL e incluso compara los valores de los atributos contra expresiones regulares o tiene funciones de filtrado personalizadas por atributo.

Si se usa con cuidado, podría ser una solución segura.


la solución de con BeautifulSoup para abordar el problema planteado por Chris Drost . Un poco crudo, pero hace el trabajo:

from BeautifulSoup import BeautifulSoup, Comment VALID_TAGS = {''strong'': [], ''em'': [], ''p'': [], ''ol'': [], ''ul'': [], ''li'': [], ''br'': [], ''a'': [''href'', ''title''] } def sanitize_html(value, valid_tags=VALID_TAGS): soup = BeautifulSoup(value) comments = soup.findAll(text=lambda text:isinstance(text, Comment)) [comment.extract() for comment in comments] # Some markup can be crafted to slip through BeautifulSoup''s parser, so # we run this repeatedly until it generates the same output twice. newoutput = soup.renderContents() while 1: oldoutput = newoutput soup = BeautifulSoup(newoutput) for tag in soup.findAll(True): if tag.name not in valid_tags: tag.hidden = True else: tag.attrs = [(attr, value) for attr, value in tag.attrs if attr in valid_tags[tag.name]] newoutput = soup.renderContents() if oldoutput == newoutput: break return newoutput

Editar: actualizado para admitir atributos válidos.


Bleach funciona mejor con opciones más útiles. Está construido en html5lib y listo para producción.

Verifique estos documents .