วันพุธที่ 8 สิงหาคม พ.ศ. 2550

HACK#59 การเขียนโปรแกรม Google Web API ด้วย VB.NET

สร้าง Google Search Application แบบ GUI ด้วย Visual Basic และ .NET Framework

นอกจากที่มีตัวอย่างในการพัฒนา Google Search Application ด้วย C# [Hack #58] แล้ว Google Web APIs Developer’s Kit ได้มีตัวอย่างของ Google Search ใน Visual Basic รวมเอาไว้อยู่ด้วยเช่นกัน ในขณะที่คุณอาจจะผสมผเสรวบรวมสิ่งต่างๆที่จำเป็นเกือบจะทั้งหมดจาก Google Demo Form.vb ซึ่งเป็นโค้ดที่มีมาให้อยู่ในชุด kit ดังกล่าวอยู่แล้ว การแฮ็กในหัวข้อนี้ก็ได้เตรียมโค้ดพื้นฐานอันเป็นเบื้องต้นสำหรับ Google Search Application โดยปราศจากข้อกังขาในการพัฒนาถึงขั้นความสามารถอันเต็มพิกัดของโปรเจ็ค Visual Studio .NET

  • Tip: การคอมไพล์และรันการแฮ็กนี้จำเป็นต้องมี .NET Framework (http://msdn.microsoft.com/library/default.asp?url=/nhp/default.asp?contentid=28000519) ติดตั้งอยู่ด้วย

โค้ดตัวอย่าง

' googly.vb

' A Google Web API VB.NET console application

' Usage: googly.exe

' Copyright (c) 2002, Chris Sells.

' No warranties extended. Use at your own risk.

Imports System

Module Googly

Sub Main(ByVal args As String( ))

' Your Google API developer's key

Dim googleKey As String = "insert key here"

' Take the query from the command-line

If args.Length <> 1 Then

Console.WriteLine("Usage: google.exe ")

Return

End If

Dim query As String = args(0)

' Create a Google SOAP client proxy, generated by:

' c:\> wsdl.exe /l:vb http://api.google.com/GoogleSearch.wsdl

Dim googleSearch As GoogleSearchService = New GoogleSearchService( )

' Query Google

Dim results As GoogleSearchResult = googleSearch.doGoogleSearch(googleKey, query, 0, 10, False, "", False, "", "latin1", "latin1")

' No results?

If results.resultElements Is Nothing Then Return

' Loop through results

Dim result As ResultElement

For Each result In results.resultElements

Console.WriteLine( )

Console.WriteLine(result.title)

Console.WriteLine(result.URL)

Console.WriteLine(result.snippet)

Console.WriteLine( )

Next

End Sub

End Module

โปรดอย่าลืมใส่ Google Developer Key (เช่น 12BuCK13mY5h0E/34KNocK@ttH3DoOR)

ของคุณลงใน “insert key here”

‘Your Google API developer’s key

Dim googleKey As String = “12BuCK13mY5h0E/34KNocK@ttH3DoOR”

การคอมไพล์โค้ด

ก่อนที่จะคอมไพล์โค้ด VB Application ได้นั้น คุณจะต้องสร้าง Google SOAP Client Proxy เสียก่อน ซึ่ง Google SOAP Client Proxy นี้จะเป็นโค้ดจำนวนหนึ่งที่สร้างตามรายละเอียด (specification) ในไฟล์ GoogleSearch.wsdl ซึ่งจะประกอบด้วย XML based description เกี่ยวกับ Google Web Service, mothod, parameter, และ return value แต่นับว่าโชคดีที่ .NET Framework kit ที่มีมาให้นั้น มีไฟล์ wsdl.exe ซึ่งทำให้คุณไม่ต้องมาเขียนโค้ดด้วยตัวคุณเอง

  • Tip: นี่อาจเป็นสิ่งที่แปลกสักหน่อยหากคุณได้ลองนึกดู เนื่องจากส่วนสำคัญในการเชื่อมต่อกับ Web Service กลับถูกสร้างขึ้นจาก description ของมันเองโดยอัตโนมัติ

เรียกไฟล์ wsdl.exe จากไดเรกทอรีที่ GoogleSearch.wsdl อยู่ ดังนี้

C:\GOOGLY.NET>wsdl.exe /l:vb GoogleSearch.wsdl

คุณอาจจะระบุถึงไฟล์ wsdl.exe ที่อยู่ในเว็บไซต์ของ Google ก็ได้เช่นกัน

C:\GOOGLY.NET\VB>wsdl.exe /l:vb http://api.google.com/GoogleSearch.wsdl

Microsoft (R) Web Services Description Language Utility

[Microsoft (R) .NET Framework, Version 1.0.3705.0]

Copyright (C) Microsoft Corporation 1998-2001. All rights reserved.

Writing file 'C:\GOOGLY.NET\VB\GoogleSearchService.vb'.

ผลลัพธ์ที่คุณจะได้รับไฟล์ GoogleSearchService.vb นั้นจะมีหน้าตาลักษณะนี้

'---------------------------------------------------------------------------

' <autogenerated>

' This code was generated by a tool.

' Runtime Version: 1.0.3705.288

'

' Changes to this file may cause incorrect behavior and will be lost if

' the code is regenerated.

' </autogenerated>

'---------------------------------------------------------------------------

Option Strict Off

Option Explicit On

Imports System

Imports System.ComponentModel

Imports System.Diagnostics

Imports System.Web.Services

Imports System.Web.Services.Protocols

Imports System.Xml.Serialization

...

Public Function BegindoGoogleSearch(ByVal key As String, ByVal q As

String, ByVal start As Integer, ByVal maxResults As Integer, ByVal

filter As Boolean, ByVal restrict As String, ByVal safeSearch As

Boolean, ByVal lr As String, ByVal ie As String, ByVal oe As String,

ByVal callback As System.AsyncCallback, ByVal asyncState As Object) As

System.IAsyncResult

Return Me.BeginInvoke("doGoogleSearch", New Object( ) {key, q,

start, maxResults, filter, restrict, safeSearch, lr, ie, oe}, callback,

asyncState) End Function

'<remarks/>

Public Function EnddoGoogleSearch(ByVal asyncResult As System.IAsyncResult) As GoogleSearchResult

Dim results( ) As Object = Me.EndInvoke(asyncResult)

Return CType(results(0),GoogleSearchResult)

End Function

End Class

สำหรับการคอมไพล์ไฟล์ googly.vb

C:\GOOGLY.NET\VB>vbc /out:googly.exe *.vb

Microsoft (R) Visual Basic .NET Compiler version 7.00.9466

for Microsoft (R) .NET Framework version 1.00.3705

Copyright (C) Microsoft Corporation 1987-2001. All rights reserved.

Running the Hack

รัน Googly ที่ command line โดยการใส่คำถามที่ต้องการเข้าไปแทนที่ query word

C:\GOOGLY.NET\VB>googly.exe “query words”

  • Tip: หน้าต่างของ dos command อาจจะไม่กว้างพอสำหรับการแสดงผล ซึ่งคุณสามารถแก้ปัญหาได้โดยการส่งผลลัพธ์ที่ได้จากการค้นหาไปยังไฟล์อื่นใด เพื่อใช้เอดิเตอร์ (editor) ตรวจดูผลลัพธ์ได้ ซึ่งการส่งผลลัพธ์ไปยังไฟล์ดังกล่าวทำได้โดย เพิ่ม > results.txt ต่อท้ายเข้าไปที่คำสั่งข้างต้น

ผลลัพธ์

ขั้นตอนต่างๆของการแฮ็กในหัวข้อนี้จะเหมือนกับในส่วนของ C# [Hack #58] และตามปกติเมื่อสั่งรันแล้วก็ควรจะได้ผลลัพธ์เหมือนๆกันด้วยเช่นกัน

HACK#58 การโปรแกรม Google Web API ด้วย C# และ .NET

สร้าง Google Search Application แบบ GUI ด้วย C# และ .NET framework

ใน Google Web API Developer’s Kit [ดู “The Google Web APIs Developer’s Kit”] มีตัวอย่างของ C# Visual Studio .NET (http://msdn.microsoft.com/vstudio) ซึ่งเป็น project ทางด้าน GUI Google Search Application (ดูในโฟลเดอร์ dotnet/cSharp) ซึ่งส่วนที่น่าสนใจที่สุดน่าจะเป็นโค้ดในไฟล์ Form1.cs

การแฮ็กในหัวข้อนี้จะเป็นการเตรียมโค้ดเบื้องต้นสำหรับ Google Search Application ซึ่งจะคล้ายๆกับการทำงานใน Perl [Hack #50], Java [Hack #56] และ Python [Hack #57]

  • Tip:การคอมไพล์และรันการแฮ็กนี้จำเป็นต้องมี .NET Framework (http://msdn.microsoft.com/library/default.asp?url=/nhp/default.asp?contentid=28000519) ติดตั้งในเครื่องของคุณอยู่ด้วย

โค้ดตัวอย่าง

// googly.cs

// A Google Web API C# console application

// Usage: googly.exe

// Copyright (c) 2002, Chris Sells.

// No warranties extended. Use at your own risk.

using System;

class Googly {

static void Main(string[] args) {

// Your Google API developer's key

string googleKey = "insert key here";

// Take the query from the command-line

if( args.Length != 1 ) {

Console.WriteLine("Usage: google.exe ");

return;

}

string query = args[0];

// Create a Google SOAP client proxy, generated by:

// c:\> wsdl.exe http://api.google.com/GoogleSearch.wsdl

GoogleSearchService googleSearch = new GoogleSearchService( );

// Query Google

GoogleSearchResult results = googleSearch.doGoogleSearch(googleKey, query, 0, 10, false, "", false, "", "latin1", "latin1");

// No results?

if( results.resultElements == null ) return;

// Loop through results

foreach( ResultElement result in results.resultElements ) {

Console.WriteLine( );

Console.WriteLine(result.title);

Console.WriteLine(result.URL);

Console.WriteLine(result.snippet);

Console.WriteLine( );

}

}

}

อย่าลืมใส่ Google Developer’s key [ดู “Using the key in the hack”] (เช่น 12BuCK13mY5h0E/34KNocK@ttH3DoOR) ของคุณลงไปแทนใน “insert key here”

//Your Google API developer’s key

string googleKey = “12BuCK13mY5h0E/34KNocK@ttH3DoOR”;

การคอมไพล์โค้ด

ก่อนที่จะคอมไพล์โค้ด C# ได้นั้น คุณจะต้องสร้าง Google SOAP Client Proxy เสียก่อน ซึ่ง Google SOAP Client Proxy นี้จะเป็นโค้ดจำนวนหนึ่งที่สร้างตาม specification ในไฟล์ GoogleSearch.wsdl ซึ่งจะประกอบด้วย XML based description เกี่ยวกับ Google Web Service, mothod ทั้งหมดของมัน และ parameter ต่างๆ รวมถึง return value ด้วย แต่นับว่าโชคดีที่ .NET Framework Kit ที่มีมาให้ มีไฟล์ wsdl.exe ซึ่งทำให้คุณไม่ต้องมานั่งเขียนโค้ดด้วยตัวคุณเอง

  • Tip: สิ่งที่น่าสนใจในเรื่องนี้ก็คือ ส่วนสำคัญในการอินเทอร์เฟสกับ web service ที่ถูกสร้างขึ้นจาก description ของมันเอง
เรียกไฟล์ wsdl.exe จากไดเรกทอรีที่ GoogleSearch.wsdl อยู่ ดังนี้

C:\GOOGLY.NET>wsdl.exe GoogleSearch.wsdl

คุณอาจจะระบุถึงไฟล์ wsdl.exe ที่อยู่ในเว็บไซต์ของ Google ก็ได้เช่นกัน

C:\GOOGLY.NET\CS>wsdl.exe http://api.google.com/GoogleSearch.wsdl

Microsoft (R) Web Services Description Language Utility

[Microsoft (R) .NET Framework, Version 1.0.3705.0]

Copyright (C) Microsoft Corporation 1998-2001. All rights reserved.

Writing file 'C:\GOOGLY.NET\CS\GoogleSearchService.cs'.

ผลลัพธ์รายการสุดท้าย ซึ่งก็คือไฟล์ GoogleSearchService.cs จะมีหน้าตาลักษณะนี้

//--------------------------------------------------------------------------

// <autogenerated>

// This code was generated by a tool.

// Runtime Version: 1.0.3705.288

//

// Changes to this file may cause incorrect behavior and will be lost if

// the code is regenerated.

// </autogenerated>

//--------------------------------------------------------------------------

//

// This source code was auto-generated by wsdl, Version=1.0.3705.288.

//

using System.Diagnostics;

using System.Xml.Serialization;

using System;

using System.Web.Services.Protocols;

using System.ComponentModel;

using System.Web.Services;

...

public System.IAsyncResult BegindoGoogleSearch(string key,

string q, int start, int maxResults, bool filter, string restrict,

bool safeSearch, string lr, string ie, string oe,

System.AsyncCallback callback, object asyncState) {

return this.BeginInvoke("doGoogleSearch", new object[] {

key,

q,

start,

maxResults,

filter,

restrict,

safeSearch,

lr,

ie,

oe}, callback, asyncState);

}


สำหรับไฟล์ googly.cs

C:\GOOGLY.NET\CS>csc /out:googly.exe *.cs

Microsoft (R) Visual C# .NET Compiler version 7.00.9466

for Microsoft (R) .NET Framework version 1.0.3705

Copyright (C) Microsoft Corporation 2001. All rights reserved.

Running the Hack

รัน Googly ที่ command line โดยการใส่คำถามที่ต้องการเข้าไปแทนที่ “query word”

C:\GOOGLY.NET\CS>googly.exe “query words”

  • Tip: หน้าต่างของ dos command อาจจะไม่กว้างพอสำหรับการแสดงผล ซึ่งคุณสามารถแก้ปัญหาได้โดยการส่งผลลัพธ์ที่ได้จากการค้นหาไปยังไฟล์อื่นใด เพื่อใช้เอดิเตอร์ (editor) ในการตรวจดูผลลัพธ์ก่อน ซึ่งการส่งผลลัพธ์ไปยังไฟล์ดังกล่าวทำได้โดย เพิ่ม > results.txt ต่อท้ายเข้าไปที่คำสั่ง

ผลลัพธ์

% googly.exe "WSDL while you work"

Axis/Radio interop, actual and potential

http://www.intertwingly.net/stories/2002/02/08/

axisradioInteropActualAndPotential.html <b>...</b> But

<b>you</b> might find more exciting services here

<b>...</b> Instead, we should <b>work</b>

together and<br> continuously strive to <b>...</b>

<b>While</b> <b>WSDL</b> is certainly far from

perfect and has many <b>...</b>

...

Simplified <b>WSDL</b>

http://capescience.capeclear.com/articles/simplifiedWSDL/

<b>...</b> So how does it <b>work</b>?

<b>...</b> If <b>you</b> would like to edit

<b>WSDL</b> <b>while</b> still avoiding<br> all

those XML tags, check out the <b>WSDL</b> Editor in

CapeStudio. <b>...</b>

HACK#57 การเขียนโปรแกรม Google Web API ด้วย Python

การโปรแกรม Google Web API ด้วย Python นี้ค่อนข้างง่าย ลองดูไปตามที่สคริปต์และตัวอย่างนี้ได้สาธิตเอาไว้

การเขียนโปรแกรม Google Web API ด้วย Python เป็นสิ่งที่ง่ายเป็นอย่างมาก และต้องขอขอบคุณ PyGoogle wrapper module ของ Mark Pilgrim (http://diveintomark.org/projects/pygoogle/) ซึ่งได้เตรียมสิ่งต่างๆสำหรับรองรับ SOAP, XML และ request/response layer ช่วยให้คุณมีเวลาเหลือสำหรับศึกษาข้อมูลจากสิ่งเหล่านั้นมากขึ้น

การติดตั้ง PyGoogle

ให้ดาวน์โหลด PyGoogle มา แล้วทำตามคำแนะนำในไฟล์ readme.txt (http://diveintomark.org/projects/pygoogle/readme.txt) ซึ่งถ้าไม่ติดขัดประการใด การติดตั้งไม่น่าจะยุ่งยากไปกว่าเพียงแค่ใช้คำสั่ง

% python setup.py install

อีกวิธีหนึ่ง ถ้าคุณต้องการไปอย่างรวดเร็วและไม่ต้องการติดตั้ง PyGoogle หรือต้องการติดตั้งแต่ไม่ได้รับอนุญาต(Permission) ในการติดตั้งในระบบของคุณ คุณก็เพียงแต่บันทึกไฟล์ SOAP.py และ google.py ลงไปในไดเรกทอรีเดียวกันเท่านั้น

โค้ดตัวอย่าง

#!/usr/bin/python

# googly.py

# A typical Google Web API Python script using Mark Pilgrim's

# PyGoogle Google Web API wrapper

# [http://diveintomark.org/projects/pygoogle/]

# Usage: python googly.py

import sys, string, codecs

# Use the PyGoogle module

import google

# Grab the query from the command-line

if sys.argv[1:]:

query = sys.argv[1]

else:

sys.exit('Usage: python googly.py ')

# Your Google API developer's key

google.LICENSE_KEY = 'insert key here'

# Query Google

data = google.doGoogleSearch(query)

# Teach standard output to deal with utf-8 encoding in the results

sys.stdout = codecs.lookup('utf-8')[-1](sys.stdout)

# Output

for result in data.results:

print string.join( (result.title, result.URL, result.snippet), "\n"), "\n"

Running the Hack

สั่งสคริปต์ให้ทำงานที่ command line ด้วยคำสั่งดังนี้

% python googly.py "query words"

ผลลัพธ์

% python googly.py "learning python"

oreilly.com -- Online Catalog: <b>Learning</b>

<b>Python</b>

http://www.oreilly.com/catalog/lpython/

<b>Learning</b> <b>Python</b> is an

introduction to the increasingly popular interpreted programming

language that's portable, powerful, and remarkably easy to use in both

<b>...</b>

...

Book Review: <b>Learning</b> <b>Python</b>

http://www2.linuxjournal.com/lj-issues/issue66/3541.html

<b>...</b> Issue 66: Book Review: <b>Learning</b>

<b>Python</b> <b>...</b> Enter

<b>Learning</b> <b>Python</b>. My executive summary

is that this is the right book for me and probably for many others

as well. <b>...</b>

Hacking the Hack

Python มีความสามารถในการเชื่อมต่อการทำงานแบบ Interactive ร่วมกับตัวแปลภาษา (Interpreter) ได้อย่างดีเยี่ยม ซึ่งเป็นที่ที่ดีในการทดลองกับโมดูลต่างๆอย่างเช่น PyGoogle เป็นต้น ซึ่งจะทำให้การส่งค่า Google Web API อย่างรวดเร็ว รวมทั้งค้นหาตลอดโครงสร้างข้อมูล (Data Structure) ที่ได้รับมา

ข้างล่างนี้คือตัวอย่างแบบ Interactive ของ PyGoogle Session ซึ่งใช้ฟังก์ชัน doGoogleSearch, deGetCatchedPage และ doSpellingSuggestion ในการทำงาน

% python

Python 2.2 (#1, 07/14/02, 23:25:09)

[GCC Apple cpp-precomp 6.14] on darwin

Type "help", "copyright", "credits" or "license" for more information.

>>> import google

>>> google.LICENSE_KEY = 'insert key here'

>>> data = google.doGoogleSearch("Learning Python")

>>> dir(data.meta)

['_ _doc_ _', '_ _init_ _', '_ _module_ _', 'directoryCategories',

'documentFiltering', 'endIndex', 'estimateIsExact',

'estimatedTotalResultsCount', 'searchComments', 'searchQuery',

'searchTime', 'searchTips', 'startIndex']

>>> data.meta.estimatedTotalResultsCount

115000

>>> data.meta.directoryCategories

[{u'specialEncoding': '', u'fullViewableName': "Top/Business/Industries/

Publishing/Publishers/Nonfiction/Business/O'Reilly_and_Associates/

Technical_Books/Python"}]

>>> dir(data.results[5])

['URL', '_ _doc_ _', '_ _init_ _', '_ _module_ _', 'cachedSize',

'directoryCategory', 'directoryTitle', 'hostName',

'relatedInformationPresent', 'snippet', 'summary', 'title']

>>> data.results[0].title

'oreilly.com -- Online Catalog: <b>Learning</b> <b>Python'

>>> data.results[0].URL

'http://www.oreilly.com/catalog/lpython/'

>>> google.doGetCachedPage(data.results[0].URL)

'<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">\n

<BASE HREF="http://www.oreilly.com/catalog/lpython/"><table border=1

...

>>> google.doSpellingSuggestion('lurn piethon')

'learn python'

HACK#56 การโปรแกรม Google Web API ด้วย Java

การโปรแกรม Google Web API ในจาวานั้นสามารถทำใด้อย่างรวดเร็ว เนื่องจากมีไลบรารี่ให้มาพร้อมกับ Google Web API Developer’s Kit อยู่แล้ว

ต้องขอขอบคุณ Java Archive (JAR) ซึ่งมีมาให้พร้อมกับ Google Web API Developer’s Kit [ใน “The Google Web APIs Developer’s Kit”] ซึ่งใน googleapi.jar archive ได้รวมเอา com.google.soap.search ซึ่งเป็น wrapper ที่ดีมากมาให้ด้วย พร้อมทั้ง Crimson XML parser (http://xml.apache.org/crimson) ของ Apache Software Foundation และตัวสุดท้ายที่มาพร้อมกันก็คือ Apache SOAP stack (http://xml.apache.org/soap)

  • Tip:คุณอาจจำเป็นต้องใช้ copy ของ Java 2 Platform, Standard Edition (J2SE, http://java.sun.com/downloads/) ในการคอมไพล์และรันการแฮ็กด้วยวิธีนี้
โค้ดตัวอย่าง

// Googly.java

// Bring in the Google SOAP wrapper

import com.google.soap.search.*;

import java.io.*;

public class Googly {

// Your Google API developer's key

private static String googleKey = "insert key here";

public static void main(String[] args) {

// Make sure there's a Google query on the command-line

if (args.length != 1) {

System.err.println("Usage: java [-classpath classpath] Googly ");

System.exit(1);

}

// Create a new GoogleSearch object

GoogleSearch s = new GoogleSearch( );

try {

s.setKey(googleKey);

s.setQueryString(args[0]); // Google query from the command-line

s.setMaxResults(10);

// Query Google

GoogleSearchResult r = s.doSearch( );

// Gather the results

GoogleSearchResultElement[] re = r.getResultElements( );

// Output

for ( int i = 0; i <>

System.out.println(re[i].getTitle( ));

System.out.println(re[i].getURL( ));

System.out.println(re[i].getSnippet( ) + "\n");

}

// Anything go wrong?

} catch (GoogleSearchFault f) {

System.out.println("GoogleSearchFault: " + f.toString( ));

}

}

}


โปรดอย่าลืมใส่ Google Developer Key (เช่น 12BuCK13mY5h0E/34KNocK@ttH3DoOR) ของคุณลงไปแทนใน “insert key here”

//Your Google API developer’s key

private static String googleKey = “12BuCK13mY5h0E/34KNocK@ttH3DoOR”;

การคอมไพล์โค้ด

สำหรับการคอมไพล์ Googly application (Googly.java) คุณจำเป็นต้องมี googleapi.jar archive ก่อน สำหรับผู้เขียนได้เก็บเอาไว้ในไดเรกทอรีเดียวกับ googly.java แต่ถ้าหากคุณเก็บไว้ในที่อื่น ให้เปลี่ยนพาธหลัง –classpath ไปตามนั้น

% javac -classpath googleapi.jar Googly.java

ด้วยวิธีนี้คุณจะได้ไฟล์ Googly.class ใหม่ ซึ่งพร้อมที่จะทำงานได้แล้ว

Running the Hack

รัน Googly ที่ command line โดยการใส่คำถามที่ต้องการลงไปแทนที่ “query word”

% java -classpath .:googleapi.jar Googly "query words"

ผลลัพธ์

% java -classpath .:googleapi.jar Googly "Learning Java"

oreilly.com -- Online Catalog: Learning Java

http://www.oreilly.com/catalog/learnjava/

For programmers either just migrating to Java or already working

steadily in the forefront of Java development, Learning Java gives

a clear, systematic ...

oreilly.com -- Online Catalog: Learning Java , 2nd Edition

http://www.oreilly.com/catalog/learnjava2/

This new edition of Learning Java has been expanded and updated for

Java 2 Standard Edition SDK 1.4. It comprehensively addresses ...

...

Java Programming...From the Grounds Up / Web Developer

http://www.webdeveloper.com/java/java_programming_grounds_up.html

... WebDeveloper.com. Java Programming... From the Grounds Up. by

Mark C. Reynolds ... Java Classes and Methods. Java utilizes the

basic object technology found in C++. ...

HACK#55 การเขียนโปรแกรม Google Web API ด้วย PHP

ตัวอย่างง่ายๆในการโปรแกรม Google Web API ด้วย PHP และโมดูล NuSOAP

PHP (http://www.php.net) ซึ่งย่อมาจาก “PHP Hypertext Processing” เป็นภาษาสคริปต์ที่ใช้งานร่วมกับ HTML และได้รับการยอมรับเป็นอย่างมาก ในการพัฒนาโปรแกรมทางด้านเว็บ และสำหรับการเพิ่มโมดูลของ PHP ที่ชื่อ NuSOAP เข้าไป (http://dietrich.ganx4.com/nusoap) จะทำให้สามารถสร้างและใช้งาน SOAP based web service ได้

การแฮ็กในหัวข้อนี้ จะแสดงให้เห็นการใช้งาน PHP และ NuSOAP ในการทำงานร่วมกับ Google Web API

โค้ดตัวอย่าง

<!--

# googly.php

# A typical Google Web API php script

# Usage: googly.php?query=<query>

-->

<html>

<head>

<title>googly.php</title>

</head>

<body>

<?

# Use the NuSOAP php library

require_once('nusoap.php');

# Set parameters

$parameters = array(

'key'=>'insert key here',

'q' => $HTTP_GET_VARS['query'],

'start' => '0',

'maxResults' => '10',

'filter' => 'false',

'restrict' => '',

'safeSearch' => 'false',

'lr' => '',

'ie' => 'latin',

'oe' => 'latin'

);

# Create a new SOAP client, feeding it GoogleSearch.wsdl on Google's site

$soapclient = new soapclient('http://api.google.com/GoogleSearch.wsdl', 'wsdl');

# query Google

$results = $soapclient->call('doGoogleSearch',$parameters);

# Results?

if ( is_array($results['resultElements']) ) {

print "<p>Your Google query for '" . $HTTP_GET_VARS['query'] . "' found " . $results['estimatedTotalResultsCount'] . " results, the top ten of which are:</p>";

foreach ( $results['resultElements'] as $result ) {

print

"<p><a href='" . $result['URL'] . "'>" .

( $result['title'] ? $result['title'] : 'no title' ) .

"</a><br />" . $result['URL'] . "<br />" .

( $result['snippet'] ? $result['snippet'] : 'no snippet' ) .

"</p>";

}

}

# No Results

else {

print "Your Google query for '" . $HTTP_GET_VARS['query'] . "' returned no results";

}

?>

</body>

</html>

Running the Hack

ดำเนินการแฮ็กด้วยวิธีการเดียวกันกับที่คุณทำในการเรียกใช้ CGI Script ทั่วไป โดยให้คุณใส่คำถาม (query) ที่ต้องการลงไปแทน your google query

http://localhost/googly.php?query=your google query

ผลลัพธ์

จากภาพที่ 5-1 เป็นการค้นหาด้วยคำว่า php

รูปที่ 5-1 ผลลัพธ์จากการค้นหาโดยใช้คำว่า PHP

HACK#54 NoXML อีกทางเลือกหนึ่งสำหรับใช้แทน SOAP::Lite

NoXML เป็นทางเลือกในการใช้แทน SOAP::Lite ซึ่งใช้เพียง regular expression ทำงาน โดยไม่ต้องมี XML parser แต่อย่างใด

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 สามารถใช้แทน SOAP::Lite โดยการแก้ไขสคริปต์ที่ใช้ในการแฮ็กเพียงเล็กน้อยเท่านั้น

โค้ดของ 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 = ('<'=>'<', '>'=>'>', '&'=>'&amp;amp;', '"'=>'"', '''=>"'");

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_ _

<?xml version='1.0' encoding='UTF-8'?>

<SOAP-ENV:Envelope

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:Body>

<ns1:doGoogleSearch xmlns:ns1="urn:GoogleSearch"

SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">

<key xsi:type="xsd:string">$key</key>

<q xsi:type="xsd:string">$q</q>

<start xsi:type="xsd:int">$start</start>

<maxResults xsi:type="xsd:int">$maxResults</maxResults>

<filter xsi:type="xsd:boolean">$filter</filter>

<restrict xsi:type="xsd:string">$restrict</restrict>

<safeSearch xsi:type="xsd:boolean">$safeSearch</safeSearch>

<lr xsi:type="xsd:string">$lr</lr>

<ie xsi:type="xsd:string">$ie</ie>

<oe xsi:type="xsd:string">$oe</oe>

</ns1:doGoogleSearch>

</SOAP-ENV:Body>

</SOAP-ENV:Envelope>


ข้างล่างนี้เป็นตัวอย่างสคริปต์ 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 ในการแฮ็กแทนก็ได้ โดยมีข้อกำหนดดังนี้

  1. วางไฟล์ NoXML.pm ไว้ในไดเรกทอรีเดียวกันกับสคริปต์ของเรื่องที่จะแฮ็ก
  2. แทนที่บรรทัดในสคริปต์ที่เป็น use SOAP::Lite; ด้วย use NoXML;
  3. แทนที่ my $google_search = SOAP::Lite->service(“file:$google_wdsl”); ด้วย my $google_search = new NoXML;

อย่างไรก็ตาม ยังมีข้อจำกัดในการใช้งานอยู่บ้าง ในขณะที่ NoXML ทำงานได้ดีในการดึงผลลัพธ์รวมทั้งสรุปผลลัพธ์ที่ได้จากการค้นหา แต่ก็ไม่ประสบผลสำเร็จในด้านการรวบรวมผลลัพธ์ในขั้นละเอียดขึ้น (advanced result) บางอย่าง เช่น <directoryCategories> เป็นต้น

ดูเพิ่มเติม

PoXML [Hack #53] เป็นทางเลือกในการใช้ plain old XML แทน SOAP::Lite

XooMLE [Hack #36] เป็น third-party service ซึ่งเป็นตัวกลาในการ interface ระหว่าง Plain Old XML กับ Google Web API

HACK#53 Plain Old XML

PoXML เป็นโมดูลที่สามารถใช้แทน SOAP::Lite ในกรณีที่ไม่สามารถติดตั้ง SOAP::Lite ได้

PoXML นับเป็นทางเลือกทางหนึ่งสำหรับท่านที่ไม่สามารถใช้ SOAP::Lite [Hack #52] ได้ ซึ่งอาจจะเป็นด้วยเพราะการติดตั้งที่ยุ่งยากหรืออะไรก็ตามแต่
  • Tip: ผู้รู้เกี่ยวกับ Perl ยืนยันว่าการติดตั้งโมดูลนี้มีขั้นตอนที่ง่ายมาก อย่างไรก็ตาม หลายๆคนแย้งว่า การติดตั้งไม่ได้ง่ายดายอย่างที่กล่าวเอาไว้นัก
PoXML นั้นสามารถใช้แทน SOAP::Lite ได้ในระดับที่ดีพอใช้ โดยการที่ทำงานร่วมกับ SOAP ของ Google ตามแบบ Plain Old XML โดยการใช้ LWP::UserAgent ในการสร้าง HTTP request และใช้ XML::Simple ในการประมวลผล XML แต่ส่วนที่เป็นข้อดีที่สุดก็คือ มีการแก้ไขสคริปต์เพียง 2 บรรทัดเท่านั้น เมื่อมีการใช้งานแทน SOAP::Lite ในการแฮ็กเป้าหมายต่างๆในหนังสือเล่มนี้

โค้ดของ PoXML

ไฟล์สำคัญที่ใช้ในหัวข้อนี้ก็คือไฟล์ PoXML.pm ซึ่งเป็นเพิร์ลโมดูลเล็กๆที่เหมาะสมที่จะบันทึกเก็บไว้ในไดเรกทอรีเดียวกันกับสคริปต์ที่ใช้ในการแฮ็ก

ประมวลผล XML แต่ส่วนที่เป็นข้อดีที่สุดก็คือ มีการแก้ไขสคริปต์เพียง 2 บรรทัดเท่านั้น เมื่อมีการใช้งานแทน SOAP::Lite ในการแฮ็กเป้าหมายต่างๆในหนังสือเล่มนี้

โค้ดของ PoXML

ไฟล์สำคัญที่ใช้ในหัวข้อนี้ก็คือไฟล์ PoXML.pm ซึ่งเป็นเพิร์ลโมดูลเล็กๆที่เหมาะสมที่จะบันทึกเก็บไว้ในไดเรกทอรีเดียวกันกับสคริปต์ที่ใช้ในการแฮ็ก

# PoXML.pm

# PoXML [pronounced "plain old xml"] is a dire-need drop-in

# replacement for SOAP::Lite designed for Google Web API hacking.

package PoXML;

use strict;

no strict "refs";

# LWP for making HTTP requests, XML for parsing Google SOAP

use LWP::UserAgent;

use XML::Simple;

# Create a new PoXML

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;

# Parse the XML

my $results = XMLin($soap_response);

# Normalize and drop the unnecessary encoding bits

my $return = $results->{'Body'}->{'doGoogleSearchResponse'}->{return};

foreach ( keys %{$return} ) {

$return->{$_}->{content} and

$return->{$_} = $return->{$_}->{content} || '';

}

my @items;

foreach my $item ( @{$return->{resultElements}->{item}} ) {

foreach my $key ( keys %$item ) {

$item->{$key} = $item->{$key}->{content} || '';

}

push @items, $item;

}

$return->{resultElements} = \@items;

my @categories;

foreach my $key ( keys %{$return->{directoryCategories}->{item}} ) {

$return->{directoryCategories}->{$key} =

$return->{directoryCategories}->{item}->{$key}->{content} || '';

}

# 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_ _

<?xml version='1.0' encoding='UTF-8'?>

<SOAP-ENV:Envelope

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:Body>

<ns1:doGoogleSearch xmlns:ns1="urn:GoogleSearch"

SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">

<key xsi:type="xsd:string">$key</key>

<q xsi:type="xsd:string">$q</q>

<start xsi:type="xsd:int">$start</start>

<maxResults xsi:type="xsd:int">$maxResults</maxResults>

<filter xsi:type="xsd:boolean">$filter</filter>

<restrict xsi:type="xsd:string">$restrict</restrict>

<safeSearch xsi:type="xsd:boolean">$safeSearch</safeSearch>

<lr xsi:type="xsd:string">$lr</lr>

<ie xsi:type="xsd:string">$ie</ie>

<oe xsi:type="xsd:string">$oe</oe>

</ns1:doGoogleSearch>

</SOAP-ENV:Body>

</SOAP-ENV:Envelope>

ข้างล่างนี้เป็นตัวอย่างสคริปต์ PoXML ในแง่การใช้งาน ซึ่งก็ไม่แตกต่างอะไรไปจากการแฮ็กในหัวข้ออื่นๆในหนังสือเล่มนี้ จะมีแต่ก็เพียงการเน้นข้อความเป็นตัวหนา (bold) ตรงส่วนที่มีการ comment ในบรรทัดที่เดิมใช้กับ SOAP::Lite และแทรกบรรทัดใหม่ เพื่อให้ใช้กับ PoXML ได้เท่านั้น

#!/usr/bin/perl

# poxml_google2csv.pl

# Google Web Search Results via PoXML ("plain old xml") module

# exported to CSV suitable for import into Excel

# Usage: poxml_google2csv.pl "{query}" [> results.csv]

# Your Google API developer's key

my $google_key = 'insert key here';

use strict;

# use SOAP::Lite;

use PoXML;

$ARGV[0]

or die qq{usage: perl poxml_search2csv.pl "{query}"\n};

# my $google_search = SOAP::Lite->service("file:$google_wdsl");

my $google_search = new PoXML;

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 ที่คุณต้องการสร้างขึ้นมา หรือที่ต้องการให้นำผลลัพธ์ไปต่อท้าย (เดิมมีผลลัพธ์อยู่บ้างแล้ว) ตัวอย่างเช่นข้างล่างนี้ใช้ “plain old xml” เป็นคำถาม และกำหนดไฟล์ results.csv เป็นไฟล์รับผลลัพธ์ที่ได้กลับคืนมา

$ perl poxml_google2csv.pl "plain old xml" > results.csv

หรือจะตัดส่วนของ > และ results.csv ออก เพื่อส่งผลลัพธ์ให้ไปแสดงที่หน้าจอเพื่อตรวจสอบก่อนก็ได้

ผลลัพธ์

% perl poxml_google2csv.pl "plain old xml"

"title","url","snippet"

"XML.com: Distributed XML [Sep. 06, 2000]",

"http://www.xml.com/pub/2000/09/06/distributed.html",

" ... extensible. Unlike plain old XML, there's no sense of

constraining what the document can describe by a DTD or schema.

This means ... "

...

"Plain Old Documentation",

"http://axkit.org/wiki/view/AxKit/PlainOldDocumentation",

" ... perlpodspec - Plain Old Documentation: format specification

and notes. ... Examples: =pod This is a plain Pod paragraph. ...

encodings in Pod parsing would be as in XML ... "


การใช้งานและข้อจำกัด

ด้วยวิธีเดียวกันนี้ คุณสามารถดัดแปลงวิธีการแฮ็กด้วย SOAP::Lite ที่ได้อธิบายไว้ในหัวข้อต่างๆ ตลอดหนังสือเล่มนี้ มาเป็นการใช้ PoXML ในการแฮ็กแทนก็ได้ โดยมีข้อกำหนดดังนี้

  1. วางไฟล์ PoXML.pm ไว้ในไดเรกทอรีเดียวกันกับสคริปต์ของเรื่องที่จะแฮ็ก
  2. แทนที่บรรทัดในสคริปต์ที่เป็น use SOAP::Lite; ด้วย use PoXML;
  3. แทนที่ my $google_search = SOAP::Lite->service(“file:$google_wdsl”); ด้วย my $google_search = new PoXML;

อย่างไรก็ตาม ยังมีข้อจำกัดในการใช้งานอยู่บ้าง ในขณะที่ PoXML ทำงานได้ดีในการดึงผลลัพธ์รวมทั้งสรุปผลลัพธ์ที่ได้จากการค้นหา แต่ก็ไม่ประสบผลสำเร็จในด้านการรวบรวมผลลัพธ์ในขั้นละเอียดขึ้น (Advanced Result) บางอย่าง เช่น <directoryCategories> ซึ่งเป็นลิสต์รายการหมวดของไดเรกทอรี เป็นต้น

ดูเพิ่มเติม

NoXML [Hack #54] เป็นทางเลือกในการใช้แทน SOAP::Lite ซึ่งใช้เพียง regular expression ทำงาน โดยไม่ต้องใช้ XML parser แต่อย่างใด

XooMLE [Hack #36] เป็น Third-party service ซึ่งเป็นตัวกลางในการเชื่อมต่อระหว่าง Plain Old XML กับ Google We แก้ไขบทความ b API