Разбор HTML-содержимого электронной почты внутри условных комментариев (пытаясь избежать регулярных выражений!) - PullRequest
0 голосов
/ 14 февраля 2019

Я знаю, как ужасно неправильно (пытаться) проанализировать HTML с помощью Regex, поэтому я очень, очень стараюсь его избежать.

У меня естьприложение, которое генерирует электронные письма HTML.Мы используем большой модный редактор электронной почты WYSIWYG, который заботится о создании адаптивных электронных писем, а также генерирует ужасную разметку для таких клиентов, как MS Outlook.Это делается с помощью условных комментариев, которые выглядят примерно так:Обратите внимание, что <v:roundrect> имеет атрибут href и оборачивает тег <a>, который увидят клиенты без mso.

<!--[if mso]>
    <!-- irrelevant <table> layout here, removed for your sanity and mine -->
    <v:roundrect href="https://google.com" irrelevant_attributes="snipped">
        <w:anchorlock/>
        <v:textbox inset="0,0,0,0">
        <center style="snipped">
<![endif]-->
<a href="https://google.com" target="_blank" <!-- style="snipped" --> > 
    <!-- some span tags with styles on them --> 
    click me!
    <!-- </spans> --> 
</a> 
<!--[if mso]>
    </center>
    </v:textbox>
    </v:roundrect>
    <!-- </table> layout stuff -->
<![endif]-->

Конечно, это всего лишь один из десятков (возможно, сотен?) возможных форматов, с которыми нам необходимо работать.

До появления этого редактора мы просили наших клиентов создавать собственные электронные письма в формате HTML с помощью более простого редактора WYSIWYG HMTL;но они должны были создавать адаптивные шаблоны и тестировать их содержимое в различных клиентах.С их точки зрения, этот новый редактор является огромным выигрышем.

Поскольку мы отправляем электронные письма, важно отслеживать клики по ссылке через ссылку отслеживания, которая перенаправляет на первоначально предназначенныйссылка.

На сегодняшний день мы использовали jSoup для анализа содержимого электронной почты, поиска любых тегов привязки и замены содержимого их атрибутов href. Поскольку регулярный синтаксический анализ html - это зло, верно?

Условные комментарии взломали эти механизмы.

Поскольку они являются комментариями, jSoup игнорирует их и щелкает по MSOutlook и другие клиенты, которые обрабатывают разметку <v:roundrect>, не были преобразованы для прохождения через наш трекер ссылок, поэтому клики не отслеживаются.Это проблема для нас.


Первая идея: заменить условные комментарии пользовательским тегом

Сначала я надеялся предварительно обработать тело сообщения, прежде чем позволить jSoup его получить.,Я бы заменил <!--[if mso]> на <adam> и <![endif]--> на </adam>.Это было достаточно просто, даже для сложных форм условий внутри комментариев.Я использовал регулярные выражения, чтобы сделать несколько простых замен:

  • <!--[if mso]> стал <adam orig="%3C%21%2D%2D%5Bif%20mso%5D%3E">
  • <!--[if (!mso)&(!IE)]> стал <adam orig="%3C%21%2D%2D%5Bif%20%28%21mso%29%26%28%21IE%29%5D%3E">
  • и т. Д.

