【问题标题】:Editing multiple HTML files using SED (or something similar)使用 SED(或类似的东西)编辑多个 HTML 文件
【发布时间】:2011-03-16 17:13:15
【问题描述】:

我有大约 1000 个 HTML 文件要编辑,它们代表大型技术文档中的脚注。我被要求逐个浏览 HTML 文件并手动编辑 HTML,以使所有内容都直截了当。

我知道使用 SED 可能会在几秒钟内完成,因为每个文件的更改都是相似的。每个文件中的正文可以不同,但​​我想更改标签以匹配以下内容:

<body>
<p class="Notes">See <i>R v Swain</i> (1992) 8 CRNZ 657 (HC).</p>
</body>

文本可能会发生变化,例如,它可能会说“参见 R v Pinky and the Brain (1992) 或类似的内容,但基本上正文应该是这样。

不过,目前正文可能是:

<body>
<p class="Notes"><span class="FootnoteReference"><span lang="EN-GB" xml:lang="EN-GB"><span><span 
  class="FootnoteReference"><span lang="EN-GB" xml:lang="EN-GB" style="font-size: 10.0pt;">See <i>R v Pinky and the Brain</i> (1992) </span></span></span></span></span></p>
</body>

甚至:

<body>
<p class="FootnoteText"><span class="FootnoteReference"><span lang="EN-US" 
  xml:lang="EN-US" style="font-size: 10.0pt;"><span><![endif]></span></span></span>See <i>R v Pinky and the Brain</i> (1992)</p>
</body>

谁能建议一个 SED 表达式或类似的东西来解决这个问题?

【问题讨论】:

  • 不清楚您要做什么。 HTML 标记是否正常,只需要更改文字文本吗?请更清楚地说明您想要什么以及您拥有什么。两个例子:我有 A 但想要 A'B 需要 B' 会很好,三个例子会更好。
  • 重要的文字总是以See这个词开头吗?
  • 不是很清楚,但看起来您想要做的就是删除所有跨度。对吗?

标签: html sed


【解决方案1】:

要合并跨度标签,您也可以使用 tidy(2009 年 3 月 25 日发布的版本)!

# get current tidy version: http://tidy.cvs.sourceforge.net/viewvc/tidy/tidy/
# see also: http://tidy.sourceforge.net/docs/quickref.html#merge-spans

tidy -q -c --merge-spans yes file.html

