Разбор документа с помощью BeautifulSoup, не анализируя содержимое <code> tags

Я пишу приложение для блогов с Django. Я хочу, чтобы авторы комментариев использовали некоторые теги (например, <strong>, a и т.д.), Но отключили все остальные.

Кроме того, я хочу, чтобы они помещали код в <code> теги и обрабатывают их.

Например, кто-то может написать этот комментарий:

I like this article, but the third code example <em>could have been simpler</em>:

<code lang="c">
#include <stdbool.h>
#include <stdio.h>

int main()
{
    printf("Hello World\n");
}
</code>

Проблема заключается в том, что когда я разбираю комментарий с помощью BeautifulSoup для удаления запрещенных тегов HTML, он также анализирует внутренности кода <code> блоки и обрабатывает < stdbool.h > и < stdio.h > как если бы они были тегами HTML.

Как я могу сказать BeautifulSoup не анализировать <code> блоки? Может быть, есть другие синтаксические анализаторы HTML для этой работы?

Ответ 1

Проблема заключается в том, что <code> обрабатывается в соответствии с обычными правилами для разметки HTML, а содержимое внутри тегов <code> по-прежнему является HTML (теги существуют главным образом для форматирования CSS, а не для изменения правил синтаксического анализа).

То, что вы пытаетесь сделать, это создать другой язык разметки, который очень похож, но не идентичен HTML. Простое решение заключалось бы в том, чтобы предположить, что определенные правила, такие как "<code> и </code> должны появляться в строке сами по себе" и выполнять некоторую предварительную обработку самостоятельно.

  • Очень простая, но не на 100% надежная технология заключается в замене ^<code>$ на <code><![CDATA[ и ^</code>$ на ]]></code>. Он не является полностью надежным, потому что, если блок кода содержит ]]>, все будет идти ужасно неправильно.
  • Более безопасный вариант заключается в замене опасных символов внутри кодовых блоков (<, > и &, вероятно, достаточно) с их эквивалентными объектами сущности символов (&lt;, &gt; и &amp;). Вы можете сделать это, передав каждый блок кода, который вы идентифицируете, на cgi.escape(code_block).

Как только вы завершили предварительную обработку, отправьте результат в BeautifulSoup, как обычно.

Ответ 2

Из Python wiki

>>>import cgi
>>>cgi.escape("<string.h>")
>>>'&lt;string.h&gt;'

>>>BeautifulSoup('&lt;string.h&gt;', 
...               convertEntities=BeautifulSoup.HTML_ENTITIES)

Ответ 3

К сожалению, BeautifulSoup не может быть заблокирован для анализа блоков кода.

Одно из решений того, что вы хотите достичь, тоже

1) Удалите блоки кода

soup = BeautifulSoup(unicode(content))
code_blocks = soup.findAll(u'code')
for block in code_blocks:
    block.replaceWith(u'<code class="removed"></code>')

2) Проведите обычный синтаксический анализ, чтобы удалить недопустимые теги.

3) Вставьте блоки кода и заново создайте html.

stripped_code = stripped_soup.findAll(u"code", u"removed")
# re-insert pygment formatted code

Я бы ответил с некоторым кодом, но недавно я прочитал блог, который делает это элегантно.

Ответ 4

EDIT:

Используйте python-markdown2 для обработки ввода и у пользователей есть отступы в областях кода.

>>> print html
I like this article, but the third code example <em>could have been simpler</em>:

    #include <stdbool.h>
    #include <stdio.h>

    int main()
    {
        printf("Hello World\n");
    }

>>> import markdown2
>>> marked = markdown2.markdown(html)
>>> marked
u'<p>I like this article, but the third code example <em>could have been simpler</em>:</p>\n\n<pre><code>#include &lt;stdbool.h&gt;\n#include &lt;stdio.h&gt;\n\nint main()\n{\n    printf("Hello World\\n");\n}\n</code></pre>\n'
>>> print marked
<p>I like this article, but the third code example <em>could have been simpler</em>:</p>

<pre><code>#include &lt;stdbool.h&gt;
#include &lt;stdio.h&gt;

int main()
{
    printf("Hello World\n");
}
</code></pre>

Если вам все равно нужно перемещаться и редактировать его с помощью BeautifulSoup, сделайте следующее. Включите преобразование сущности, если вам нужно '<' и ' > ' для повторной установки (вместо '<' и ' > ').

soup = BeautifulSoup(marked, 
                     convertEntities=BeautifulSoup.HTML_ENTITIES)
>>> soup
<p>I like this article, but the third code example <em>could have been simpler</em>:</p>
<pre><code>#include <stdbool.h>
#include <stdio.h>

int main()
{
    printf("Hello World\n");
}
</code></pre>


def thickened(soup):
    """
    <code>
    blah blah <entity> blah
        blah
    </code>
    """
    codez = soup.findAll('code') # get the code tags
    for code in codez:
        # take all the contents inside of the code tags and convert
        # them into a single string
        escape_me = ''.join([k.__str__() for k in code.contents])
        escaped = cgi.escape(escape_me) # escape them with cgi
        code.replaceWith('<code>%s</code>' % escaped) # replace Tag objects with escaped string
    return soup

Ответ 5

Если элемент <code> содержит неизолированные символы <, &, > внутри кода, чем недействительный html. BeautifulSoup попытается преобразовать его в действительный html. Вероятно, это не то, что вам нужно.

Чтобы преобразовать текст в действительный html, вы можете адаптировать регулярное выражение, которое удаляет теги из html, чтобы извлечь текст из блока <code> и заменить его на cgi.escape() версия. Он должен работать нормально, если нет вложенных тегов <code>. После этого вы можете прокормить дезинфицированный html до BeautifulSoup.