File Coverage

blib/lib/WWW/Scraper/ISBN/WHSmith_Driver.pm
Criterion Covered Total %
statement 84 84 100.0
branch 13 24 54.1
condition 8 18 44.4
subroutine 10 10 100.0
pod 1 1 100.0
total 116 137 84.6


line stmt bran cond sub pod time code
1             package WWW::Scraper::ISBN::WHSmith_Driver;
2              
3 6     6   101394 use strict;
  6         14  
  6         283  
4 6     6   32 use warnings;
  6         11  
  6         234  
5              
6 6     6   28 use vars qw($VERSION @ISA);
  6         14  
  6         484  
7             $VERSION = '0.07';
8              
9             #--------------------------------------------------------------------------
10              
11             =head1 NAME
12              
13             WWW::Scraper::ISBN::WHSmith_Driver - Search driver for the WHSmith online book catalog.
14              
15             =head1 SYNOPSIS
16              
17             See parent class documentation (L)
18              
19             =head1 DESCRIPTION
20              
21             Searches for book information from the WHSmith online book catalog
22              
23             =cut
24              
25             #--------------------------------------------------------------------------
26              
27             ###########################################################################
28             # Inheritence
29              
30 6     6   31 use base qw(WWW::Scraper::ISBN::Driver);
  6         11  
  6         3105  
31              
32             ###########################################################################
33             # Modules
34              
35 6     6   8996 use WWW::Mechanize;
  6         849364  
  6         308  
36              
37             ###########################################################################
38             # Constants
39              
40 6     6   65 use constant REFERER => 'http://www.whsmith.co.uk';
  6         9  
  6         468  
41 6     6   33 use constant SEARCH => 'http://www.whsmith.co.uk/pws/ProductDetails.ice?ProductID=%s&keywords=%s&redirect=true';
  6         9  
  6         284  
42 6     6   28 use constant PRODUCT => '/products/[^/]+/product/';
  6         12  
  6         6077  
