File Coverage

blib/lib/HTML/Feature/Engine/TagStructure.pm
Criterion Covered Total %
statement 121 125 96.8
branch 19 32 59.3
condition 17 36 47.2
subroutine 10 10 100.0
pod 1 1 100.0
total 168 204 82.3


line stmt bran cond sub pod time code
1             package HTML::Feature::Engine::TagStructure;
2 3     3   11 use strict;
  3         4  
  3         68  
3 3     3   9 use warnings;
  3         3  
  3         69  
4 3     3   8 use base qw(HTML::Feature::Engine);
  3         3  
  3         1077  
5 3     3   1700 use HTML::TreeBuilder;
  3         66912  
  3         26  
6 3     3   1396 use Statistics::Lite qw(statshash);
  3         3159  
  3         196  
7 3     3   1023 use HTML::Feature::Result;
  3         8  
  3         16  
8              
9             sub run {
10 8     8 1 16 my $self = shift;
11 8         7 my $c = shift;
12 8         8 my $html_ref = shift;
13 8         12 $self->_tag_cleaning($c, $html_ref);
14 8         15 return $self->_score($c, $html_ref);
15             }
16              
17             sub _tag_cleaning {
18 8     8   7 my $self = shift;
19 8         8 my $c = shift;
20 8         4 my $html_ref = shift;
21 8 50 33     41 return unless $html_ref && $$html_ref;
22              
23             # preprocessing
24 8         28 $$html_ref =~ s{}{}xmsg;
25 8         20 $$html_ref =~ s{]*>.*?<\/script>}{}xmgs;
26 8         14 $$html_ref =~ s{ }{ }xmg;
27 8         12 $$html_ref =~ s{"}{\'}xmg;
28 8         19 $$html_ref =~ s{\r\n}{\n}xmg;
29 8         114 $$html_ref =~ s{^\s*(.+)$}{$1}xmg;
30 8         67 $$html_ref =~ s{^\t*(.+)$}{$1}xmg;
31              
32             # control code ( 0x00 - 0x1F, and 0x7F on ascii)
33 8         19 for ( 0 .. 31 ) {
34 256         356 my $control_code = '\x' . sprintf( "%x", $_ );
35 256         1405 $$html_ref =~ s{$control_code}{}xmg;
36             }
37 8         18 $$html_ref =~ s{\x7f}{}xmg;
38             }
39              
40             sub _score {
41 8     8   9 my $self = shift;
42 8         8 my $c = shift;
43 8         6 my $html_ref = shift;
44 8         43 my $root = HTML::TreeBuilder->new_from_content( $$html_ref );
45 8         5879 my $result = HTML::Feature::Result->new;
46              
47 8         8 my $data;
48              
49 8 50       26 if ( my $title = $root->find("title") ) {
50 8         258 $result->title($title->as_text);
51             }
52              
53 8 50       186 if ( my $desc = $root->look_down( _tag => 'meta', name => 'description' ) )
54             {
55 8         447 my $string = $desc->attr('content');
56 8         65 $string =~ s{
}{}xms;
57 8         19 $result->desc($string);
58             }
59              
60 8         31 my $i = 0;
61 8         8 my @ratio;
62             my @depth;
63 0         0 my @order;
64 8 50       18 my $CACHE = $c->{cache} ? {} : undef;
65 8         37 for my $node ( $root->look_down( "_tag", qr/body|center|td|div/i ) ) {
66 16         537 my $html_length = bytes::length( $node->as_HTML );
67 16         4523 my $text = $node->as_text;
68 16         253 my $text_length = bytes::length($text);
69 16         44 my $text_ration = $text_length / ( $html_length + 0.001 );
70              
71             next
72             if ( $c->{max_bytes}
73             and $c->{max_bytes} =~ /^[\d]+$/
74 16 0 0     34 && $text_length > $c->{max_bytes} );
      33        
75             next
76             if ( $c->{min_bytes}
77             and $c->{min_bytes} =~ /^[\d]+$/
78 16 0 33     34 and $text_length < $c->{min_bytes} );
      33        
79              
80 16         16 my $a_count = 0;
81 16         9 my $a_length = 0;
82 16         16 my $option_count = 0;
83 16         14 my $option_length = 0;
84 16         31 my %node_hash = (
85             text => '',
86             a_length => 0,
87             short_string_length => 0
88             );
89              
90 16         28 $self->_walk_tree( $node, \%node_hash, $CACHE );
91              
92 16   50     57 $node_hash{a_length} ||= 0;
93 16   50     36 $node_hash{option_length} ||= 0;
94 16   50     37 $node_hash{short_string_length} ||= 0;
95 16   33     19 $node_hash{text} ||= $text;
96              
97 16 50       74 next if $node_hash{text} !~ /[^ ]+/;
98              
99 16         26 $data->[$i]->{text} = $node_hash{text};
100              
101             push(
102             @ratio,
103             (
104             $text_length -
105             $node_hash{a_length} -
106             $node_hash{option_length} -
107             $node_hash{short_string_length}
108 16         34 ) * $text_ration
109             );
110 16         32 push( @depth, $node->depth() );
111              
112 16         102 $data->[$i]->{element} = $node;
113              
114 16         25 $i++;
115             }
116 8         33 undef $CACHE;
117              
118 8         15 for ( 0 .. $i ) {
119 24         40 push( @order, log( $i - $_ + 1 ) );
120             }
121              
122 8         43 my %ratio = statshash @ratio;
123 8         1373 my %depth = statshash @depth;
124 8         1153 my %order = statshash @order;
125              
126             # avoid memory leak
127 8 100       1436 $root->delete() unless $c->{element_flag};
128              
129             my @sorted =
130 24         31 sort { $data->[$b]->{score} <=> $data->[$a]->{score} }
131             map {
132              
133 8         339 my $ratio_std =
134 24   100     75 ( ($ratio[$_] || 0) - ($ratio{mean} || 0) ) / ( $ratio{stddev} + 0.001 );
      50        
135             my $depth_std =
136 24   100     65 ( ($depth[$_] || 0) - ($depth{mean} || 0) ) / ( $depth{stddev} + 0.001 );
      50        
137             my $order_std =
138 24   100     79 ( ($order[$_] || 0) - ($order{mean} || 0) ) / ( $order{stddev} + 0.001 );
      50        
139              
140 24         26 $data->[$_]->{score} = $ratio_std + $depth_std + $order_std;
141 24         37 $_;
142             } ( 0 .. $i );
143 8         54 $data->[ $sorted[0] ]->{text} =~ s/ $//s;
144              
145 8         26 $result->text($data->[ $sorted[0] ]->{text});
146              
147 8 100       45 if ($c->{element_flag}) {
148 2         4 $result->root($root);
149 2         7 $result->element($data->[ $sorted[0] ]->{element});
150             }
151              
152 8 50       25 if ( $c->{enc_type} ) {
153 8         16 $result->title( Encode::encode( $c->{enc_type}, $result->title ) );
154 8         241 $result->desc( Encode::encode( $c->{enc_type}, $result->desc ) );
155 8         129 $result->text( Encode::encode( $c->{enc_type}, $result->text ) );
156             }
157              
158 8         247 return $result;
159             }
160              
161             sub _walk_tree {
162 40     40   126 my $self = shift;
163 40         24 my $node = shift;
164 40         27 my $node_hash_ref = shift;
165 40         28 my $CACHE = shift;
166              
167 40         41 my $data = $CACHE->{ $node };
168 40 100       53 if ( ! $data) {
169 24         45 $data = {
170             text => '',
171             a_length => 0,
172             short_string_length => 0,
173             };
174              
175 24 100       37 if ( ref $node ) {
176 16         27 my $text_len = bytes::length( $node->as_text );
177 16         238 my $tag = $node->tag;
178 16         58 $data->{text_length} = $text_len;
179              
180 16 50       53 if ( $tag =~ /p|br|hr|tr|ul|li|ol|dl|dd/ ) {
181 0         0 $data->{text} = "\n";
182             }
183 16 50       37 if ( $tag =~ /a|dt|th|option/) {
184 0         0 $data->{a_length} += $text_len;
185             }
186              
187 16 50       26 if ( $text_len < 20 ) {
188 0         0 $data->{short_string_length} += $text_len;
189             }
190             }
191             else {
192 8         15 $data->{text} = $node . " ";
193             }
194              
195 24         37 $CACHE->{ $node } = $data;
196             }
197              
198 40         64 $node_hash_ref->{text} .= $data->{text};
199 40         36 $node_hash_ref->{a_length} += $data->{a_length};
200 40         79 $node_hash_ref->{short_string_length} += $data->{short_string_length};
201              
202 40 100       75 if (ref $node) {
203             $self->_walk_tree( $_, $node_hash_ref, $CACHE )
204 24         37 for $node->content_list();
205             }
206              
207             }
208              
209             1;
210              
211             __END__