2012-06-19 2 views
0

Я имею дело с очень примитивным HTML конструкцией, которая выглядит следующим образом:XPath индексы узлов

<a NAME="header1"></a><b><font face="Verdana, Serif"><font color="#000000"><font size=+1>Hygiene</font></font></font></b> 
    <p><b><font face="Verdana, Serif"><font color="#000000">Shampoo</font></b> 
    <p><b><font face="Verdana, Serif"><font color="#000000"></font>Soap</font></b> 
    <p><b><font face="Verdana, Serif"><font color="#000000">Deodorant</font></b> 
    <p><b><font face="Verdana, Serif"><font color="#000000">Toothpaste</font></b> 
    <p><b><font face="Verdana, Serif"><font color="#000000"></font>Brush</font></b> 

<a NAME="header2"></a><b><font face="Verdana, Serif"><font color="#000000"><font size=+1>Food</font></font></font></b> 
    <p><b><font face="Verdana, Serif"><font color="#000000">Meat</font></b> 
    <p><b><font face="Verdana, Serif"><font color="#000000">Vegetables</font></b> 
    <p><b><font face="Verdana, Serif"><font color="#000000">Fruit</font></b> 

Дело теперь, я хочу, чтобы все элементы из заголовка Hygiene (вверху), которые являются шампунь, мыло, Дезодорант, Зубная паста, Кисть (и добавьте их, скажем, HashMap>).

Я использую этот XPath, чтобы получить заголовки (Гигиена и пища):

//html/body//b/font/font/font 

И это прекрасно работает, я получаю то, что мне нужно.

Затем я использую этот XPath собирать предметы:

//html/body//p/b/font/font 

для всех элементов. Таким образом, этот (последний) XPath вернет список из всех элементов, которые являются [Шампунь, Мыло, Дезодорант, Зубная паста, Кисть, Мясо, Овощи, Фрукты]. Дело в том, что я не знаю, когда прекратить сдавать предметы в первом списке (например, когда начинается другой заголовок, который является продуктом питания в этом случае, создайте новый список и поставьте там продукты питания). Все, что я могу получить с этим XPaths, - это значения заголовков (Гигиена, Пища) и ВСЕ элементы из обоих списков (не отдельные).

мне нужно, чтобы получить что-то вроде:

  • Карта { "Гигиена", [шампунь, мыло, дезодорант, зубная паста, щетка]}
  • Map { "Food", [мясо, овощи, фрукты ]}

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

Спасибо!

+0

У вас будет трудное время, потому что это дерьмовый вход. Вы абсолютно уверены, что это выглядит так (например, названия продуктов * вне * тегов '')? Потому что такие детали будут иметь значение. – Jon

+0

да, вы правы, я обновил html. Элемент находится в теге . –

ответ

0

Непросто разобрать этот HTML-код, потому что он не поддается разбору (судя по тэгам <font>, вы, вероятно, могли бы использовать и красочный язык).

AFAIK нет никакого способа, чтобы выразить «после братьев и сестер, пока X» состояние в XPath, так вот альтернатива: использовать один выражение XPath, которое соответствует как заголовки и элементы, например, с этой конкретной разметки вы можете использовать

//body//font/child::text() 

, который выберет все текстовые узлы («Гигиена», «Шампунь», «Мыло», ...).

Узлы будут возвращены в порядке документа (это чрезвычайно важно), поэтому впоследствии вы можете перебирать результаты и выполнять тест на каждом, чтобы определить, является ли это заголовком или элементом (в этом случае вы могли бы проверьте, является ли родитель элементом <font>, который имеет атрибут size).

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

+0

спасибо !! Это сработало бы! –

1

Я хотел бы начать с (а) преобразование этого в XML с использованием (скажем) TagSoup, затем (б) с использованием преобразования XSLT 2.0, которое преобразует его в более санитарную структуру XML.

Я не уверен, что именно это сделает TagSoup, но если мы предположим, что единственное, что он делает, это закрыть теги p (с </p>, появляющимися после </b>), тогда шаг (b) вполне просто:

<xsl:for-each-group select="//body/*" group-starting-with="a"> 
    <section name="current-group()[self::b]"> 
    <xsl:for-each select="current-group()[self::p]"> 
     <item><xsl:value-of select="."/></item> 
    </xsl:for-each> 
    </section> 
</xsl:for-each-group> 

Это даст вам что-то вроде

<section name="Hygiene"> 
    <item>Shampoo</item> 
    <item>Soap</item> 
    <item>Toothpaste</item> 
</section> 
<section name="Food"> 
    <item>Meat</item> 
    <item>Veg</item> 
</section> 

, который затем гораздо легче играть.

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

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