43              
44             #--------------------------------------------------------------------------
45              
46             ###########################################################################
47             # Public Interface
48              
49             =head1 METHODS
50              
51             =over 4
52              
53             =item C
54              
55             Creates a query string, then passes the appropriate form fields to the
56             WHSmith server.
57              
58             The returned page should be the correct catalog page for that ISBN. If not the
59             function returns zero and allows the next driver in the chain to have a go. If
60             a valid page is returned, the following fields are returned via the book hash:
61              
62             isbn (now returns isbn13)
63             isbn10
64             isbn13
65             ean13 (industry name)
66             author
67             title
68             book_link
69             image_link
70             description
71             pubdate
72             publisher
73             binding (if known)
74             pages (if known)
75             weight (if known) (in grammes)
76             width (if known) (in millimetres)
77             height (if known) (in millimetres)
78              
79             The book_link and image_link refer back to the WHSmith website.
80              
81             =back
82              
83             =cut
84              
85             sub search {
86 2     2 1 6990 my $self = shift;
87 2         3 my $isbn = shift;
88 2         12 $self->found(0);
89 2         40 $self->book(undef);
90              
91             # validate and convert into EAN13 format
92 2         23 my $ean = $self->convert_to_ean13($isbn);
93 2 50 66     105 return $self->handler("Invalid ISBN specified [$isbn]")
      33        
      66        
      33        
94             if(!$ean || (length $isbn == 13 && $isbn ne $ean)
95             || (length $isbn == 10 && $isbn ne $self->convert_to_isbn10($ean)));
96              
97 2         41 $isbn = $ean;
98             #print STDERR "\n# isbn=[\n$isbn\n]\n";
99              
100 2         16 my $mech = WWW::Mechanize->new();
101 2         50688 $mech->agent_alias( 'Linux Mozilla' );
102 2         169 $mech->add_header( 'Accept-Encoding' => undef );
103 2         31 $mech->add_header( 'Referer' => REFERER );
104              
105 2         23 my $search = sprintf SEARCH, $isbn, $isbn;
106             #print STDERR "\n# search=[$search]\n";
107 2         5 eval { $mech->get( $search ) };
  2         9  
108 2 50 33     559597 return $self->handler("the WHSmith website appears to be unavailable.")
      33        
109             if($@ || !$mech->success() || !$mech->content());
110              
111             # The Book page
112 2         148 my $html = $mech->content();
113 2 50   1   4300 return $self->handler("Failed to find that book on the WHSmith website. [$isbn]")
  1         13  
  1         2  
  1         20  
114             if($html =~ m!Sorry, no products were found!si);
115              
116 2         26947 my $url = $mech->uri();
117 2 50       81 return $self->handler("Failed to find that book on the WHSmith website. [$isbn]")
118             if($url =~ m!Error.aspx!si);
119              
120 2         919 $html =~ s/&/&/g;
121 2         884 $html =~ s/�?39;/'/g;
122 2         918 $html =~ s/ / /g;
123              
124             #print STDERR "\n# html=[\n$html\n]\n";
125              
126 2         7 my $data;
127 2         4623 ($data->{isbn13}) = $html =~ m!
  • \s*ISBN13:\s*(.*?)
  • !si;
    128 2         4285 ($data->{isbn10}) = $html =~ m!
  • \s*ISBN10:\s*(.*?)
  • !si;
    129 2         5253 ($data->{publisher}) = $html =~ m!Publisher:\s*([^<]+)!si;
    130 2         4218 ($data->{pubdate}) = $html =~ m!
  • \s*publication date:\s*(.*?)
  • !si;
    131 2         4526 ($data->{title}) = $html =~ m!

    ([^<]*)

    !si;
    132 2         4021 ($data->{binding}) = $html =~ m!
    .*?([^<]+)
    !si;
    133 2 50       14 ($data->{binding}) = $html =~ m!
  • \s*Format:\s*(.*?)
  • !si unless($data->{binding});
    134 2         4564 ($data->{pages}) = $html =~ m!
  • \s*Number Of Pages:\s*(.*?)
  • !si;
    135 2         3799 ($data->{author}) = $html =~ m!By:(.*?)!si;
    136 2         558 ($data->{image}) = $html =~ m!!si;
    137 2         212 ($data->{description}) = $html =~ m!!si;
    138              
    139 2 50       11 if($data->{image}) {
    140 2         7 $data->{thumb} = $data->{image};
    141 2         18 $data->{thumb} =~ s!/x?large/!/small/!;
    142             }
    143              
    144             # currently not provided
    145 2         4958 ($data->{width}) = $html =~ m!Width:\s*([^<]+)!si;
    146 2         4879 ($data->{height}) = $html =~ m!Height:\s*([^<]+)!si;
    147 2         122 ($data->{weight}) = $html =~ m!
  • \s*weight:\s*(.*?)
  • !s;
    148              
    149 2 50       14 $data->{width} = int($data->{width}) if($data->{width});
    150 2 50       7 $data->{height} = int($data->{height}) if($data->{height});
    151 2 50       17 $data->{weight} = int($data->{weight}) if($data->{weight});
    152              
    153 2 50       7 if($data->{author}) {
    154 2         32 $data->{author} =~ s/<[^>]*>//g;
    155 2         11 $data->{author} =~ s/\(author\)/ /g;
    156 2         13 $data->{author} =~ s/\s+/ /g;
    157 2         8 $data->{author} =~ s/\s+,\s+/, /g;
    158             }
    159              
    160             #use Data::Dumper;
    161             #print STDERR "\n# data=" . Dumper($data);
    162              
    163 2 50       8 return $self->handler("Could not extract data from The WHSmith result page.")
    164             unless(defined $data);
    165              
    166             # trim top and tail
    167 2         17 foreach (keys %$data) {
    168 28 100       44 next unless(defined $data->{$_});
    169 22         40 $data->{$_} =~ s/^\s+//;
    170 22         91 $data->{$_} =~ s/\s+$//;
    171             }
    172              
    173 2         43 my $bk = {
    174             'ean13' => $data->{isbn13},
    175             'isbn13' => $data->{isbn13},
    176             'isbn10' => $data->{isbn10},
    177             'isbn' => $data->{isbn13},
    178             'author' => $data->{author},
    179             'title' => $data->{title},
    180             'book_link' => $url,
    181             'image_link' => $data->{image},
    182             'thumb_link' => $data->{thumb},
    183             'description' => $data->{description},
    184             'pubdate' => $data->{pubdate},
    185             'publisher' => $data->{publisher},
    186             'binding' => $data->{binding},
    187             'pages' => $data->{pages},
    188             'weight' => $data->{weight},
    189             'width' => $data->{width},
    190             'height' => $data->{height},
    191             'html' => $html
    192             };
    193              
    194             #use Data::Dumper;
    195             #print STDERR "\n# book=".Dumper($bk);
    196              
    197 2         21 $self->book($bk);
    198 2         44 $self->found(1);
    199 2         20 return $self->book;
    200             }
    201              
    202             1;
    203              
    204             __END__