/[Grep]/lib/Grep/Source.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Diff of /lib/Grep/Source.pm

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 123 by dpavlin, Sat Apr 28 17:55:37 2007 UTC revision 165 by dpavlin, Mon Jun 11 22:56:53 2007 UTC
# Line 188  sub content_class { Line 188  sub content_class {
188          }          }
189  }  }
190    
 =head2 scrape  
191    
192  Create semi-complex L<WWW::Mechanize> rules to scrape page  =head2 element_by_triplet
193    
194    Helper method to select element(s) using C<element/attribute/value> triplet using
195    L<HTML::TreeBuilder> trees.
196    
197      my $el = $self->element_by_triplet(
198            tree => $tree_or_element,
199            triplets => [ qw/
200                    div id target
201                    div class another
202            / ],
203            message => 'find search result element',
204            fatal => 1,     # die instead of warn
205      );
206    
207  =cut  =cut
208    
# Line 203  sub element_by_triplet { Line 214  sub element_by_triplet {
214          my $tree = $args->{tree} || die "no tree";          my $tree = $args->{tree} || die "no tree";
215          my $message = $args->{message} || '';          my $message = $args->{message} || '';
216          my $fatal = $args->{fatal};          my $fatal = $args->{fatal};
217          die "no templates" unless defined( $args->{templates} );          die "no triplets" unless defined( $args->{triplets} );
218          my @templates;          my @triplets;
219          if ( ref( $args->{template} ) eq 'ARRAY' ) {          if ( ref( $args->{triplets} ) eq 'ARRAY' ) {
220                  @templates = @{ $args->{templates} };                  @triplets = @{ $args->{triplets} };
221          } else {          } else {
222                  @templates = ( $args->{templates} );                  @triplets = ( $args->{triplets} );
223          }          }
224    
225          push @templates, ( undef, undef ) if ( $#templates == 0 );          push @triplets, ( undef, undef ) if ( $#triplets == 0 );
226    
227          die "wrapper doesn't have 3 elements but ", $#templates unless (          die "triplet doesn't have 3 elements but ", $#triplets unless (
228                  ( $#templates + 1 ) % 3 == 0                  ( $#triplets + 1 ) % 3 == 0
229          );          );
230    
231          my ( $el, $attr, $value );          my ( $el, $attr, $value );
# Line 222  sub element_by_triplet { Line 233  sub element_by_triplet {
233          my @results;          my @results;
234          my @tags;          my @tags;
235    
236          while ( @templates ) {          $self->log->debug("looking for $message ", dump( @triplets ));
237                  ( $el,$attr,$value ) = splice( @templates, 0, 3 );          while ( @triplets ) {
238                    ( $el,$attr,$value ) = splice( @triplets, 0, 3 );
239                  my $tag = $attr ? "<$el $attr=\"$value\">" : "<$el>";                  my $tag = $attr ? "<$el $attr=\"$value\">" : "<$el>";
240                  push @tags, $tag;                  push @tags, $tag;
                 $self->log->debug("looking for $message $tag");  
241                  @results = $tree->look_down( '_tag', $el, sub {                  @results = $tree->look_down( '_tag', $el, sub {
242                                  return 1 unless ( $attr && $value );                                  return 1 unless ( $attr && $value );
243                                  ( $_[0]->attr( $attr ) || '' ) eq $value;                                  ( $_[0]->attr( $attr ) || '' ) =~ m/\b\Q$value\E\b/
244                  });                  });
245                  last if @results;                  last if @results;
246          }          }
247    
248          if ( ! @results ) {          if ( ! @results ) {
249                  my $msg = "can't find $message ", join(" ", @tags);                  my $msg = "can't find $message " . join(" ", @tags);
250                  die $msg if ( $fatal );                  die $msg if ( $fatal );
251                  warn $msg;                  #warn $msg;
252                  return;                  return;
253          }          }
254    
255          $self->log->debug("found ", $#results + 1, " results");          $self->log->debug("found ", $#results + 1, " elements");
256            #warn dump( map { $_->as_HTML } @results );
257    
258          return @results if wantarray;          return @results if wantarray;
259          return shift @results;          return shift @results;
260  }  }
261    
262    =head2 scrape
263    
264    Create semi-complex L<WWW::Mechanize> rules to scrape page easily
265    
266      $parent->scrape(
267                    # if search string isn't part or URI
268                    submit_form => {
269                            fields => {
270                                    value => $parent->q,
271                            },
272                            button => 'fullsearch',
273                    },
274                    # element with search results
275                    wrapper => [ qw/div class searchresults/ ],
276                    # element (or tripple) for each result with link
277                    # <a href=".."> inside it to full-text result
278                    results => 'dt',
279                    # collect which element on page linked from results
280                    scrape => [ qw/div id page/ ],
281                    # when search returns just single hit, it will redirect to result page
282                    redirect_single_result => 1,
283      );
284    
285    =cut
286    
287  sub scrape {  sub scrape {
288          my $self = shift;          my $self = shift;
289    
# Line 284  sub scrape { Line 321  sub scrape {
321          my $tree = HTML::TreeBuilder->new or die "can't create html tree";          my $tree = HTML::TreeBuilder->new or die "can't create html tree";
322          $tree->parse( $mech->content ) or die "can't parse fetched content";          $tree->parse( $mech->content ) or die "can't parse fetched content";
323    
324          my $div = $self->element_by_triplet(          my @wrapper_divs = $self->element_by_triplet(
325                  tree => $tree,                  tree => $tree,
326                  templates => $args->{wrapper},                  triplets => $args->{wrapper},
327                  message => 'wrapper for all results',                  message => 'wrapper for all results',
328                  fatal => 1                  # on closer recollection, this shouldn't be ever fatal, because
329                    # "no results found" page might not contain wrapper
330                    #fatal => $args->{redirect_single_result} ? 0 : 1,
331          );          );
332    
333          my $max = 15;          my $max = 15;
# Line 297  sub scrape { Line 336  sub scrape {
336          my $base_uri = $uri;          my $base_uri = $uri;
337          $base_uri =~ s!\?.*$!!;          $base_uri =~ s!\?.*$!!;
338    
339          my @r = $self->element_by_triplet(          # directly got first result
340                  tree => $tree,          if ( $args->{redirect_single_result} && ! @wrapper_divs ) {
341                  templates => $args->{results},  
342                  message => 'result element',                  my $uri = $mech->uri; $uri->query( undef ); $uri = $uri->canonical;
343          );  
344                    my $div = $self->element_by_triplet(
345                            tree => $tree,
346                            message => "single result - redirect to $uri",
347                            triplets => $args->{scrape},
348                            fatal => 1,
349                    );
350    
351                    $self->add_record(
352                            in_feed => $feed,
353                            title => $mech->title,
354                            link => $uri,
355                            content => $div->as_HTML,
356                    );
357    
358                    $tree->delete; # clear memory!
359                    return;
360            }
361    
362            my @r;
363    
364            foreach my $div ( @wrapper_divs ) {
365    
366                    my @r_here = $self->element_by_triplet(
367                            tree => $div,
368                            triplets => $args->{results},
369                            message => 'result element',
370                    );
371    
372                    push @r, @r_here if (@r_here);
373            }
374    
375            $self->log->debug("in total, found ", $#r + 1, " results in ", $#wrapper_divs + 1, " result wrapper elements");
376    
377          foreach my $dt ( @r ) {          foreach my $dt ( @r ) {
378                  my $a = $dt->look_down( '_tag', 'a', sub { $_[0]->attr('href') } );                  my $a = $dt->look_down( '_tag', 'a', sub { $_[0]->attr('href') } );
379                  if ( $a ) {                  if ( $a ) {
380    
381                          my $href = $a->attr('href') or die "can't find href inside <", $args->{results}, ">";                          my $href = $a->attr('href') or die "can't find href inside <", $args->{results}, ">";
382                          my $page_uri = URI->new_abs( $a->attr('href'), $base_uri );  
383                            my $page_uri = URI->new_abs( $href, $base_uri );
384                          $page_uri->query( undef );                          $page_uri->query( undef );
385                          $page_uri = $page_uri->canonical;                          $page_uri = $page_uri->canonical;
386    
387                          $self->log->debug("fetching page: ",$a->as_text," from $page_uri");                          $self->log->debug("fetching page: ",$a->as_text," from $page_uri");
388                          if ( $mech->follow_link( url => $a->attr('href') ) ) {                          if ( $mech->follow_link( url => $href ) ) {
389    
390                                  $self->save( "page-${nr}.html", $mech->content );                                  $self->save( "page-${nr}.html", $mech->content );
391    
392                                  my $page_tree = HTML::TreeBuilder->new or die "can't create page tree";                                  my $page_tree = HTML::TreeBuilder->new or die "can't create page tree";
393                                  $page_tree->parse( $mech->content ) or die "can't parse page at $page_uri";                                  $page_tree->parse( $mech->content ) or die "can't parse page at $page_uri";
394                                  $div = $self->element_by_triplet(                                  my @divs = $self->element_by_triplet(
395                                          tree => $page_tree,                                          tree => $page_tree,
396                                          message => "result $nr",                                          message => "result page $nr",
397                                          templates => $args->{scrape}                                          triplets => $args->{scrape}
398                                  );                                  );
399    
400                                  $self->add_record(                                  if ( @divs ) {
401                                          in_feed => $feed,  
402                                          title => $mech->title,                                          my $html = join("<hr/>\n", map { $_->as_HTML } @divs );
403                                          link => $page_uri,                                          $self->log->debug("combined ", $#divs + 1, " elements elements in ", length($html), " bytes");
404                                          content => $div->as_HTML,  
405  #                                       summary =>                                          $self->add_record(
406  #                                       category =>                                                  in_feed => $feed,
407  #                                       author =>                                                  title => $mech->title,
408  #                                       issued =>                                                  link => $page_uri,
409  #                                       modified =>                                                  content => $html,
410                                  ) if ( $div );  #                                               summary =>
411    #                                               category =>
412    #                                               author =>
413    #                                               issued =>
414    #                                               modified =>
415                                            );
416    
417                                    } else {
418                                            $self->log->debug("NO CONTENT scraped from page $nr");
419                                    }
420    
421                                  $mech->back;                                  $mech->back;
422                                  $page_tree->delete;                                  $page_tree->delete;

Legend:
Removed from v.123  
changed lines
  Added in v.165

  ViewVC Help
Powered by ViewVC 1.1.26