File Coverage

blib/lib/WWW/FreeProxyListsCom.pm
Criterion Covered Total %
statement 107 118 90.6
branch 39 58 67.2
condition 11 15 73.3
subroutine 16 17 94.1
pod 4 4 100.0
total 177 212 83.4


line stmt bran cond sub pod time code
1             package WWW::FreeProxyListsCom;
2              
3 6     6   481130 use warnings;
  6         54  
  6         183  
4 6     6   26 use strict;
  6         9  
  6         199  
5              
6             our $VERSION = '1.006';
7              
8 6     6   28 use Carp;
  6         10  
  6         312  
9 6     6   2001 use Data::Dumper;
  6         21649  
  6         311  
10 6     6   2360 use URI;
  6         28544  
  6         154  
11 6     6   3279 use WWW::Mechanize;
  6         609570  
  6         247  
12 6     6   2330 use HTML::TokeParser::Simple;
  6         33331  
  6         150  
13 6     6   33 use HTML::Entities;
  6         13  
  6         274  
14 6     6   1846 use Devel::TakeHashArgs;
  6         1929  
  6         248  
15 6     6   43 use base 'Class::Accessor::Grouped';
  6         13  
  6         3532  
16              
17             __PACKAGE__->mk_group_accessors( simple => qw/
18             error
19             mech
20             debug
21             list
22             filtered_list
23             url_list
24             /);
25              
26             sub new {
27 6     6 1 41970 my $self = bless {}, shift;
28              
29 6 50       44 get_args_as_hash(
30             \@_, \my %args,
31             {
32             timeout => 30,
33             }
34             ) or croak $@;
35              
36             $args{mech} ||= WWW::Mechanize->new(
37             timeout => $args{timeout},
38 6   33     319 agent => 'Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.12)'
39             .' Gecko/20080207 Ubuntu/7.10 (gutsy) Firefox/2.0.0.12',
40             );
41              
42 6         58003 $self->mech( $args{mech} );
43 6         1388 $self->debug( $args{debug} );
44              
45 6         796 return $self;
46             }
47             sub get_list {
48 16     16 1 89402 my $self = shift;
49              
50 16         1369 $self->$_(undef) for qw(error list);
51              
52 16 50       1558 get_args_as_hash(\@_, \my %args, {
53             type => 'elite',
54             max_pages => 1,
55             }
56             ) or croak $@;
57              
58             my %page_for = (
59             non_anonymous => 'non-anonymous',
60 16         448 map { $_ => $_ } qw(
  144         301  
61             elite
62             anonymous
63             https
64             standard
65             socks
66             us
67             uk
68             ca
69             fr
70             ),
71             );
72              
73             exists $page_for{ $args{type} }
74 16 100       270 or croak 'Invalid `type` argument was passed to fetch(). '
75             . 'Must be one of' . join q|, |, keys %page_for;
76              
77 15         51 my $mech = $self->mech;
78 15         42 my $page_type = $page_for{ $args{type} };
79 15         48 my $url = $self->_url;
80              
81 15         115 my $uri = URI->new(
82             "$url/$page_type.html"
83             );
84              
85 15 50       27854 $mech->get($uri)->is_success
86             or return $self->_set_error($mech, 'net');
87              
88 13 100       3560670 $page_type eq 'anonymous'
89             and $page_type = 'anon';
90 13 50       54 $page_type eq 'non-anonymous'
91             and $page_type = 'nonanon';
92              
93             # little tweaking to get the URI to the file normally loaded with AJAX
94             my @links = map {
95 13         123 "http://www.freeproxylists.com/load_${page_type}_" .
  208         320302  
96             ($_->url =~ m|([^/]+$)|)[0]
97             } $mech->find_all_links(text_regex => qr/^detailed list #\d+/i);
98              
99             $args{max_pages}
100 13 50       176 and @links = splice @links, 0, $args{max_pages};
101              
102 13 50       109 $self->debug
103             and print "Going to fetch data from: \n" . join "\n", @links,'';
104              
105 13         35 my @proxies;
106 13         34 for ( @links ) {
107 13 50       71 unless ( $mech->get($_)->is_success ) {
108 0 0       0 $self->debug
109             and carp 'Network error: ' . $mech->res->status_line;
110 0         0 next;
111             }
112              
113 13 50       6130647 my $list_ref = $self->_parse_list( $mech->res->content )
114             or next;
115              
116 13         732 push @proxies, @$list_ref;
117             }
118              
119 13         365 return $self->list( \@proxies );
120             }
121             sub filter {
122 9     9 1 1913 my $self = shift;
123              
124 9         54 $self->$_(undef) for qw(error filtered_list);
125              
126 9 50       267 get_args_as_hash( \@_, \my %args)
127             or croak $@;
128              
129 9         177 my %valid_filters;
130 9         28 @valid_filters{ qw(ip port is_https country last_test latency) }
131             = (1) x 5;
132              
133 9 50       20 grep { not exists $valid_filters{$_} } keys %args
  8         23  
134             and return $self->_set_error(
135             'Invalid filter specified, valid ones are: '.
136             join q|, |, keys %valid_filters
137             );
138              
139 9 50       25 my $list_ref = $self->list
140             or return $self->_set_error(
141             'Proxy list seems to be undefined, did you call get_list() first?'
142             );
143              
144 9         12 my @filtered;
145 9         16 foreach my $proxy_ref ( @$list_ref ) {
146 234         271 my $is_good = 0;
147 234         321 for ( keys %args ) {
148 208 100       281 if ( ref $args{$_} eq 'Regexp' ) {
149 26 100       88 $proxy_ref->{$_} =~ /$args{$_}/
150             and $is_good++;
151             }
152             else {
153 182 100       305 $proxy_ref->{$_} eq $args{$_}
154             and $is_good++;
155             }
156             }
157              
158 234 100       699 $is_good == keys %args
159             and push @filtered, { %$proxy_ref };
160             }
161 9         75 return $self->filtered_list( \@filtered );
162             }
163             sub urls {
164 0     0 1 0 my ($self) = @_;
165              
166 0 0       0 my $proxies = $self->filtered_list ? $self->filtered_list : $self->list;
167              
168 0 0       0 if (! @$proxies){
169 0         0 $self->_set_error(
170             'Proxy list seems to be undefined, did you call get_list() first?'
171             );
172             }
173              
174 0         0 my @urls;
175              
176 0         0 for (@$proxies){
177 0 0       0 my $protocol = $_->{is_https} eq 'true'
178             ? 'https'
179             : 'http';
180              
181 0         0 push @urls, join '', "$protocol://", join ':', @$_{qw(ip port)};
182             }
183              
184 0         0 return $self->url_list(\@urls);
185             }
186             sub _parse_list {
187 13     13   364 my ( $self, $content ) = @_;
188              
189             # EVIL EVIL EVIL!! WEEE \o/
190 13         1051 ( $content ) = $content =~ m|(.+?)|s;
191 13         5015 decode_entities $content;
192              
193 13         139 my $parser = HTML::TokeParser::Simple->new( \$content );
194              
195 13         2283 my %cells;
196 13         108 @cells{ 1..6 } = qw(ip port is_https latency last_test country);
197 13         26 my %nav;
198 13         70 @nav{ qw(get_data level data_cell) } = (0) x 3;
199              
200 13         28 my @data;
201             my %current;
202 13         79 while ( my $t = $parser->get_token ) {
203 60074 100 100     1943620 if ( $t->is_start_tag('tr') ) {
    100 100        
    100          
    100          
204 3005         33142 @nav{ qw(get_data level) } = (1, 1);
205             }
206             elsif ( $nav{get_data} == 1 and $t->is_start_tag('td') ) {
207 17887         338316 $nav{level} = 2;
208 17887         41085 $nav{data_cell}++;
209             }
210             elsif ( $nav{data_cell} and $t->is_text ) {
211 17939         149834 $current{ $cells{ $nav{data_cell} } } = $t->as_is;
212             }
213             elsif ( $t->is_end_tag('tr') ) {
214 3005         57624 @nav{ qw(level get_data data_cell) } = ( 3, 0, 0 );
215              
216 3005 100       5186 next unless keys %current;
217              
218             $current{ $_ } = 'N/A'
219 2992   66     5184 for grep { !defined $current{$_} or !length $current{$_} }
  17952         44631  
220             values %cells;
221              
222 2992         11576 push @data, { %current };
223 2992         9273 %current = ();
224             }
225             }
226              
227 13         415 shift @data; # quick and dirty fix to rid of bad data.
228 13         180 return \@data;
229             }
230             sub _set_error {
231 2     2   13 my ( $self, $mech_or_error, $type ) = @_;
232 2 100 66     10 if ( defined $type and $type eq 'net' ) {
233 1         6 $self->error('Network error: ' . $mech_or_error->res->status_line);
234             }
235             else {
236 1         5 $self->error( $mech_or_error );
237             }
238 2         21 return;
239             }
240             sub _url {
241 16     16   45 my ($self, $url) = @_;
242 16 100       45 $self->{url} = $url if defined $url;
243 16 100       64 return defined $self->{url} ? $self->{url} : 'http://freeproxylists.com';
244             }
245             1;
246             __END__