HACK#54 NoXML อีกทางเลือกหนึ่งสำหรับใช้แทน SOAP::Lite
NoXML เป็นทางเลือกในการใช้แทน SOAP::Lite ซึ่งใช้เพียง regular expression ทำงาน โดยไม่ต้องมี XML parser แต่อย่างใด
# NoXML.pm $key $start $maxResults $filter $restrict $safeSearch $lr $ie $oe
NoXML เป็นอีกโมดูลหนึ่ง ที่ใช้แทน SOAP::Lite ได้ ซึ่ง NOXML นี้จะใช้เพียง regular expression ทำงาน โดยปราศจาก XML parser ทุกชนิด ดังที่ชื่อของโมดูลนี้ได้แนะนำไว้
ถ้าหากคุณมีเพียงความรู้พื้นฐานในการติดตั้ง Perl เพื่อใช้งานของคุณเองเท่านั้น รวมทั้งไม่มีทั้ง SOAP::Lite [Hack #52] และ XML::Parser แล้วล่ะก็ NoXML นับเป็นทางเลือกที่ดีทีเดียวสำหรับการแฮ็กเกือบจะทั้งหมดในหนังสือเล่มนี้
- Tip: ผู้เชี่ยวชาญ XML บางท่านยืนยันว่า ไม่มีอะไรมาแทนที่ XML parser จริงๆได้ ซึ่งเป็นสิ่งที่ถูกต้องอย่างยิ่ง เพราะยังมีประเด็นเรื่องการ Encode และ Hierarchy ที่ regular expression-based parser ไม่สามารถทำได้ แต่สำหรับตัว NOXML นั้นก็ยังเป็นตัวที่ง่ายที่สุดสำหรับการใช้งานและติดตั้ง
โค้ดของ NoXML
ไฟล์สำคัญในการแฮ็กในหัวข้อนี้ก็คือ NoXML.pm ซึ่งควรจะบันทึกลงในไดเรกทอรีเดียวกันกับสคริปต์ที่ใช้ในการแฮ็ก
# NoXML.pm
# NoXML [pronounced "no xml"] is a dire-need drop-in
# replacement for SOAP::Lite designed for Google Web API hacking.
package NoXML;
use strict;
no strict "refs";
# LWP for making HTTP requests, XML for parsing Google SOAP
use LWP::UserAgent;
use XML::Simple;
# Create a new NoXML
sub new {
my $self = {};
bless($self);
return $self;
}
# Replacement for the SOAP::Lite-based doGoogleSearch method
sub doGoogleSearch {
my($self, %args);
($self, @args{qw/ key q start maxResults filter restrict
safeSearch lr ie oe /}) = @_;
# grab SOAP request from _ _DATA_ _
my $tell = tell(DATA);
my $soap_request = join '', ;
seek(DATA, $tell, 0);
$soap_request =~ s/\$(\w+)/$args{$1}/ge; #interpolate variables
# Make (POST) a SOAP-based request to Google
my $ua = LWP::UserAgent->new;
my $req = HTTP::Request->new(POST => 'http://api.google.com/search/beta2');
$req->content_type('text/xml');
$req->content($soap_request);
my $res = $ua->request($req);
my $soap_response = $res->as_string;
# Drop the HTTP headers and so forth until the initial xml element
$soap_response =~ s/^.+?(<\?xml)/$1/migs;
# Drop element namespaces for tolerance of future prefix changes
$soap_response =~ s!(<\/?)[\w-]+?:([\w-]+?)!$1$2!g;
# Set up a return dataset
my $return;
# Unescape escaped HTML in the resultset
my %unescape = ('<'=>'<', '>'=>'>', '&'=>'&', '"'=>'"', '''=>"'");
my $unescape_re = join '|' => keys %unescape;
# Divide the SOAP response into the results and other metadata
my($before, $results, $after) = $soap_response =~
m#(^.+)(.+?)(.+$)#migs ;
my $before_and_after = $before . $after;
# Glean as much metadata as possible (while being somewhat lazy ;-)
while ($before_and_after =~ m#([^<]*?)<#migs) {
$return->{$1} = $3; # pack the metadata into the return dataset
}
# Glean the results
my @results;
while ($results =~ m#(.+?)#migs) {
my $item = $1;
my $pairs = {};
while ( $item =~ m#([^<]*)#migs ) {
my($element, $value) = ($1, $2);
$value =~ s/($unescape_re)/$unescape{$1}/g;
$pairs->{$element} = $value;
}
push @results, $pairs;
}
# Pack the results into the return dataset
$return->{resultElements} = \@results;
# Return nice, clean, usable results
return $return;
}
1;
# This is the SOAP message template sent to api.google.com. Variables
# signified with $variablename are replaced by the values of their
# counterparts sent to the doGoogleSearch subroutine.
_ _DATA_ _
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/1999/XMLSchema">
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
$q
ข้าง ล่างนี้เป็นตัวอย่างสคริปต์ NoXML ในแง่การใช้งาน ซึ่งก็ไม่แตกต่างอะไรไปจากการแฮ็กในหัวข้ออื่นๆในหนังสือเล่มนี้ จะมีก็เพียงแต่การเน้นข้อความเป็นตัวหนา (bold) ตรงส่วนที่มีการ comment บรรทัดเดิมที่ใช้กับ SOAP::Lite และแทรกบรรทัดใหม่ เพื่อให้ใช้กับ PoXML ได้เท่านั้น
#!/usr/bin/perl
# noxml_google2csv.pl
# Google Web Search Results via NoXML ("no xml") module
# exported to CSV suitable for import into Excel
# Usage: noxml_google2csv.pl "{query}" [> results.csv]
# Your Google API developer's key
my $google_key='insert key here';
use strict;
# use SOAP::Lite;
use NoXML;
$ARGV[0]
or die qq{usage: perl noxml_search2csv.pl "{query}"\n};
# my $google_search = SOAP::Lite->service("file:$google_wdsl");
my $google_search = new NoXML;
my $results = $google_search ->
doGoogleSearch(
$google_key, shift @ARGV, 0, 10, "false",
"", "false", "", "latin1", "latin1"
);
@{$results->{'resultElements'}} or die('No results');
print qq{"title","url","snippet"\n};
foreach (@{$results->{'resultElements'}}) {
$_->{title} =~ s!"!""!g; # double escape " marks
$_->{snippet} =~ s!"!""!g;
my $output = qq{"$_->{title}","$_->{URL}","$_->{snippet}"\n};
$output =~ s!<.+?>!!g; # drop all HTML tags
print $output;
}
Running the Hack
รัน สคริปต์นี้ที่ command line โดยการใส่คำถามลงไปที่ command line และระบุให้แสดงผลลัพธ์ในไฟล์ CSV ที่คุณต้องการสร้างขึ้นมา หรือที่ต้องการให้นำผลลัพธ์ไปต่อท้าย (appending) ตัวอย่างเช่นข้างล่างนี้ใช้ “no xml” เป็นคำถาม และกำหนดให้ไฟล์ results.csv เป็นไฟล์รับผลลัพธ์ที่ได้กลับคืนมา
$ perl noxml_google2csv.pl "no xml" > results.csv
หรือจะตัดส่วนของ > และ results.csv ออก เพื่อส่งผลลัพธ์ให้ไปแสดงที่หน้าจอเพื่อตรวจสอบก่อนก็ได้ และเห็นผลลัพธ์ทันที
ผลลัพธ์
% perl noxml_google2csv.pl "no xml"
"title","url","snippet"
"site-comments@w3.org from January 2002: No XML specifications",
"http://lists.w3.org/Archives/Public/site-comments/2002Jan/0015.html",
"No XML specifications. From: Prof. ... Next message: Ian B. Jacobs:
"Re: No XML specifications"; Previous message: Rob Cummings:
"Website design..."; ... "
...
"Re: [xml] XPath with no XML Doc",
"http://mail.gnome.org/archives/xml/2002-March/msg00194.html",
" ... Re: [xml] XPath with no XML Doc. From: "Richard Jinks"
; To: ; Subject:
Re: [xml] XPath with no XML Doc; ... "
การใช้งานและข้อจำกัด
ด้วย วิธีเดียวกันนี้ คุณสามารถดัดแปลงวิธีการแฮ็กด้วย SOAP::Lite ที่ได้อธิบายไว้ในหัวข้อต่างๆ ตลอดหนังสือเล่มนี้ มาเป็นการใช้ PoXML ในการแฮ็กแทนก็ได้ โดยมีข้อกำหนดดังนี้
- วางไฟล์ NoXML.pm ไว้ในไดเรกทอรีเดียวกันกับสคริปต์ของเรื่องที่จะแฮ็ก
- แทนที่บรรทัดในสคริปต์ที่เป็น use SOAP::Lite; ด้วย use NoXML;
- แทนที่ my $google_search = SOAP::Lite->service(“file:$google_wdsl”); ด้วย my $google_search = new NoXML;
อย่าง ไรก็ตาม ยังมีข้อจำกัดในการใช้งานอยู่บ้าง ในขณะที่ NoXML ทำงานได้ดีในการดึงผลลัพธ์รวมทั้งสรุปผลลัพธ์ที่ได้จากการค้นหา แต่ก็ไม่ประสบผลสำเร็จในด้านการรวบรวมผลลัพธ์ในขั้นละเอียดขึ้น (advanced result) บางอย่าง เช่น
เป็นต้นดูเพิ่มเติม
PoXML [Hack #53] เป็นทางเลือกในการใช้ plain old XML แทน SOAP::Lite
XooMLE [Hack #36] เป็น third-party service ซึ่งเป็นตัวกลาในการ interface ระหว่าง Plain Old XML กับ Google Web API