Получить первые несколько элементов HTML-фрагмента с помощью xpath на ruby - PullRequest
4 голосов
/ 21 октября 2010

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

Так что, если у меня есть

<h1>hello world</h1>
<p>Lets say these are 100 chars</p>
<ul>
    <li>some bla bla, 40 chars</li>
</ul>
<p>some other text</p>

И предположим, я хочу подвести итог с текстом в пределах первых 150 символов (не обязательно должен быть слишком точным, я мог бы просто получить первые 150 символов, включая теги и продолжить с этим, новероятно, создаст некоторые артефакты в хвосте, которые могут быть более трудными для обработки ...), он должен дать мне h1, p и ul, но не конечный p (который будет обрезан).Если первый элемент должен иметь более 150 символов, я бы взял полный первый элемент.

Как я могу получить это?Используя XPath или регулярное выражение?Я немного не знаю об этом ...

Редактировать

Сначала я хочу поблагодарить СПАСИБО всем, кто ответил!

Несмотря на то, что я получил действительно хорошие ответы в этой теме, я обнаружил, что гораздо проще подключить его до того, как включится интерпретатор уценки, взять первые n текстовых блоков, разделенных \ r \ n \ r \ n, и просто передать их для md.поколение.

  class String
    def summarize_md length
        arr = self.split(/\r\n\r\n/)
        sum =""
        arr.each do |ea|
          break if sum.length + ea.length > length
          sum = sum+"#{ea}\r\n\r\n"
        end
        sum
      end
  end

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

Ответы [ 4 ]

1 голос
/ 22 октября 2010

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

Я использовал что-то подобное много раз.

require 'nokogiri'

html = %q{
<h1>hello world</h1>
<p>Lets say these are 100 chars</p>
<ul>
    <li>some bla bla, 40 chars</li>
</ul>
<p>some other text</p>
}

doc = Nokogiri::HTML(html)
puts doc.content.gsub(/\n/, ' ').squeeze(' ').strip[0 .. 150]

Какие выходы

hello world Lets say these are 100 chars some bla bla, 40 chars some other text

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

1 голос
/ 21 октября 2010

Чистое решение XPath 1.0 :

подстрока (/ *, 1 150)

, где родительский элемент предоставленного фрагмента XHTML является верхним элементом (/*или /html).

Существует очень точное решение XPath 2.0 :

   for $t in (//text())[not(sum((.| preceding::text())/string-length(.)) gt 150)]
     return
       ($t, '&#xA;')

Примечание : XML-документ должен быть проанализированв режиме, который отбрасывает текстовые узлы только для пробелов.В противном случае string-length(.) необходимо заменить на string-length(normalize-space(.))

1 голос
/ 21 октября 2010

Как я мог получить это?

XSLT, конечно!

Эта таблица стилей:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:strip-space elements="*"/>
    <xsl:param name="pMaxLength" select="73"/>
    <xsl:template match="node()">
        <xsl:param name="pPrecedingLength" select="0"/>
        <xsl:variable name="vContent">
            <xsl:copy>
                <xsl:copy-of select="@*"/>
                <xsl:apply-templates select="node()[1]">
                    <xsl:with-param name="pPrecedingLength"
                                    select="$pPrecedingLength"/>
                </xsl:apply-templates>
            </xsl:copy>
        </xsl:variable>
        <xsl:variable name="vLength"
                      select="$pPrecedingLength + string-length($vContent)"/>
        <xsl:if test="$pMaxLength > $vLength and
                      (string-length($vContent) or not(node()))
                      or not($pPrecedingLength)">
            <xsl:copy-of select="$vContent"/>
            <xsl:apply-templates select="following-sibling::node()[1]">
                <xsl:with-param name="pPrecedingLength" select="$vLength"/>
            </xsl:apply-templates>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

Выход:

<html>
    <h1>hello world</h1>
    <p>Lets say these are 100 chars</p>
    <ul>
        <li>some bla bla, 40 chars</li>
    </ul>
</html>
0 голосов
/ 21 октября 2010

Использование XPath является наиболее надежным и гибким.Вот пример приложения:

require 'rubygems'
require 'nokogiri'

html = <<End
<h1>hello world</h1>
<p>Lets say these are 100 chars.......................................................................</p>
<ul>
    <li>some bla bla, 40 chars</li>
</ul>
<p>some other text</p>
End

LIMIT = 150
summary = ""

doc = Nokogiri::HTML.parse(html)
doc.xpath('//text()').each do |node|
  text = node.text
  break if summary.length + text.length >= LIMIT
  summary << text
end

puts summary
puts summary.length

XPath //text() просто выделяет все текстовые узлы в документе.Если вы хотите более конкретно указать, какие элементы вас интересуют, вы можете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...