----
presentation_topic: FEAR::API
presentation_title: FEAR::API - There's no fear with this elegant site scraper
presentation_place:
presentation_date:
----
== 任務
請您抓回某商務網站(例如說是 Amazon.com)
上所有的網頁內容,並從每一頁中抓出產品的詳細規格,並把每一筆資料存入資料庫裡。
----
== 我們通常使用什麼工具?
* LWP::Simple
* LWP::UserAgent
* WWW::Mechanize
* Template::Extract
* Encode
* ....., etc.
----
== 範例 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);
+
# 或是使用 WWW::Mechanize
use WWW::Mechanize;
use Data::Dumper;
my $mech = WWW::Mechanize->new();
$mech->get( "http://google.com" );
print Dumper $mech->links;
----
== 範例 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;
----
== 範例 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"));
----
== 我們需要什麼?
* 一個整合性的模組
* 使用較少的程式碼
* 合併取得網頁及抓取資料的工作
+/所以 FEAR::API 誕生了!/
----
== 重寫範例 1
=== 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;
----
== 重寫範例 2
=== 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;
----
== 重寫範例 3
=== 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;
----
== 什麼是 FEAR::API?
* FEAR::API 代表:
+** Fetch, Extract, Aggregate, and Reorganize
* FEAR::API 可以幫忙您簡化從網站抓資料的過程,盡量讓您用很短的時間,並用最少的程式碼達成相同的需求。
+* 特色:
** 結合了 LWP::Simple, WWW::Mechanize, Template::Extract, Encode 等等的精華
+** 使用了 operator overloading 讓程式碼變的更簡潔
+** 取回的網頁自動轉成 UTF-8
+** 使用 FEAR::API 有點像是使用一個新的 sub-language 不過又具有 perl 的完整支援。
+** 它是一個物件導向的模組
+讓我們來看更多範例
----
== 抓取網頁並取入純量變數
use FEAR::API -base;
my $content = fetch("google.com")->document->as_string;
fetch("google.com") > my $content;
----
== 抓取網頁並印到 STDOUT
getprint("google.com");
print fetch("google.com")->document->as_string;
fetch("google.com");
print $$_;
fetch("google.com") | _print;
----
== 抓取網頁並存入檔案
getstore("google.com");
url("google.com")->() | _save_as("google.html");
use IO::All;
fetch("google.com") | io('google.html');
----
== 跟隨google 首頁上的連結
url("google.com")->() >> _self;
&$_ while $_;
----
== 儲存google 首頁上的連結
url("google.com")->() >> _self | _save_as_tree("./root");
$_->() | _save_as_tree("./root") while $_;
----
== 遞迴的從google 抓取網頁
url("google.com");
&$_ >> _self while $_;
----
== 遞迴的從google 抓取網頁並存入檔案
url("google.com");
&$_ >> _self | _save_as_tree("./root") while $_;
----
== 跟隨google 首頁上的第二個連結
url("google.com")->()->follow_link(n => 2);
----
== 回傳google 首頁上的連結
print Dumper fetch("google.com")->links;
----
== 送出查詢
url("google.com")->();
submit_form(
form_number => 1,
fields => { q => "Kill Bush" }
);
----
== 抓取有某樣版的連結
url("[% FOREACH i = ['a'..'z'] %]
http://some.site/[% i %]
[% END %]");
&$_ while $_;
----
== 處理網頁上的連結 (I)
url("google.com")->()
>> [
qr(^http:) => _self,
qr(google) => \my @l,
qr(google) => sub { print ">>>".$_[0]->[0],$/ }
];
$_->() while $_;
print Dumper \@l;
----
== 處理網頁上的連結 (II)
url("google.com")->()
>> {
qr(^http:) => _self,
qr(google) => \my @l,
qr(google) => sub { print ">>>".$_[0]->[0],$/ }
};
$_->() while $_;
print Dumper \@l;
----
== 同步抓取網頁
url("google.com")->() >> _self;
pfetch(sub{
local $_ = shift;
print join q/ /, title, current_url, document->size, $/;
});
----
== 抓取資料
=== 從 CPAN 上抓出資料
url("http://search.cpan.org/recent")->();
submit_form(
form_name => "f",
fields => {
query => "perl"
});
template("[% p %]");
extract;
print Dumper extresult;
+=== 清理 HTML 再從 CPAN 上抓出資料
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, 再從 CPAN 上抓出資料, 並修飾結果
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;
+=== 使用過濾器的語法
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 \@$_;
+=== 呼叫結果處理器
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');
+
你也可以直接把抓出來的資料放到關聯式資料庫裡。
invoke_handler('Some::Module::based::on::Class::DBI');
invoke_handler('Some::Module::based::on::DBIx::Class::CDBICompat');
----
== 文件前處理
url("google.com")->()
| _preproc(use => "html_to_null")
| _preproc(use => "decode_entities")
| _print;
----
== 結果後處理
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 的功能
file("some.document.txt")->()
| _map(qr(some_pattern))
| _sort
| _grep(qr(some_other_pattern))
| _uniq
;
----
== 如果不用 -base
use FEAR::API;
my $f = fear();
$f->fetch("blah");
# ..., etc.
使用 -base, $_ 會自動設成 FEAR::API 物件。
use FEAR::API -base;
fetch("blah");
# ..., etc.
----
== 在命令列使用 FEAR::API
fearperl -e 'fetch("google.com")'
perl -M'FEAR::API -base' -e 'fetch("google.com")'
----
== TO DO
* 程式碼重整及清理
* 更多測試
* 補齊文件
* 發展更多 method
----
banner_bgcolor: lightblue
----
== 就這樣! :D
* The END