2015-12-29 1 views
3

Использование AngleSharp для обработки некоторого HTML-кода и извлечения текстового содержимого элемента для последующего поиска, у меня возникла проблема с тем, как AngleSharp удаляет теги HTML. Например, у меня есть кусок HTML-то вроде этого (минус строки и вкладки):Сохранение (или восстановление) пробела в TextContent

<div id="someID"> 
    blah, blah, blah, blah 
    blah, blah, 
    <ul> 
     <li><i>action.</i></li> 
     <li><i>Typical, blah, blah, blah</li> 
    </ul> 
    blah, blah, blah 
</div> 

Проблема здесь, когда я получаю TextContent:

var content = someDiv.TextContext; 

Он будет выходить как это:

"...blah, blah, action.Typical blah, blah..." 

слова action и Typical были разбиты вместе без пробела (потому что единственное между ними HTML-теги). Это сворачивает мои усилия, чтобы затем символизировать текстовое содержимое, потому что action.Typical рассматривается как одно слово вместо двух слов.

Я мог бы, конечно, просто запустить поиск и замену (возможно, с использованием регулярных выражений), что-то вроде (\S)\.(\S) и заменить его $1. $2, но потом, что бы что-то вроде www.somecompany.com и разделить его на www, somecompany и com и I может хочу сохранить это (или, если не считать, что www и com вряд ли будут очень полезны в любом случае). Я мог исключить слова с более чем одной точкой, но веб-адрес может отображаться как somecompany.com (без www), или вы можете столкнуться с адресом электронной почты, например, [email protected].

Есть ли надежный способ обойти это? Чтобы сохранить хотя бы одно пространство после того, как теги были удалены?

+0

Действительно ли это HTML, который вы разбираете (буквально)? Потому что, если бы это было так, то AngleSharp, безусловно, имел бы пробелы (фактически новую строку и некоторые пробелы) между «действием». и "типичный". –

+0

@FlorianRappl: На самом деле нет, я отформатировал его для ясности, но на самом деле это единственная строка без разрывов строк или вкладок. Думаю, эта часть немного запутанна. Я уточню в вопросе. –

+0

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

ответ

0

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

var rootElem = myDoc.GetElementById("someId"); 

Я могу создать такую ​​функцию:

IEnumerable<string> ExtractChildNodes(INode node) 
{ 
    if (node.HasChildNodes) 
    { 
     foreach (var c in node.ChildNodes) 
     { 
      foreach (var r in ExtractChildNodes(c)) 
      { 
       yield return r; 
      } 
     } 
    } 
    else 
    { 
     yield return node.TextContent; 
    } 
} 

Это будет проверить, если узел имеет дочерние узлы и если он делает детализацию вплоть до самого низкого листового узла и вернуть текст оттуда. Затем я могу это сделать:

var textContentWithSpacesBetweenNodes = string.Join(" ", ExtractChildNodes(rootElem)); 

И должны дать мне:

"...blah, blah, action. Typical blah, blah..." 

С пространством между action и Typical.

Это, кажется, чтобы справиться с не только ситуации, как <p>some.</p><p>words</p>, но также самозакрывающиеся теги, как some</br>words или даже some<br>words, которые были бы массивное боль, чтобы иметь дело с использованием регулярных выражений или что-то подобное.

0

Способ описания работы, за исключением некоторых сценариев, которые вы уже встречали (например, самозакрывающиеся теги).Поэтому, я предлагаю следующее:

  • текстовых узлы будут буквально
  • элементов перебирать их дочерние узлы, w.r.t.
    • если два соседних элемента дают содержание, вставляется пробел
    • если нет дочерних узлов не смотреть, если элемент является особенным (например, ш) и поместить некоторую репрезентативную строку (например, символ новой строки)
    • иначе , например, если текстовый узел находится рядом с элементом, текст не вставляется

Так в целом следующая реализация должна сделать работу:

String Stringify(INode node) 
{ 
    switch (node.NodeType) 
    { 
     case NodeType.Text: 
      return node.TextContent; 

     case NodeType.Element: 
      if (node.HasChildNodes) 
      { 
       var sb = new StringBuilder(); 
       var isElement = false; 

       foreach (var child in node.ChildNodes) 
       { 
        var isPreviousElement = isElement; 
        var content = Stringify(child); 
        isElement = child.NodeType == NodeType.Element; 

        if (!String.IsNullOrEmpty(content) && isElement && isPreviousElement) 
        { 
         sb.Append(' '); 
        } 

        sb.Append(content); 
       } 

       return sb.ToString(); 
      } 

      switch (node.NodeName.ToLowerInvariant()) 
      { 
       case "br": return "\n"; 
      } 

      goto default; 

     default: 
      return String.Empty; 

    } 
} 

Преимущество такой реализации в том, что вы можете ее настроить в соответствии с вашими потребностями. Например, для таких тегов, как br, вы можете легко выводить пробелы вместо строк новой строки.