XSLT: Как фильтровать контент со сложных HTML-страниц?

#xslt #filter #merge

Вопрос:

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

Существует масса отдельных сохраненных HTML-страниц (которые были сгенерированы с помощью ASP), как в следующем примере, которые следует отфильтровать и объединить в один HTML, чтобы создать из него книгу.

 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="../../../../external.html?link=http://www.w3.org/1999/xhtml" >
<head id="Head1"><title>
    2021_0623.aspx
</title>
</style></head>
<body>
<div align="center">
   
<div class="aspNetHidden">
</div>
    <table width="95%" id="table1" cellspacing="0" cellpadding="0" border="0" >   
    <tr>
        <td>
            amp;nbsp;
        </td>
        <td width="100%" bgcolor="black" style="padding: 10px;">
        <div align="center">
            
        </div>    
        </td>
    </tr>
    <tr>
        <td>
            amp;nbsp;
        </td>
        <td bgcolor="black" width="100%" height="20px" style="padding-left: 20px; padding-right: 20px; padding-bottom: 10px;">
        <div class="align-left">
        </div>
        </td>    
    </tr>
    <tr>
        <td align="right" valign="top" style="padding-right: 10px">
            <a href="" /></a><div id="Menu1">
    <ul class="level1">
        <li>Recent Updates</li>
    </ul>
</div><a id="Menu1_SkipLink"></a>
        </td>
        <td width="100%" valign="top" bgcolor="white" style="padding: 20px;"> 
            
<p class="page-title">Library</p>
<p class="page-title-2">Library Text</p>
<div class="nav">
    <table class="nav">
    <tr class="nav">
    <td class="nav-title">Some unneeded navigation</td>
    <td class="nav">
    </td>
    </tr>
    </table>
</div>
<p class="copyright">Copyright © 2021</p>
<p class="about"><strong>ABOUT THE CONTENTS.</strong></p>
<p class="text-title">Title of text</p>
<p class="text-date">August 22, 2021</p>
<p>text of interest.</p>
<p>more text of interest.</p>

<p class="separator-left-33">amp;nbsp;</p>
<p class="footnote"><a id="_ftn1" href="#_ftnref1" name="_ftn1">[1]</a> a footnote of interest</p>
<p class="footnote"><a id="_ftn2" href="#_ftnref2" name="_ftn1">[2]</a> one more footnote of interest</p>

<div class="nav">
<table class="nav">
</table>
</div>
    </td>
    </tr>   
    <tr>
        <td>
            amp;nbsp;
        </td>
        <td width="100%" height="45" align="left" valign="top" style="padding-left: 20px; padding-top: 5px;" bgcolor="black">
            </td>
    </tr>     
    </table>
    </form>
</div>
</body>
</html>
 

Результатом должна быть фильтрация всего содержимого, начинающегося с заголовка
<p class="page-title">Library</p>
включая сноски.

Возможно ли это с помощью XSLT и, возможно, показать способ сделать это?

Было бы неплохо отфильтровать ненужную навигацию и, возможно, class=»о», который всегда один и тот же. Но это можно сделать в несколько этапов позже.

Ожидаемый результат должен быть таким или может быть хорошо сформированной HTML-страницей:

 <p class="page-title">Library</p>
<p class="page-title-2">Library Text</p>
<p class="copyright">Copyright © 2021</p>
<p class="text-title">Title of text</p>
<p class="text-date">August 22, 2021</p>
<p>text of interest.</p>
<p>more text of interest.</p>
<p class="separator-left-33">amp;nbsp;</p>
<p class="footnote"><a id="_ftn1" href="#_ftnref1" name="_ftn1">[1]</a> a footnote of interest</p>
<p class="footnote"><a id="_ftn2" href="#_ftnref2" name="_ftn1">[2]</a> one more footnote of interest</p>
 

Комментарии:

1. Какая версия XSLT? И фильтр/слияние на самом деле не является точным описанием.

2. Да, ты можешь. В SO рекомендуется публиковать выходные данные, которые вы хотите, соответствующие входным данным выше, а также публиковать код, который вы написали до сих пор.

3. Я просто хочу привести несколько примеров использования xsltproc в Linux. Я понятия не имею, какая версия XSLT необходима?

