#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 предпочтительнее, поскольку может быть реализовано больше возможностей в поведении и фильтрации.