File Coverage

blib/lib/WWW/FreeProxyListsCom.pm
Criterion Covered Total %
statement 78 102 76.4
branch 20 48 41.6
condition 9 15 60.0
subroutine 12 14 85.7
pod 3 3 100.0
total 122 182 67.0


line stmt bran cond sub pod time code
1             package WWW::FreeProxyListsCom;
2              
3 1     1   237614 use warnings;
  1         2  
  1         26  
4 1     1   5 use strict;
  1         1  
  1         41  
5              
6             our $VERSION = '1.001';
7              
8 1     1   4 use Carp;
  1         5  
  1         48  
9 1     1   4 use URI;
  1         1  
  1         23  
10 1     1   4 use WWW::Mechanize;
  1         2  
  1         15  
11 1     1   4 use HTML::TokeParser::Simple;
  1         1  
  1         23  
12 1     1   4 use HTML::Entities;
  1         1  
  1         53  
13 1     1   5 use Devel::TakeHashArgs;
  1         1  
  1         45  
14 1     1   4 use base 'Class::Accessor::Grouped';
  1         2  
  1         1450  
15             __PACKAGE__->mk_group_accessors( simple => qw/
16             error
17             mech
18             debug
19             list
20             filtered_list
21             /);
22              
23             sub new {
24 1     1 1 227 my $self = bless {}, shift;
25 1 50       10 get_args_as_hash(\@_, \my %args, { timeout => 30 } )
26             or croak $@;
27              
28 1   33     51 $args{mech} ||= WWW::Mechanize->new(
29             timeout => $args{timeout},
30             agent => 'Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.12)'
31             .' Gecko/20080207 Ubuntu/7.10 (gutsy) Firefox/2.0.0.12',
32             );
33              
34 1         70997 $self->mech( $args{mech} );
35 1         342 $self->debug( $args{debug} );
36              
37 1         178 return $self;
38             }
39              
40             sub get_list {
41 6     6 1 154104 my $self = shift;
42              
43 6         5800 $self->$_(undef) for qw(error list);
44              
45 6 50       474 get_args_as_hash(\@_, \my %args, {
46             type => 'elite',
47             max_pages => 1,
48             }
49             ) or croak $@;
50              
51 36         101 my %page_for = (
52             non_anonymous => 'non-anonymous',
53 6         156 map { $_ => $_ } qw(
54             elite
55             anonymous
56             https
57             standard
58             us
59             socks
60             ),
61             );
62              
63 6 50       29 exists $page_for{ $args{type} }
64             or croak 'Invalid `type` argument was passed to fetch(). '
65             . 'Must be one of' . join q|, |, keys %page_for;
66              
67 6         29 my $mech = $self->mech;
68 6         15 my $page_type = $page_for{ $args{type} };
69 6         53 my $uri = URI->new(
70             "http://www.freeproxylists.com/$page_type.html"
71             );
72              
73 6 50       17363 $mech->get($uri)->is_success
74             or return $self->_set_error($mech,'net');
75              
76 6 50       2583746 $page_type eq 'anonymous'
77             and $page_type = 'anon';
78 6 50       32 $page_type eq 'non-anonymous'
79             and $page_type = 'nonanon';
80              
81             # little tweaking to get the URI to the file normally loaded with AJAX
82 48         141916 my @links = map {
83 6         67 "http://www.freeproxylists.com/load_${page_type}_" .
84             ($_->url =~ m|([^/]+$)|)[0]
85             } $mech->find_all_links(text_regex => qr/^detailed list #\d+/i);
86              
87 6 50       105 $args{max_pages}
88             and @links = splice @links, 0, $args{max_pages};
89              
90 6 50       49 $self->debug
91             and print "Going to fetch data from: \n" . join "\n", @links,'';
92              
93 6         11 my @proxies;
94 6         17 for ( @links ) {
95 6 50       35 unless ( $mech->get($_)->is_success ) {
96 0 0       0 $self->debug
97             and carp 'Network error: ' . $mech->res->status_line;
98 0         0 next;
99             }
100              
101 6 50       6326061 my $list_ref = $self->_parse_list( $mech->res->content )
102             or next;
103              
104 6         1612 push @proxies, @$list_ref;
105             }
106              
107 6         127 return $self->list( \@proxies );
108             }
109              
110             sub filter {
111 0     0 1 0 my $self = shift;
112              
113 0         0 $self->$_(undef) for qw(error filtered_list);
114              
115 0 0       0 get_args_as_hash( \@_, \my %args)
116             or croak $@;
117              
118 0         0 my %valid_filters;
119 0         0 @valid_filters{ qw(ip port is_https country last_test latency) }
120             = (1) x 5;
121              
122 0 0       0 grep { not exists $valid_filters{$_} } keys %args
  0         0  
123             and return $self->_set_error(
124             'Invalid filter specified, valid ones are: '.
125             join q|, |, keys %valid_filters
126             );
127              
128 0 0       0 my $list_ref = $self->list
129             or return $self->_set_error(
130             'Proxy list seems to be undefined, did you call get_list() first?'
131             );
132              
133 0         0 my @filtered;
134 0         0 foreach my $proxy_ref ( @$list_ref ) {
135 0         0 my $is_good = 0;
136 0         0 for ( keys %args ) {
137 0 0       0 if ( ref $args{$_} eq 'Regexp' ) {
138 0 0       0 $proxy_ref->{$_} =~ /$args{$_}/
139             and $is_good++;
140             }
141             else {
142 0 0       0 $proxy_ref->{$_} eq $args{$_}
143             and $is_good++;
144             }
145             }
146              
147 0 0       0 $is_good == keys %args
148             and push @filtered, { %$proxy_ref };
149             }
150 0         0 return $self->filtered_list( \@filtered );
151             }
152              
153              
154             sub _parse_list {
155 6     6   169 my ( $self, $content ) = @_;
156              
157             # EVIL EVIL EVIL!! WEEE \o/
158 6         2640 ( $content ) = $content =~ m|(.+?)|s;
159 6         19590 decode_entities $content;
160              
161 6         104 my $parser = HTML::TokeParser::Simple->new( \$content );
162              
163 6         1315 my %cells;
164 6         66 @cells{ 1..6 } = qw(ip port is_https latency last_test country);
165 6         1243 my %nav;
166 6         41 @nav{ qw(get_data level data_cell) } = (0) x 3;
167              
168 6         189 my @data;
169             my %current;
170 6         34 while ( my $t = $parser->get_token ) {
171 92628 100 100     10960016 if ( $t->is_start_tag('tr') ) {
    100 100        
    100          
    100          
172 4632         66110 @nav{ qw(get_data level) } = (1, 1);
173             }
174             elsif ( $nav{get_data} == 1 and $t->is_start_tag('td') ) {
175 27726         710518 $nav{level} = 2;
176 27726         120331 $nav{data_cell}++;
177             }
178             elsif ( $nav{data_cell} and $t->is_text ) {
179 27750         438706 $current{ $cells{ $nav{data_cell} } } = $t->as_is;
180             }
181             elsif ( $t->is_end_tag('tr') ) {
182 4632         123433 @nav{ qw(level get_data data_cell) } = ( 3, 0, 0 );
183              
184 4632 100       12409 next unless keys %current;
185              
186 27756   66     127943 $current{ $_ } = 'N/A'
187 4626         9964 for grep { !defined $current{$_} or !length $current{$_} }
188             values %cells;
189              
190 4626         27923 push @data, { %current };
191 4626         27276 %current = ();
192             }
193             }
194              
195 6         219 shift @data; # quick and dirty fix to rid of bad data.
196 6         129 return \@data;
197             }
198              
199             sub _set_error {
200 0     0     my ( $self, $mech_or_error, $type ) = @_;
201 0 0 0       if ( defined $type and $type eq 'net' ) {
202 0           $self->error('Network error: ' . $mech_or_error->res->status_line);
203             }
204             else {
205 0           $self->error( $mech_or_error );
206             }
207 0           return;
208             }
209              
210             1;
211             __END__