【讨论】:

    【解决方案2】:

    首先使用http://tidy.sourceforge.net 将您的HTML 文件转换为正确的XHTML,然后使用xmlstarlet 进行必要的XHTML 处理。

    注意:获取当前版本的 xmlstarlet 用于就地 XML 文件编辑。

    这是一个简单但完整的小例子:

    curl -s http://checkip.dyndns.org > dyndns.html
    
    tidy -wrap 0 -numeric -asxml -utf8 2>/dev/null < dyndns.html > dyndns.xml
    
    # test: print body text to stdout (dyndns.xml)
    xml sel -T \
       -N XMLNS="http://www.w3.org/1999/xhtml" \
       -t -m "//XMLNS:body" -v '.' -n \
       dyndns.xml
    
    # edit body text in-place (dyndns.xml)
    xml ed -L \
       -N XMLNS="http://www.w3.org/1999/xhtml" \
       -u "//XMLNS:body" -v '<p NEW BODY TEXT </p>' \
       dyndns.xml
    
    # create new HTML file (by overwriting the original one!)
    xml unesc < dyndns.xml > dyndns.html
    

    【讨论】:

      【解决方案3】:

      您必须检查您的输入文件以验证是否可以做出一些假设。根据你的两个例子,我做了以下假设。您将需要检查它们并获取一些示例输入文件以验证您已找到所有假设。

      • 该文件由一个脚注组成,该脚注包含在一个 &lt;body&gt;&lt;/body&gt; 对中。正文标签始终存在且格式正确。

      • 脚注隐藏在一对&lt;p&gt;&lt;/p&gt; 和一个或多个&lt;span&gt;&lt;/span&gt; 标记内。 &lt;!...&gt;标签可以丢弃。

      以下 Perl 脚本适用于您提供的两个示例(在带有 Perl 5.10.0 的 Linux 上)。 在使用它之前,请确保您有原始 html 文件的备份。默认情况下,它只会在 stdout 上打印结果而不更改任何文件。

      #!/usr/bin/perl
      
      $overwrite = 0;
      
      # get rid of default line separator to facilitate slurping in a $scalar var
      $/ = '';
      foreach $filename (@ARGV)
      {
        # slurp entire file in $text variable
        open FH, "<$filename";
        $full_text = <FH>;
        close FH;
      
        if ($overwrite)
        {
            ! -f "$filename.bak" && rename $filename, "$filename.bak";
        }
      
        # match everything that is found before the body tag, everything
        # between and including the body tags, and what follows
        # s modifier causes full_text to be considered a single long string
        # instead of individual lines
        ($before_body, $body, $after_body) = ($full_text =~ m!(.*)<body>(.*)</body>(.*)!s);
        #print $before_body, $body, $after_body;
      
        # Discard unwanted tags from the body
        $body =~ s%<span.*?>%%sg;
        $body =~ s%</span.*?>%%sg;
        $body =~ s%<p.*?>%%sg;
        $body =~ s%</p.*?>%%sg;
        $body =~ s%<!.*?>%%sg;
        # Remaining leading and trailing whitespace likely to be newlines: remove
        $body =~ s%^\s*%%sg;
        $body =~ s%\s*$%%sg;
      
        if ($overwrite)
        {
          open FH, ">$filename";
          print FH $before_body, "<body>\n<p class=\"Notes\">$body</p>\n</body>", $after_body;
          close FH;
        }
        else
        {
              print $before_body, "<body>\n<p class=\"Notes\">$body</p>\n</body>", $after_body;
        }
      }
      

      使用它:

      ./script.pl file1.html 
      ./script.pl file1.html file2.html
      ./script.pl *.html
      

      调整它,当您满意时设置 $overwrite=1。仅当 .bak 不存在时,该脚本才会创建它。

      【讨论】:

        【解决方案4】:

        像这样?:

        perl -pe 's/Swain/Pinky and the Brain/g;' -i lots.html of.html files.html
        

        细分:

        • -e = "在命令行中使用代码"
        • -p = "在每个文件的每一行执行代码,并打印出该行,包括更改的内容"
        • -i = "实际上用新内容替换文件"

        如果您将-i 换成-i.old,那么lots.html.old 和of.html.old (etc) 将包含更改前的文件,以防您需要返回。

        这将在所有文件中将 Swain 替换为 Pinky and the Brain。进一步的更改将需要更多运行该命令。或者:

        s/Swain/Pinky/g; s/Twain/Brain/g;
        

        将 Swain 与 Pinky 互换,将 Twain 与 Brain 互换。

        更新:

        如果您可以确定传入的数据格式,那么这样的事情可能就足够了:

        # cat ff.html
          <body>
          <p class="Notes"><span class="FootnoteReference"><span lang="EN-GB" xml:lang="EN-GB"><span><span 
            class="FootnoteReference"><span lang="EN-GB" xml:lang="EN-GB" style="font-size: 10.0pt;">See <i>R v Twain</i> (1992) </span></span></span></span></span></p>
          <p class="Notes"><span class="FootnoteReference"><span lang="EN-GB" xml:lang="EN-GB"><span><span 
            class="FootnoteReference"><span lang="EN-GB" xml:lang="EN-GB" style="font-size: 10.0pt;">See <i>R v Swain</i> (1992) </span></span></span></span></span></p>
          </body>
        
        # perl -pe 'BEGIN{undef $/;} s/<[pP][ >].*?See <i>(.*?)<\/i>(.*?)<.*?\/[pP]>/<p class="Notes">See <i>$1<\/i>$2<\/p>/gsm;' ff.html
          <body>
            <p class="Notes">See <i>R v Twain</i> (1992) </p>
            <p class="Notes">See <i>R v Swain</i> (1992) </p>
          </body>
        

        解释:

        • BEGIN{undef $/;} = 将整个文档视为一个字符串,否则其中包含换行符的 html 将无法正确处理

        • &lt;[pP[ &gt;] = p-tag 的开头(不区分大小写)

        • .*? = 很多东西,非贪婪匹配,即http://en.wikipedia.org/wiki/Regular_expression#Lazy_quantification
        • See &lt;i&gt; = 从字面上寻找那个字符串 - 非常重要,因为它似乎是唯一的共同点
        • (.*?) = 将更多内容放入括号组(稍后使用)
        • &lt;\/i&gt; = 结束 i-tag
        • (.*?) = 将更多内容放入括号组(稍后使用)
        • &lt;.*?\/[pP] = 结束 p-tag 和其他可能的标签在它之前混搭(就像你所有的跨度一样)

        • 并将其替换为您想要的字符串,其中 $1 和 $2 是之前括号中的内容,即两个 (.*?)

        • g = 全局搜索 - 所以每行可能不止一个

        • s = 将所有内容都视为一行(现在是由于顶部的 BEGIN

        【讨论】:

        • 我的问题指定得很糟糕。正文总是不同的,但标签需要保持不变
        • 已更新。我肯定会先在一个文件上试一试,然后用 -i.old 来做这一切,以防万一。如果您的输入文件真的很垃圾,它可能无法满足您的所有需求。
        • 嗯,是的,有 -1 的人,他想要一个快速解决几个字符串的方法。这不是一个完美的解决方案。
        • 在使用正则表达式解析 html/xml 时要非常小心。阅读这个:codinghorror.com/blog/2009/11/parsing-html-the-cthulhu-way.html 这个:stackoverflow.com/questions/1732348/…
        • @worldsayshi - 是的,这绝对是真的。但操作人员似乎并不想要一个花哨的答案,只是一个带有解释的简单正则表达式。否则,是的,除了非常简单的 *ml 解析之外,其他任何事情都不是一个好主意。
        【解决方案5】:

        如果每个文件有 1 个条目,这些文件中没有严格的结构,并且可能有多行,我会使用 php 或 perl 脚本逐个文件处理它们,同时在模式不匹配时发出适当的警告.

        使用

        php -f thescript.php
        

        执行script.php,其中包含

        <?php
        $path = "datapath/";
        $dir = opendir($path);
        while ( ( $fn = readdir($dir) ) !== false )
        {
            if ( preg_match("/html$/",$fn) ) process($path.$fn);
        }
        
        function process($file)
        {
            $in = file_get_contents($file);
            $in2 = str_replace("\n"," ",strip_tags($in,"<i>"));
            if ( preg_match("#^(.*)<i>(.*)</i>(.*)$#i",$in2,$match) )
            {
                 list($dummy,$p0,$p1,$p2) = $match;
                 $out = "<body>$p0<i>$p1</i>$p2</body>";
                 file_put_contents($file.".out",$out);
            } else {
                 print "Problem with $file? (stripped down to: $in2)\n";
                 file_put_contents($file.".problematic",$in);
            }
        }
        ?>
        

        您可以根据自己的需要对此进行调整,直到未命中的数量足够低,可以手动完成最后几个。您可能需要添加一些 $p0 = trim($p0); 等来清理所有内容。

        【讨论】:

        • 我认为冒犯解析 html 的人可能认为他们的更好:-P
        • 我讨厌人们把编程、实用主义和宗教混为一谈。
        猜你喜欢
        • 1970-01-01
        • 2019-02-19
        • 2010-10-26
        • 1970-01-01
        • 2021-11-04
        • 1970-01-01
        • 1970-01-01
        • 2011-05-26
        • 2012-08-22
        相关资源
        最近更新 更多