开发者

How can I rewrite the text parts of HTML using HTML::Parser without altering the <script> and <head> sections?

开发者 https://www.devze.com 2023-01-08 06:54 出处:网络
The following code is a shortened version of an example from HTML::Parser #!/usr/bin/perl -w use strict;

The following code is a shortened version of an example from HTML::Parser

#!/usr/bin/perl -w
use strict;
my $code = shift || usage();
sub edit_print { local $_ = shift; tr/a-z/n-za-m/; print } 
use HTML::开发者_如何学运维Parser 3.05;
my $p = HTML::Parser->new(unbroken_text => 1,
     default_h => [ sub { print @_; }, "text" ],
     text_h    => [ \&edit_print,      "text" ],
);
my $file = shift;
$p->parse_file($file)

This code works pretty well, but it has a disadvantage that it also rewrites the text inside <script> and also <head> sections. I've adapted the example above to do what I want, but unfortunately there is a remaining bug where it rewrites things like the text inside the <title> tag which I don't want to rewrite.

Does anyone know how to write something like the above, but without mangling the JavaScript, <title>, or other sections? I'm happy to use another module apart from HTML::Parser if necessary.


Add start and end handlers to your parser, and have them record the ancestry of the current element. When the ancestry contains <head> or <script>, disable rewriting.

Keep your front matter

#! /usr/bin/perl

use warnings;
use strict;

use HTML::Parser 3.05;

sub edit_print { local $_ = shift; tr/a-z/n-za-m/; print }

and use the following sub to create a new parser:

sub create_parser {
  my @tags;
  my $start = sub {
    my($text,$tagname) = @_;
    push @tags => $tagname;
    print $text;
  };
  my $end = sub {
    my($text,$tagname) = @_;
    die "$0: expected </$tags[-1]>, got </$tagname>"
      unless $tagname eq $tags[-1];
    pop @tags;
    print $text;
  };
  my $edit_print = sub {
    if (grep /^(head|script)$/, @tags) { print @_ }
    else                               { edit_print @_ }
  };

  HTML::Parser->new(
    unbroken_text => 1,
    default_h     => [ sub { print @_ }, "text" ],
    text_h        => [ $edit_print,      "text" ],
    start_h       => [ $start,           "text,tagname" ],
    end_h         => [ $end,             "text,tagname" ],
  );
}

The reason for creating it inside a sub is the handler callbacks are closures that share private state in @tags. This implementation allows you to instantiate multiple parsers without worrying about them stomping on each other's data.

my $p = create_parser;
$p->parse_file(\*DATA);

__DATA__
foo
<html>
<head>
<title>My Title</title>
<style type="text/css">
  /* don't change me */
</style>
</head>
<body>
<script type="text/javascript">
  // or me
</script>
<h1>My Document</h1>
<p>Yo.</p>
</body>
</html>

Output:

sbb
<html>
<head>
<title>My Title</title>
<style type="text/css">
  /* don't change me */
</style>
</head>
<body>
<script type="text/javascript">
  // or me
</script>
<h1>Ml Dbphzrag</h1>
<p>Yb.</p>
</body>
</html>


Looking at your existing code, I'm not sure where you're stuck:

  1. add a stack of Booleans

    my @do_edit = (0)
    
  2. in edit_print, don't edit if $do_edit[0] is 0

  3. add start_h and end_h handlers to shift/unshift values for certain element names

0

精彩评论

暂无评论...
验证码 换一张
取 消