Использование Beautiful Soup для преобразования атрибутов CSS в отдельные атрибуты HTML?

Я пытаюсь написать программу, которая возьмет файл HTML и сделает его более удобным для электронной почты. На данный момент все преобразования выполняются вручную, потому что ни один из онлайн-конвертеров не делает именно то, что нам нужно.

Это звучало как отличная возможность раздвинуть границы моих знаний в области программирования и на самом деле написать что-то полезное, поэтому я предложил попробовать написать программу в свободное время, чтобы сделать процесс более автоматизированным.

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

Большинство изменений довольно простые — если вы видите тег/атрибут X, преобразуйте его в тег/атрибут Y. Но у меня возникли проблемы при работе с HTML-тегом, содержащим атрибут стиля. Например:

<img src="http://example.com/file.jpg" style="width:150px;height:50px;float:right" />

По возможности я хочу преобразовать атрибуты стиля в атрибуты HTML (или преобразовать атрибут стиля во что-то более удобное для электронной почты). Таким образом, после преобразования это должно выглядеть так:

<img src="http://example.com/file.jpg" width="150" height="50" align="right"/>

Теперь я понимаю, что не все атрибуты стилей CSS имеют HTML-эквиваленты, поэтому сейчас я хочу сосредоточиться только на тех, у которых они есть. Я набросал скрипт Python, который будет выполнять это преобразование:

from bs4 import BeautifulSoup
import re

class Styler(object):

    img_attributes = {'float' : 'align'}

    def __init__(self, soup):
        self.soup = soup

    def format_factory(self):
        self.handle_image()

    def handle_image(self):
        tag = self.soup.find_all("img", style = re.compile('.'))
        print tag
        for i in xrange(len(tag)):
            old_attributes = tag[i]['style']
            tokens = [s for s in re.split(r'[:;]+|px', str(old_attributes)) if s]
            del tag[i]['style']
            print tokens

            for j in xrange(0, len(tokens), 2):
                if tokens[j] in Styler.img_attributes:
                    tokens[j] = Styler.img_attributes[tokens[j]]

                tag[i][tokens[j]] = tokens[j+1]

if __name__ == '__main__':
    html = """
    <body>hello</body>
    <img src="http://example.com/file.jpg" style="width:150px;height:50px;float:right" />
    <blockquote>my blockquote text</blockquote>
    <div style="padding-left:25px; padding-right:25px;">text here</div>
    <body>goodbye</body>
    """
    soup = BeautifulSoup(html)
    s = Styler(soup)
    s.format_factory()

Теперь этот скрипт отлично справится с моим конкретным примером, но он не очень надежен, и я понимаю, что при сопоставлении с примерами из реального мира он легко сломается. Мой вопрос в том, как я могу сделать это более надежным? Насколько я могу судить, в Beautiful Soup нет способа изменить или извлечь отдельные части атрибута стиля. Я думаю, это то, что я хочу сделать.


person David DeMar    schedule 01.05.2012    source источник
comment
Для начала вы должны проверить, существует ли атрибут style, прежде чем использовать его здесь: old_attributes = tag[i]['style']. Не все теги есть.   -  person Nadh    schedule 01.05.2012
comment
Это плохая идея — атрибуты HTML, определяющие стили, устарели именно потому, что их заменил CSS. Пусть будет CSS.   -  person Gareth Latty    schedule 01.05.2012
comment
Проблема в том, что многие почтовые клиенты неправильно отображают определенные атрибуты (или вообще не отображают), поэтому их необходимо преобразовать в атрибуты HTML.   -  person David DeMar    schedule 01.05.2012
comment
Можете ли вы привести несколько примеров таких почтовых клиентов?   -  person suzanshakya    schedule 01.05.2012
comment
Outlook является одним из примеров. Но я не эксперт в том, как работает каждый почтовый клиент. Я просто пытаюсь написать код, который форматирует HTML так, как ему нужно.   -  person David DeMar    schedule 01.05.2012


Ответы (2)


Для такого рода вещей я бы рекомендовал парсер HTML (например, BeautifulSoup или lxml) в сочетании со специализированным парсером CSS. Мне удалось успешно использовать пакет cssutils. Вам будет намного легче, чем пытаться придумать регулярные выражения для соответствия любому возможному CSS, который вы можете найти в дикой природе.

Например:

>>> import cssutils
>>> css = 'width:150px;height:50px;float:right;'
>>> s = cssutils.parseStyle(css)
>>> s.width
u'150px'
>>> s.height
u'50px'
>>> s.keys()
[u'width', u'height', u'float']
>>> s.cssText
u'width: 150px;\nheight: 50px;\nfloat: right'
>>> del s['width']
>>> s.cssText
u'height: 50px;\nfloat: right'

Таким образом, используя это, вы можете довольно легко извлекать и манипулировать нужными свойствами CSS, а также вставлять их в HTML непосредственно с помощью BeautifulSoup. Однако будьте немного осторожны с символами новой строки, которые появляются в атрибуте cssText. Я думаю, что cssutils больше предназначен для форматирования вещей в виде отдельных файлов CSS, но он достаточно гибок, чтобы в основном работать с тем, что вы здесь делаете.

person chigby    schedule 09.05.2012
comment
Но эта библиотека дает сбой при попытке использовать собственный атрибут css, как в div style=position:absolute; граница: текстовое поле 1px сплошное; режим письма: LR-TB; слева: 45px; верх: 81px; ширина: 127 пикселей; высота: 9 пикселей; подробности в stackoverflow.com/questions/33151188/ - person SIslam; 15.10.2015

Вместо того, чтобы изобретать велосипед, используйте пакет Stoneage http://pypi.python.org/pypi/StoneageHTML

person nueces    schedule 16.05.2012