----
presentation_topic: FEAR::API
presentation_title: FEAR::API - There's no fear with this elegant site scraper
presentation_place:
presentation_date:
----
== The assignment
Please fetch the web pages from a certain commercial site, say Amazon.com.
Extract specifications of products from each page, and store each record in
database.
----
== What do we usually use?
* LWP::Simple
* LWP::UserAgent
* WWW::Mechanize
* Template::Extract
* Encode
* ....., etc.
----
== Example 1
use LWP::Simple;
use HTML::LinkExtractor;
use Data::Dumper;
my $input = get("http://google.com");
my $LX = new HTML::LinkExtractor();
$LX->parse(\$input);
print Dumper($LX->links);
+
# or you use WWW::Mechanize
use WWW::Mechanize;
use Data::Dumper;
my $mech = WWW::Mechanize->new();
$mech->get( "http://google.com" );
print Dumper $mech->links;
----
== Example 2
use WWW::Mechanize;
use Data::Dumper;
my $mech = WWW::Mechanize->new();
$mech->get( "http://google.com" );
my @link;
foreach ($mech->links){
if($_->[0] =~ /foo/){
$mech->get($_->[0]);
}
elsif($_->[0] =~ /bar/){
push @link;
}
}
print Dumper \@link;
----
== Example 3
use LWP::Simple;
use Template::Extract;
use Data::Dumper;
my $obj = Template::Extract->new;
print Dumper $obj->extract('[% FOREACH rec %][% link %][% END %]',
get("http://google.com"));
----
== What do we need?
* An integrative module
* Less coding
* Something combining web page fetching and data extraction
+/So, FEAR::API was born!/
----
== Example 1 rewrite
=== Old
use WWW::Mechanize;
use Data::Dumper;
my $mech = WWW::Mechanize->new();
$mech->get( "http://google.com" );
print Dumper $mech->links;
+=== New
use FEAR::API -base;
print Dumper fetch("google.com")->links;
----
== Example 2 rewrite
=== Old
use WWW::Mechanize;
use Data::Dumper;
my $mech = WWW::Mechanize->new();
$mech->get( "http://google.com" );
my @link;
foreach ($mech->links){
if($_->[0] =~ /foo/){
$mech->get($_->[0]);
}
elsif($_->[0] =~ /bar/){
push @link;
}
}
print Dumper \@link;
+=== New
use FEAR::API -base;
fetch("google.com") >> [
qr(foo) => _self,
qr(bar) => \my @link,
];
$_->() while $_;
print Dumper \@link;
----
== Example 3 rewrite
=== Old
use LWP::Simple;
use Template::Extract;
use Data::Dumper;
my $obj = Template::Extract->new;
print Dumper $obj->extract('[% FOREACH rec %][% link %][% END %]',
get("http://google.com"));
+=== New
use FEAR::API -base;
url("google.com")->();
extract('[% link %]');
print Dumper extresult;
----
== What is FEAR::API?
* FEAR::API is for:
+** Fetch, Extract, Aggregate, and Reorganize
* FEAR::API can help simplify the process of extracting data from web sites. It tries to help you
spend shorter time and write the least amount of code to come up to the same request.
+* Features:
** Combines the essence of LWP::Simple, WWW::Mechanize, Template::Extract, Encode
+** Cleaner code through operator overloading
+** Autoconverts web pages into UTF-8
+** Using FEAR::API is a bit like using a new sub-language, but with the full Perl support.
+** It's object-oriented.
+Let us see more examples
----
== Fetch a page and store it in a scalar
use FEAR::API -base;
my $content = fetch("google.com")->document->as_string;
fetch("google.com") > my $content;
----
== Fetch a page and print to STDOUT
getprint("google.com");
print fetch("google.com")->document->as_string;
fetch("google.com");
print $$_;
fetch("google.com") | _print;
----
== Fetch a page and save it to a file
getstore("google.com");
url("google.com")->() | _save_as("google.html");
use IO::All;
fetch("google.com") | io('google.html');
----
== Follow links in Google's homepage
url("google.com")->() >> _self;
&$_ while $_;
----
== Save links in Google's homepage
url("google.com")->() >> _self | _save_as_tree("./root");
$_->() | _save_as_tree("./root") while $_;
----
== Recursively get web pages from Google
url("google.com");
&$_ >> _self while $_;
----
== Recursively get web pages from Google
url("google.com");
&$_ >> _self | _save_as_tree("./root") while $_;
----
== Follow the second link of Google
url("google.com")->()->follow_link(n => 2);
----
== Return links from Google's homepage
print Dumper fetch("google.com")->links;
----
== Submit a query to Google
url("google.com")->();
submit_form(
form_number => 1,
fields => { q => "Kill Bush" }
);
----
== Get links of some pattern
url("[% FOREACH i = ['a'..'z'] %]
http://some.site/[% i %]
[% END %]");
&$_ while $_;
----
== Deal with links in a web page (I)
url("google.com")->()
>> [
qr(^http:) => _self,
qr(google) => \my @l,
qr(google) => sub { print ">>>".$_[0]->[0],$/ }
];
$_->() while $_;
print Dumper \@l;
----
== Deal with links in a web page (II)
url("google.com")->()
>> {
qr(^http:) => _self,
qr(google) => \my @l,
qr(google) => sub { print ">>>".$_[0]->[0],$/ }
};
$_->() while $_;
print Dumper \@l;
----
== Get pages in parallel
url("google.com")->() >> _self;
pfetch(sub{
local $_ = shift;
print join q/ /, title, current_url, document->size, $/;
});
----
== Extraction
=== Extract data from CPAN
url("http://search.cpan.org/recent")->();
submit_form(
form_name => "f",
fields => {
query => "perl"
});
template("[% p %]");
extract;
print Dumper extresult;
+=== Extract data from CPAN after some HTML cleanup
url("http://search.cpan.org/recent")->();
submit_form(
form_name => "f",
fields => {
query => "perl"
});
preproc(q(s/\A.+(.+).+\Z/$1/s));
print document->as_string; # print content to STDOUT
template("[% p %]");
extract;
print Dumper extresult;
+=== HTML cleanup, extract data, and refine results
url("http://search.cpan.org/recent")->();
submit_form(
form_name => "f",
fields => {
query => "perl"
});
preproc(q(s/\A.+(.+).+\Z/$1/s));
print $$_; # print content to STDOUT
template("[% rec %]");
extract;
postproc(q($_->{rec} =~ s/<.+?>//g)); # Strip HTML tags
print Dumper extresult;
+=== Use filtering syntax
fetch("http://search.cpan.org/recent");
submit_form(
form_name => "f",
fields => {
query => "perl"
});
$_ | _doc_filter(q(s/\A.+(.+).+\Z/$1/s))
| _template("[% rec %]")
| _result_filter(q($_->{rec} =~ s/<.+?>//g));
print Dumper \@$_;
+=== Invoke handler for extracted results
fetch("http://search.cpan.org/recent");
submit_form(
form_name => "f",
fields => {
query => "perl"
});
$_ | _doc_filter(q(s/\A.+(.+).+\Z/$1/s))
| "[% rec %]"
| _result_filter(q($_->{rec} =~ s/<.+?>//g));
invoke_handler('Data::Dumper');
+
You can also put extracted results straight into relational databases.
invoke_handler('Some::Module::based::on::Class::DBI');
invoke_handler('Some::Module::based::on::DBIx::Class::CDBICompat');
----
== Preprocess document
url("google.com")->()
| _preproc(use => "html_to_null")
| _preproc(use => "decode_entities")
| _print;
----
== Postprocess extraction results
fetch("http://search.cpan.org/recent");
submit_form(
form_name => "f",
fields => {
query => "perl"
});
$_ | _doc_filter(q(s/\A.+(.+).+\Z/$1/s))
| _template("[% rec %]")
| _result_filter(use => "html_to_null", qw(rec));
| _result_filter(use => "decode_entities", qw(rec))
print Dumper \@$_;
----
== Shell-like functions
file("some.document.txt")->()
| _map(qr(some_pattern))
| _sort
| _grep(qr(some_other_pattern))
| _uniq
;
----
== If you don't use with -base
use FEAR::API;
my $f = fear();
$f->fetch("blah");
# ..., etc.
Use with -base, and $_ will be set to FEAR::API.
use FEAR::API -base;
fetch("blah");
# ..., etc.
----
== Use FEAR::API in command line
fearperl -e 'fetch("google.com")'
perl -M'FEAR::API -base' -e 'fetch("google.com")'
----
== TO DO
* Code refactoring and cleaning
* Add more tests
* Patch documents
* Develop more methods
----
banner_bgcolor: lightblue
----
== That's All:D
* The END