2015-12-15 4 views
15

История:Отключить специальный «класс» атрибут обработки

Когда вы разбираете HTML с BeautifulSoup, class атрибут считается multi-valued attribute и обрабатывается особым образом:

Помните, что один тег может иметь несколько значений для своего атрибута «class». Когда вы ищете тег, который соответствует определенному классу CSS, вы соответствуете любому из его классов CSS.

Кроме того, цитата из встроенной HTMLTreeBuilder используется BeautifulSoup в качестве основы для других классов Tree Builder, как, например, 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. 

Вопрос:

Как настроить BeautifulSoup для обработки class как обычного однозначного атрибута? Другими словами, я не хочу, чтобы он обрабатывал class специально и считал его обычным атрибутом.

FYI, вот один из потребительных случаев, когда это может быть полезным:

Что я пробовал:

Я на самом деле сделал он работает, создав пользовательский класс строителя деревьев и удалив class из списка специально обработанных атрибутов:

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

Что мне не нравится в этом подходе является то, что это вполне «неестественным» и «магическая» с участием импорта «частный» внутренний _htmlparser. Надеюсь, что есть более простой способ.

ПРИМЕЧАНИЕ. Я хочу сохранить все другие функции, связанные с анализом HTML, что означает, что я не хочу анализировать HTML с «xml» - единственными функциями (которые могли быть другим обходным решением).

+2

Я думал, что это была ошибка, когда я увидел ваш аватар под вопросом beautifulsoup без ответа, а затем я понял, что вы * задали * вопрос! Я не могу помочь вам в том, что все, что я пробовал, не работает или задействовано в двух итерациях. – dstudeba

+0

Я не знаю, как это сделать, но для конкретного примера использования, представленного в качестве примера, я дал другой ответ (поэтому я разместил его там). Это проще, на мой взгляд, но может быть недостаточно для других случаев использования. – rll

+0

Использование его как селектор css ?. Возможно, в этом случае самым простым вариантом может быть не использование обычного селектора классов, а селектор атрибутов. Селектор «.myclass» - это то же самое, что «[class = ~» myclass »] ', но selector' [class =" class "] '- это элемент, значение атрибута класса которого точно равно« myclass »(не myclass в пространственном разделенном списке). –

ответ

6

То, что мне не нравится в этом подходе, заключается в том, что оно довольно «неестественное» и «магическое», связанное с импортом «частных» внутренних _htmlparser. Надеюсь, что есть более простой способ.

Да, вы можете импортировать его из bs4.builder вместо:

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

И если это достаточно важно, чтобы вы не хотите повторить себя, поставить строитель в своем собственном модуле, и зарегистрировать его register_treebuilders_from(), чтобы он имел преимущество.

+0

Хотя это работает, я ненавижу, что мои IDE (PyCharm и IntelliJ IDEA с плагином Python) жалуются на импорт 'bs4.builder'. Они говорят «Unresolved reference» HTMLParserTreeBuilder », и он не может перейти к объявлению для него, когда я его прошу. Являются ли другие IDE лучше об этом? –

2

Класс HTMLParserTreeBuilder фактически объявлен на upper module_init__.py, поэтому нет необходимости импортировать непосредственно из частного субмодуля. При этом я хотел бы сделать это следующим образом:

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 

Это в основном так же, как определение класса, как в ОП (возможно, немного более ясно), но я не думаю, что есть лучший способ сделай это.

Смежные вопросы