4. В данный момент я думаю, что решение в XSLT требует больших усилий, потому что я понимаю только принципы XSLT. Поэтому я пишу несколько строк в perl, которые соответствуют соответствующей строке, а затем вывожу каждую строку, начинающуюся с »

5. <html xmlns="../../../../external.html?link=http://www.w3.org/1999/xhtml" > Действительно ли объявление пространства имен во входных документах?

Ответ №1:

xsltproc, похоже, имеет возможность обрабатывать --html документы вместо XML, поэтому предположим, что эта опция позволяет анализировать ваши входные данные в HTML без пространства имен. Код XSLT 1

 <xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">

  <xsl:output method="html" indent="yes" version="5" doctype-system="about:legacy-doctype"/>

  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="body">
      <xsl:copy>
          <xsl:variable name="start-element" select="//p[@class = 'page-title']"/>
          <xsl:apply-templates select="$start-element | $start-element/following-sibling::p"/>
      </xsl:copy>
  </xsl:template>

</xsl:stylesheet>
 

Если HTML — документы окажутся в этом нечетном пространстве имен, которое содержит ваш ввод, вам придется привязать префикс к этому пространству имен в XSLT 1, а также выбрать и сопоставить узлы элементов с определенными именами prefix:local-name , например, с использованием xhtml:body или xhtml:p где будет объявлено пространство xmlns:xhtml="../../../../external.html?link=http://www.w3.org/1999/xhtml" имен .

Комментарии:

1. Все в порядке — спасибо! «xsltproc —html -o output.html xslt.xml 1.html» работает — есть пара ошибок валидности, но output.html это то, что ожидается. Можно ли отфильтровать класс=»о» ?

2. @Karsten, да, добавьте пустой шаблон <xsl:template match="p[@class = 'about']"/> , чтобы заблокировать обработку этих элементов.

3. Да, это работает — спасибо — принцип теперь ясен. Последнее, чего не хватает, — это снова объединить несколько отфильтрованных HTML-страниц в один большой HTML-файл, который можно преобразовать в электронную книгу. В Perl я уже реализовал это, но с XSLT, возможно, будет лучшее решение для следов.

4. @Karsten, в XSLT 1 есть способ обработки множественного или вторичного ввода, это делается с помощью document функции. Я не думаю, что имеет смысл объяснять это в комментарии, прочитайте введение в XSLT и использование document функции в такой книге, как cranesoftwrights.github.io/books/ptux/index.htm или любое другое введение в XSLT, затем попробуйте использовать это, если у вас не получится, задайте отдельный вопрос по этому поводу.

Ответ №2:

Итак, вот базовое решение в соответствии со сценарием perl, выполняющим необходимую экстракцию:

 #!/usr/bin/perl
my $LCount = 0; # Line count
my $ICount = 0; # Line ignore count
my $DCount = 0; # Line done count
my $Line;           # actual line

if (@ARGV == 0) {       # Kein Paramter -> Beschreibung
    print "n";
    print "extract.pl [input-file] [output-file]n";
    print "n";
    exit;
}

if (@ARGV < 1) { die "To less parameter!n"; }
if (@ARGV > 2) { die "To much parameter!n"; }

my $InputFile = $ARGV[0];
my $OutputFile = $ARGV[1];


###############################################################################
# Main programm
###############################################################################

open(InFile, $InputFile) or die "Error opening '$InputFile': $!n";
open(OutFile,"> $OutputFile") or die "Error opening '$OutputFile': $!n";

while(defined($Line = <InFile>)) {
    $LCount   ;

    if ($Line =~ /^<p/) {
        if ($Line =~ /class="about"/) {
            $ICount   ;
        } else {
            $DCount   ;
            print OutFile $Line;
        }
    } else {
        $ICount   ;
    }
}

close(InFile) or die "Error closing '$InputFile': $! n";
close(OutFile) or die "Error closing '$OutputFile': $! n";

print "n$LCount lines from $InputFile processed.n";
print "$DCount lines extracted.n";
print "$ICount lines ignored.nn";
 

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

Но все равно интересно, можно ли это сделать так же просто с помощью XSLT …

Ответ №3:

В этом особом случае базовая фильтрация может быть выполнена в оболочке с помощью простого grep:

 grep "<p" 1.html > out.html
 

Решение perl предпочтительнее, поскольку может быть реализовано больше возможностей в поведении и фильтрации.