XSLT:如何从复杂生成的 HTML 页面中过滤内容?

XSLT: How to filter content from complex generated HTML pages?

这里可以找到一些很好的示例,说明如何使用 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>
            &nbsp;
        </td>
        <td width="100%" bgcolor="black" style="padding: 10px;">
        <div align="center">
            
        </div>    
        </td>
    </tr>
    <tr>
        <td>
            &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">&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>
            &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>

结果应该是过滤掉所有以title开头的内容 <p class="page-title">Library</p> 包括脚注。

XSLT 是否可以做到这一点,也许可以展示实现此目的的方法?

过滤不需要的导航会很好,也许 class="about" 总是一样的。 但这可以在之后分几步完成。

预期的输出应该是这样的,或者可以是格式正确的 HTML-page:

<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">&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>

所以这是执行所需提取的 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.\n\n";

随着一些行的增加,可以过滤掉更多的内容,并且 HTML 框架是可选的。

但如果这可以用 XSLT 类似简单地完成,这仍然很有趣......

在这种特殊情况下,可以使用简单的 grep 在 shell 中完成基本过滤:

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

首选 perl 解决方案,因为可以实现更多的行为和过滤选项。

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 和 select 中将前缀绑定到该命名空间,并使用 prefix:local-name 例如xhtml:bodyxhtml:p 其中名称空间声明为 xmlns:xhtml="../../../../external.html?link=http://www.w3.org/1999/xhtml".