File Coverage

blib/lib/WWW/Scrape/FindaGrave.pm
Criterion Covered Total %
statement 79 88 89.7
branch 29 52 55.7
condition 6 12 50.0
subroutine 7 7 100.0
pod 2 2 100.0
total 123 161 76.4


line stmt bran cond sub pod time code
1             package WWW::Scrape::FindaGrave;
2              
3 3     3   591613 use warnings;
  3         4  
  3         73  
4 3     3   9 use strict;
  3         2  
  3         40  
5 3     3   1194 use WWW::Mechanize::GZip;
  3         300824  
  3         77  
6 3     3   18 use LWP::UserAgent;
  3         2  
  3         37  
7 3     3   1247 use HTML::SimpleLinkExtor;
  3         13033  
  3         1760  
8              
9             =head1 NAME
10              
11             WWW::Scrape::FindaGrave - Scrape the FindaGrave site
12              
13             =head1 VERSION
14              
15             Version 0.01
16              
17             =cut
18              
19             our $VERSION = '0.01';
20              
21             =head1 SYNOPSIS
22              
23             use HTTP::Cache::Transparent; # be nice
24             use WWW::Scape::FindaGrave;
25              
26             HTTP::Cache::Transparent::init({
27             BasePath => '/var/cache/findagrave'
28             });
29             my $f = WWW::Scrape::FindaGrave->new({
30             firstname => 'John',
31             lastname => 'Smith',
32             country => 'England',
33             date_of_death => 1862
34             });
35              
36             while(my $url = $f->get_next_entry()) {
37             print "$url\n";
38             }
39             }
40              
41             =head1 SUBROUTINES/METHODS
42              
43             =head2 new
44              
45             Creates a WWW::Scrape::FindaGrave object.
46              
47             It takes two manadatory arguments firstname and lastname.
48              
49             Also one of either date_of_birth and date_of_death must be given
50              
51             There are two optional arguments: middlename and mech. Mech is a pointer
52             to an object such as L. If not given it will be created.
53             =cut
54              
55             sub new {
56 2     2 1 87753 my $proto = shift;
57 2   33     13 my $class = ref($proto) || $proto;
58              
59 2 50       5 return unless(defined($class));
60              
61 2 50       8 my %args = (ref($_[0]) eq 'HASH') ? %{$_[0]} : @_;
  2         9  
62              
63 2 50       8 die "First name is not optional" unless($args{'firstname'});
64 2 50       5 die "Last name is not optional" unless($args{'lastname'});
65             die "You must give one of the date of birth or death"
66 2 50 66     7 unless($args{'date_of_death'} || $args{'date_of_birth'});
67              
68             my $rc = {
69             mech => $args{'mech'} || WWW::Mechanize::GZip->new(),
70             date_of_birth => $args{'date_of_birth'},
71             date_of_death => $args{'date_of_death'},
72             country => $args{'country'},
73             firstname => $args{'firstname'},
74             middlename => $args{'middlename'},
75 2   33     22 lastname => $args{'lastname'},
76             };
77              
78 2         19450 my $resp = $rc->{'mech'}->get('http://www.findagrave.com/cgi-bin/fg.cgi');
79 2 50       1215930 unless($resp->is_success()) {
80 0         0 die $resp->status_line;
81             }
82              
83             my %fields = (
84             GSfn => $rc->{'firstname'},
85 2         28 GSln => $rc->{'lastname'},
86             GSiman => 0,
87             GSpartial => 0,
88             );
89              
90 2 100       9 if($rc->{date_of_death}) {
    50          
91 1         2 $fields{GSdy} = $rc->{date_of_death};
92 1         2 $fields{GSdyrel} = 'in';
93             } elsif($rc->{'date_of_birth'}) {
94 1         2 $fields{GSby} = $rc->{date_of_birth};
95 1         2 $fields{GSbyrel} = 'in';
96             }
97              
98 2 50       7 if($rc->{'middlename'}) {
99 0         0 $fields{GSmn} = $rc->{'middlename'};
100             }
101              
102             # Don't enable this. If we know the date of birth but findagrave
103             # doesn't, findagrave will miss the match. Of course, the downside
104             # of not doing this is that you will get false positives. It's really
105             # a problem with findagrave.
106             # if($date_of_birth) {
107             # $fields{GSby} = $date_of_birth;
108             # $fields{GSbyrel} = 'in';
109             # }
110              
111 2 100       7 if($rc->{'country'}) {
112 1 50       3 if($rc->{'country'} eq 'United States') {
113 0         0 $fields{GScntry} = 'The United States';
114             } else {
115 1         2 $fields{GScntry} = $rc->{'country'};
116             }
117             }
118              
119 2         13 $resp = $rc->{'mech'}->submit_form(
120             form_number => 1,
121             fields => \%fields,
122             );
123 2 50       1085013 unless($resp->is_success) {
124 0         0 die $resp->status_line;
125             }
126 2 50       20 if($resp->content =~ /Sorry, there are no records in the Find A Grave database matching your query\./) {
127 0         0 $rc->{'matches'} = 0;
128 0         0 return bless $rc, $class;
129             }
130 2 50       36 if($resp->content =~ /(\d+)<\/B>\s+total matches/mi) {
131 2         77 $rc->{'matches'} = $1;
132 2 50       8 return bless $rc, $class if($rc->{'matches'} == 0);
133             }
134              
135             # Shows 40 per page
136 2         6 $rc->{'base'} = $resp->base();
137 2         422 $rc->{'ua'} = LWP::UserAgent->new(
138             keep_alive => 1,
139             agent => __PACKAGE__,
140             from => 'foo@example.com',
141             timeout => 10,
142             );
143              
144 2         3358 $rc->{'ua'}->env_proxy(1);
145 2         148 $rc->{'index'} = 0;
146 2         4 $rc->{'resp'} = $resp;
147              
148 2         24 return bless $rc, $class;
149             }
150              
151             =head2 get_next_entry
152              
153             Returns the next match as a URL to the Find-A-Grave page.
154              
155             =cut
156              
157             sub get_next_entry
158             {
159 2     2 1 1144 my $self = shift;
160              
161 2 50       12 return if($self->{'matches'} == 0);
162              
163 2         2 my $rc = pop @{$self->{'results'}};
  2         5  
164 2 50       6 return $rc if $rc;
165              
166 2 100       6 return if($self->{'index'} >= $self->{'matches'});
167              
168 1         2 my $firstname = $self->{'firstname'};
169 1         1 my $lastname = $self->{'lastname'};
170 1         2 my $date_of_death = $self->{'date_of_death'};
171 1         1 my $date_of_birth = $self->{'date_of_birth'};
172              
173 1         4 my $base = $self->{'resp'}->base();
174 1         217 my $e = HTML::SimpleLinkExtor->new($base);
175 1         214 $e->remove_tags('img', 'script');
176 1         8 $e->parse($self->{'resp'}->content);
177              
178 1         1983 foreach my $link ($e->links) {
179 29         6718 my $match = 0;
180 29 50       24 if($date_of_death) {
    0          
181 29 100       86 if($link =~ /www.findagrave.com\/cgi-bin\/fg.cgi\?.*&GSln=\Q$lastname\E.*&GSfn=\Q$firstname\E.*&GSdy=\Q$date_of_death\E.*&GRid=\d+/i) {
182 1         7 $match = 1;
183             }
184             } elsif(defined($date_of_birth)) {
185 0 0       0 if($link =~ /www.findagrave.com\/cgi-bin\/fg.cgi\?.*&GSln=\Q$lastname\E.*&GSfn=\Q$firstname\E.*&GSby=\Q$date_of_birth\E.*&GRid=\d+/i) {
186 0         0 $match = 1;
187             }
188             }
189 29 50 66     123 if($match && $self->{'country'}) {
190 1         2 my $country = $self->{'country'};
191 1 50       3 if($self->{'resp'}->content !~ /\Q$country\E/i) {
192 0         0 $match = 0;
193             }
194             }
195 29 100       85 if($match) {
196 1         1 push @{$self->{'results'}}, $link;
  1         2  
197             }
198             }
199 1         4 $self->{'index'}++;
200 1 50       3 if($self->{'index'} <= $self->{'matches'}) {
201 1         2 my $index = $self->{'index'};
202 1         2 $self->{'resp'} = $self->{'ua'}->get("$base&sr=$index");
203             }
204              
205 1         338051 return pop @{$self->{'results'}};
  1         10  
206             }
207              
208             =head1 AUTHOR
209              
210             Nigel Horne, C<< >>
211              
212             =head1 BUGS
213              
214             Please report any bugs or feature requests to C,
215             or through the web interface at
216             L.
217             I will be notified, and then you'll
218             automatically be notified of progress on your bug as I make changes.
219              
220             =head1 SEE ALSO
221              
222             L
223             L
224              
225             =head1 SUPPORT
226              
227             You can find documentation for this module with the perldoc command.
228              
229             perldoc WWW::Scape::FindaGrave
230              
231              
232             You can also look for information at:
233              
234             =over 4
235              
236             =item * RT: CPAN's request tracker
237              
238             L
239              
240             =item * AnnoCPAN: Annotated CPAN documentation
241              
242             L
243              
244             =item * CPAN Ratings
245              
246             L
247              
248             =item * Search CPAN
249              
250             L
251              
252             =back
253              
254              
255             =head1 LICENSE AND COPYRIGHT
256              
257             Copyright 2016 Nigel Horne.
258              
259             This program is released under the following licence: GPL
260              
261              
262             =cut
263              
264             1; # End of WWW::Scape::FindaGrave