File Coverage

blib/lib/HTML/Feature.pm
Criterion Covered Total %
statement 78 113 69.0
branch 14 34 41.1
condition 5 10 50.0
subroutine 18 24 75.0
pod 8 8 100.0
total 123 189 65.0


line stmt bran cond sub pod time code
1             package HTML::Feature;
2 5     5   127483 use strict;
  5         8  
  5         119  
3 5     5   16 use warnings;
  5         5  
  5         123  
4 5     5   17 use vars qw($VERSION $UserAgent $engine @EXPORT_OK);
  5         8  
  5         267  
5 5     5   16 use Exporter qw(import);
  5         5  
  5         112  
6 5     5   14 use Carp;
  5         5  
  5         269  
7 5     5   2055 use HTTP::Response::Encoding;
  5         1620  
  5         131  
8 5     5   2005 use Encode::Guess;
  5         54197  
  5         15  
9 5     5   264 use List::Util qw(first);
  5         7  
  5         463  
10 5     5   23 use Scalar::Util qw(blessed);
  5         8  
  5         291  
11 5     5   2124 use UNIVERSAL::require;
  5         4412  
  5         36  
12 5     5   2244 use URI;
  5         22758  
  5         42  
13              
14             $VERSION = '2.00004_00';
15             @EXPORT_OK = qw(feature);
16              
17             sub new {
18 7     7 1 1554 my $class = shift;
19 7         17 my %arg = @_;
20 7   33     39 $class = ref $class || $class;
21 7         15 my $self = bless \%arg, $class;
22              
23 7   100     47 $self->{enc_type} ||= 'utf8';
24              
25 7         14 return $self;
26             }
27              
28             sub parse {
29 8     8 1 999 my $self = shift;
30 8         10 my $obj = shift;
31              
32 8 50       27 if (! $obj) {
33 0         0 croak('Usage: parse( $uri | $http_response | $html_ref )');
34             }
35              
36 8         21 my $pkg = blessed($obj);
37 8 50       17 if (! $pkg) {
38 8 50       24 if (my $ref = ref $obj) {
39             # if it's a scalar reference, then we've been passed a piece of
40             # HTML code.
41 8 50       25 if ($ref eq 'SCALAR') {
42 8         25 return $self->parse_html( $obj, @_ );
43             }
44              
45             # Otherwise we don't know how to handle
46 0         0 croak('Usage: parse( $uri | $http_response | $html_ref )');
47             }
48              
49             # We seemed to have an unblessed scalar. Assume it's a URI
50 0         0 $pkg = 'URI';
51 0         0 $obj = URI->new($obj);
52             }
53              
54             # If it's an object, then we can handle URI or HTTP::Response
55 0 0       0 if ($pkg->isa('URI')) {
    0          
56 0         0 return $self->parse_url( $obj, @_ );
57             } elsif ($pkg->isa('HTTP::Response')) {
58 0         0 return $self->parse_response( $obj, @_ );
59             } else {
60 0         0 croak('Usage: parse( $uri | $http_response | $html_ref )');
61             }
62             }
63              
64             sub parse_url {
65 0     0 1 0 my $self = shift;
66 0         0 my $url = shift;
67 0         0 my $ua = $self->_user_agent();
68 0         0 my $res = $ua->get($url);
69 0         0 $self->parse_response($res, @_);
70             }
71              
72             sub parse_response {
73 0     0 1 0 my $self = shift;
74 0         0 my $res = shift;
75 0         0 my $content = $self->_decode_response($res);
76 0         0 $self->_run( \$content, @_ );
77             }
78              
79             sub parse_html {
80 8     8 1 9 my $self = shift;
81 8         11 my $html = shift;
82 8 50       18 my $html_ref = ref $html ? $html : \$html;
83 8         20 $self->_decode_htmlref( $html_ref );
84 8         170 $self->_run( $html_ref, @_ );
85             }
86              
87             sub engine
88             {
89 8     8 1 8 my $self = shift;
90 8         12 my $engine = $self->{engine};
91 8 100       44 if (! $engine) {
92 6 50       13 my $engine_module = $self->{engine} ? $self->{engine} : 'TagStructure';
93 6         13 my $class = __PACKAGE__ . '::Engine::' . $engine_module;
94 6 50       44 $class->require or die $@;
95 6         153 $engine = $class->new;
96 6         16 $self->{engine} = $engine;
97             }
98 8         28 return $engine;
99             }
100              
101             sub _run {
102 8     8   14 my $self = shift;
103 8         9 my $html_ref = shift;
104 8   100     29 my $opts = shift || {};
105              
106 8 100       28 local $self->{element_flag} = exists $opts->{element_flag} ? $opts->{element_flag} : $self->{element_flag};
107 8 50       17 local $self->{cache} = exists $opts->{cache} ? $opts->{cache} : 1;
108 8         22 $self->engine->run($self, $html_ref);
109             }
110              
111             sub _decode_response
112             {
113 0     0   0 my $self = shift;
114 0         0 my $res = shift;
115              
116 0         0 my @encoding = (
117             $res->encoding,
118             # XXX - falling back to latin-1 may be risky. See Data::Decode
119             # could be multiple because HTTP response and META might be different
120             ( $res->header('Content-Type') =~ /charset=([\w\-]+)/g ),
121             "latin-1",
122             );
123             my $encoding =
124 0 0   0   0 first { defined $_ && Encode::find_encoding($_) } @encoding;
  0         0  
125 0         0 return Encode::decode( $encoding, $res->content );
126             }
127              
128             sub _decode_htmlref
129             {
130 8     8   10 my $self = shift;
131 8         11 my $html_ref = shift;
132              
133 8         21 local $Encode::Guess::NoUTFAutoGuess = 1;
134 8         33 my $guess =
135             Encode::Guess::guess_encoding( $$html_ref,
136             ( 'shiftjis', 'euc-jp', '7bit-jis', 'utf8' ) );
137 8 50       17814 unless ( ref $guess ) {
138 0         0 $$html_ref = Encode::decode( "latin-1", $$html_ref );
139             } else {
140 8         13 eval { $$html_ref = $guess->decode($$html_ref); };
  8         32  
141             }
142             }
143              
144             sub _user_agent {
145 0     0   0 my $self = shift;
146 0         0 require LWP::UserAgent;
147 0   0     0 $UserAgent ||= LWP::UserAgent->new();
148 0 0       0 $self->{http_proxy} and $UserAgent->proxy( ['http'], $self->{http_proxy} );
149 0         0 return $UserAgent;
150             }
151              
152             sub feature {
153 3     3 1 3494 my $self = __PACKAGE__->new;
154 3         9 my $result = $self->parse(@_);
155 3         6 my %ret = (
156             text => $result->text,
157             title => $result->title,
158             desc => $result->desc,
159             element => $result->element
160             );
161 3 50       38 return wantarray ? %ret : $ret{text};
162             }
163              
164             sub extract {
165 0     0 1   warn
166             "HTML::Feature::extract() has been deprecated. Use HTML::Feature::parse() instead";
167 0           my $self = shift;
168 0           my %args = @_;
169 0 0         my $result = $self->parse( $args{string} ? \$args{string} : $args{url} );
170 0           my $ret = {
171             title => $result->title,
172             description => $result->desc,
173             block => [ { contents => $result->text } ],
174             };
175 0           return $ret;
176             }
177              
178             1;
179              
180             __END__