2015-12-15 5 views
15

Die Story:Disable spezielle "class" -Attribut Handhabung

Wenn Sie HTML mit BeautifulSoup analysieren, class Attribut gilt als multi-valued attribute und wird in besonderer Weise behandelt:

Denken Sie daran, dass ein einzelnes Das Tag kann mehrere Werte für sein Attribut "class" haben. Wenn Sie nach einem Tag suchen, das einer bestimmten CSS-Klasse entspricht, stimmen Sie mit allen CSS-Klassen überein.

Auch ein Zitat aus einem eingebauten in HTMLTreeBuilder von BeautifulSoup als Basis für andere Tree-Builder-Klassen verwendet, wie zum Beispiel HTMLParserTreeBuilder:

# The HTML standard defines these attributes as containing a 
# space-separated list of values, not a single value. That is, 
# class="foo bar" means that the 'class' attribute has two values, 
# 'foo' and 'bar', not the single value 'foo bar'. When we 
# encounter one of these attributes, we will parse its value into 
# a list of values if possible. Upon output, the list will be 
# converted back into a string. 

Die Frage:

Wie kann ich BeautifulSoup so konfigurieren, dass class als normales einwertiges Attribut gehandhabt wird? Mit anderen Worten, ich möchte nicht, dass es speziell mit class arbeitet und es als reguläres Attribut ansieht.

FYI, hier ist eine der Anwendungsfälle, wenn es hilfreich sein kann:

Was ich habe versucht:

Ich habe tatsächlich in es funktioniert, indem Sie eine benutzerdefinierte Baum Builder Klasse und entfernen class aus der Liste der speziell behandelten Attribute:

from bs4.builder._htmlparser import HTMLParserTreeBuilder 

class MyBuilder(HTMLParserTreeBuilder): 
    def __init__(self): 
     super(MyBuilder, self).__init__() 

     # BeautifulSoup, please don't treat "class" specially 
     self.cdata_list_attributes["*"].remove("class") 


soup = BeautifulSoup(data, "html.parser", builder=MyBuilder()) 

Was ich nicht bei diesem Ansatz ist, dass es sehr „unnatürlich“ und „magische“ Beteiligung „private“ interne _htmlparser importieren. Ich hoffe es gibt einen einfacheren Weg.

HINWEIS: Ich möchte alle anderen HTML-Parsing-Funktionen speichern, dh ich möchte HTML nicht mit "xml" -only-Funktionen analysieren (was eine andere Problemumgehung hätte sein können).

+2

Ich dachte, es wäre ein Fehler, als ich deinen Avatar unter einer schönen, nicht beantworteten Frage sah und dann merkte ich, dass du die Frage * gestellt * hast! Ich kann dir nicht helfen, alles was ich versucht habe, hat nicht funktioniert oder zwei Iterationen involviert. – dstudeba

+0

Ich weiß nicht, wie das geht, aber für den spezifischen Anwendungsfall, der als Beispiel angegeben wurde, habe ich eine andere Antwort angegeben (also habe ich sie dort gepostet). Es ist meiner Meinung nach einfacher, aber möglicherweise nicht ausreichend für andere Anwendungsfälle – rll

+0

Verwenden Sie es als CSS-Selektor ?. Vielleicht könnte in diesem Fall die einfachste Option nicht ein gemeinsamer Klassenselektor, sondern ein Attributselektor sein. Selector '.myclass' ist genau das gleiche wie '[class = ~ "myclass"]', aber selector '[class = "class"]' ist ein Element, dessen Attributwert "class" exakt gleich "myclass" ist (nicht Myclass in einer Liste mit Leerzeichen getrennt). –

Antwort

6

Was ich ist nicht in diesem Ansatz mag, dass es recht „unnatürlich“ und „magische“ Beteiligung „private“ interne _htmlparser importieren. Ich hoffe es gibt einen einfacheren Weg.

Ja, können Sie es aus bs4.builder stattdessen importieren:

from bs4 import BeautifulSoup 
from bs4.builder import HTMLParserTreeBuilder 

class MyBuilder(HTMLParserTreeBuilder): 
    def __init__(self): 
     super(MyBuilder, self).__init__() 
     # BeautifulSoup, please don't treat "class" as a list 
     self.cdata_list_attributes["*"].remove("class") 


soup = BeautifulSoup(data, "html.parser", builder=MyBuilder()) 

Und wenn es wichtig genug ist, dass man sich nicht wiederholen wollen, die Baumeister in seinem eigenen Modul setzen, und registrieren Sie es mit register_treebuilders_from(), so dass es Vorrang hat.

+0

Während das funktioniert, hasse ich, dass meine IDEs (PyCharm und IntelliJ IDEA mit Python-Plugin) sich über den 'bs4.builder' Import beschweren. Sie sagen "Ungelöste Referenz 'HTMLParserTreeBuilder'" und es kann nicht zu der Deklaration dafür springen, wenn ich danach frage. Sind andere IDEs besser? –

2

Die Klasse HTMLParserTreeBuilder ist tatsächlich auf den _init__.pyupper module erklärt, so gibt es keine Notwendigkeit, direkt aus dem privaten Submodul zu importieren. Das sagte, ich würde es die folgende Art und Weise tun:

import re 

from bs4 import BeautifulSoup 
from bs4.builder import HTMLParserTreeBuilder 

bb = HTMLParserTreeBuilder() 
bb.cdata_list_attributes["*"].remove("class") 

soup = BeautifulSoup(bs, "html.parser", builder=bb) 
found_elements = soup.find_all(class_=re.compile(r"^name\-single name\d+$")) 
print found_elements 

Es ist im Grunde die gleiche wie die Klasse, wie im OP definieren (vielleicht ein bisschen mehr explizit), aber ich glaube nicht, gibt es eine bessere Art und Weise zu TU es.