Обратите внимание, что я полностью закодировал исходный комментарий по URL.url-кодировка позволила мне легко использовать регулярные выражения, чтобы найти комментарии к маркеру и преобразовать их обратно (чтобы мне не пришлось беспокоиться о > внутри содержимого атрибута "orig" ...

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

  • <!--<![endif]--> стал</adam orig="%3C%21%2D%2D%3C%21%5Bendif%5D%2D%2D%3E">
  • такой же подход для <![endif]--> и <!-->

Я не знаю, можно ли иметь атрибуты на закрывающем теге. Я никогда не проверял его, потому что у меня былоеще одна реализация, прежде чем я дошел до этого момента: осознание того, что использование <adam></adam> не приведет к желаемому выводу из jSoup, потому что результирующий INPUT часто будет выглядеть так:и jSoup попытается исправить это, изменив порядок тегов, чтобы сделать то, что он считает более правильным. Когда я понял это, я прекратил то, что делал, и начал думатьЕще раз о проблеме.


Вторая идея: то же самое, но с комментариями

Если (новая) проблема заключалась в том, что jSoup не нравилась моя вложенность тегов, что если яМожно ли открыть HTML-код внутри условных комментариев, как если бы он не был закомментирован, но оставить некоторые маркеры в качестве комментариев, которые я позже смогу преобразовать обратно в комментарии?Цель состояла в том, чтобы сделать это:

<!-- adam --><table><v:roundrect><center><!-- /adam -->
<a></a>
<!-- adam --></center></v:roundrect></table><!-- /adam -->

Это должно выглядеть как довольно аккуратный HTML, верно?Поэтому я внес изменения в код и дал ему шанс.

К сожалению, документы, с которыми мы работаем, намного сложнее, чем простой пример, который я начал сверху.Вот первые несколько строк фактического образца документа:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
    <head> 
        <!--[if gte mso 9]><xml><o:OfficeDocumentSettings><o:AllowPNG/><o:PixelsPerInch>96</o:PixelsPerInch></o:OfficeDocumentSettings></xml><![endif]--> 
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 
        <meta name="viewport" content="width=device-width"> 
        <!--[if !mso]><!--> 
        <meta http-equiv="X-UA-Compatible" content="IE=edge"> 
        <!--<![endif]--> 
        <title></title> 
        <!--[if !mso]><!--> 
        <!--<![endif]--> 
        <style type="text/css">/* snip */</style> 
        <style type="text/css" id="media-query">/* snip */</style> 
    </head>
    <body class="clean-body" style="margin: 0; padding: 0; -webkit-text-size-adjust: 100%; background-color: #FFFFFF;"> 
        <style type="text/css" id="media-query-bodytag">

После преобразования комментариев мы фактически сбросили блок <xml> в блок <head>, из которых jSoup определенно не фанат.Это то, что я возвращаю для вышеупомянутого ввода, после преобразования условных комментариев в мои обычные помеченные комментарии, анализа с помощью jSoup, а затем преобразования моих маркеров обратно в их условные комментарии:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
<head> 
    <!--[if gte mso 9]>
    </head>
    <body class="clean-body" style="margin: 0; padding: 0; -webkit-text-size-adjust: 100%; background-color: #FFFFFF;">
        <xml> <o:officedocumentsettings> <o:allowpng /> <o:pixelsperinch> 96 </o:pixelsperinch> </o:officedocumentsettings> </xml>
    <![endif]--> 
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 
    <meta name="viewport" content="width=device-width"> 
    <!--[if !mso]><!--> 
    <meta http-equiv="X-UA-Compatible" content="IE=edge"> 
    <!--<![endif]--> 
    <title></title> 
    <!--[if !mso]><!--><!--<![endif]--> 
    <style type="text/css">/* snip */</style> 
    <style type="text/css" id="media-query">/* snip */</style>   
    <style type="text/css" id="media-query-bodytag">/* snip */</style> 

Есть некоторые большиепроблемы здесь.Блок <head> сразу же закрывается.Тег <body> перемещается вверх до блока <xml>, а все, что появилось после него, перемещается в тело.Это не сработает.


И что теперь?

Я чувствую, что у нас в основном нет выбора.

  1. Ничего не делайте и просто не считайте клики от клиентов MS Outlook / etc.В некоторых случаях мы можем обнаружить клик в любом случае с помощью конверсии по этому адресу.(Даже если у нас нет записи о том, что вы щелкнули ссылку, если вы сделали платеж, мы знаем, что вы туда попали ...)
  2. Мы могли бы позволить нашему почтовому провайдеру сделать отслеживание ссылок для нас (эксперимент)обязательно; не обязательно, что они будут отслеживать ссылки <v:roundrect>).Исторически мы запустили эту систему с провайдером, который не предлагал отслеживание ссылок, поэтому нам пришлось развернуть свою собственную.Текущий провайдер предлагает это, но у нас есть годы существующего кода и процессов, которые необходимо обновить, чтобы поддержать это изменение.Мы держим это в нашем заднем кармане, если не можем понять что-то еще, но перспектива смены судов в середине потока ... не привлекательна.
  3. Или, наконец ... возможно ...регулярное выражение?(/ me ducks) Мы могли бы позволить jSoup сделать свое дело для обычного HTML, а затем использовать регулярные выражения для замены любых оставшихся ссылок.Это становится игрой в крота с текущей и будущей разметкой.Что мы можем столкнуться с <v:roundrect> в будущем?¯ \ _ (ツ) _ / ¯ И мы не узнаем, чего нам не хватает, без регулярных ручных проверок.

Если нет другого варианта, который мы еще не исследовали.Итак ... мы застряли ни с чем / regex?

Мы находимся в JVM, так что все, что Java находится в пределах досягаемости, я думаю.

...