将 Google Book Search 集成到 PHP 应用程序中

红薯 发布于 2010/02/21 11:28
阅读 226
收藏 0

如果您与出版业有某种联系,或者即使您只是一位热心读者,您也可能听说过 Google Books。Google Books 是 Google 致力于创建世界最大的可搜索数字图书馆的尝试,其方法是扫描数百万本图书并使它们可以在线预览和购买。这也是 Google 最有争议的项目之一,是最近的 2009 年 11 月才结案的一个集体诉讼的焦点。

除了作为一个有趣的闲聊话题外,从开发人员角度看,Google Books 因具有 Data API 而变得有趣。该 API 允许开发人员阅读和搜索 Google Books 数据库,寻找匹配用户指定标准的图书,并在其他 Web 应用程序中使用这些搜索结果。您可以通过任何支持 XML 的开发工具包来访问这个遵循 REST 模型的 API。该 API 已经拥有针对 PHP、Java™ 和其他常用编程语言的客户端库。

本文将向您介绍 Google Book Search Data API,展示如何在自定义 PHP 应用程序中使用并集成图书搜索结果。本文包含了一些示例,展示如何通过关键字、语言和作者搜索图书,检索图书数据(包括 ISBN 编号和缩略图),对数据库中现有的图书添加评论和评分。来吧,我们现在就开始!

了解 Book Search 提要

在开始 PHP 代码之前,需要先介绍一下 Google Book Search Data API。与其它基于 REST 的服务一样,该 API 接受包含一个或多个 XML 编码输入参数的 HTTP 请求,并返回可以在任何具有 XML 意识的客户机中解析的 XML 编码响应。对于 Google Book Search Data API,响应总是包含一个 Atom 提要,它包含请求的信息。

一个典型的 Book Search 提要包含的信息足够构建一个有用且相关的应用程序。眼见为实,尝试在您喜爱的 Web 浏览器中访问这个 URL:http://books.google.com/books/feeds/volumes?q=php。该 REST 方法返回一个匹配关键字 php 的图书列表。该方法的原始 XML 响应(可以在生成的页面的源代码中查看)包含关于这些图书的详细信息,看起来可能类似于 清单 1


清单 1. 一个示例 Google Book Search 提要

				
<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns='http://www.w3.org/2005/Atom'
xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'
xmlns:gbs='http://schemas.google.com/books/2008'
xmlns:dc='http://purl.org/dc/terms'
xmlns:gd='http://schemas.google.com/g/2005'>
<id>http://www.google.com/books/feeds/volumes</id>
<updated>2009-12-28T06:14:28.000Z</updated>
<category scheme='http://schemas.google.com/g/2005#kind'
term='http://schemas.google.com/books/2008#volume'/>
<title type='text'>Search results for php</title>
<link rel='alternate' type='text/html' href='http://www.google.com'/>
<link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml'
href='http://www.google.com/books/feeds/volumes'/>
<link rel='self' type='application/atom+xml'
href='http://www.google.com/books/feeds/volumes?q=php'/>
<link rel='next' type='application/atom+xml'
href='http://www.google.com/books/feeds/volumes?q=php
&start-index=11&max-results=10'/>
<author>
<name>Google Books Search</name>
<uri>http://www.google.com</uri>
</author>
<generator version='beta'>Google Book Search data API</generator>
<openSearch:totalResults>277</openSearch:totalResults>
<openSearch:startIndex>1</openSearch:startIndex>
<openSearch:itemsPerPage>10</openSearch:itemsPerPage>
<entry>
<id>http://www.google.com/books/feeds/volumes/tywvv3ULal0C</id>
<updated>2009-12-28T06:14:28.000Z</updated>
<category scheme='http://schemas.google.com/g/2005#kind'
term='http://schemas.google.com/books/2008#volume'/>
<title type='text'>Programming PHP</title>
<link rel='http://schemas.google.com/books/2008/thumbnail'
type='image/x-unknown' href='http://bks8.books.google.com/books?
id=tywvv3ULal0C&printsec=frontcover&img=1&zoom=5
&edge=curl&sig=ACfU3U0WFIZOyvLPjIv7jqlX4XZ7GI4TAg
&source=gbs_gdata'/>
<link rel='http://schemas.google.com/books/2008/info' type='text/html'
href='http://books.google.com/books?id=tywvv3ULal0C&dq=php
&ie=ISO-8859-1&source=gbs_gdata'/>
<link rel='http://schemas.google.com/books/2008/preview' type='text/html'
href='http://books.google.com/books?id=tywvv3ULal0C
&printsec=frontcover&dq=php&ie=ISO-8859-1&cd=1
&source=gbs_gdata'/>
<link rel='http://schemas.google.com/books/2008/annotation'
type='application/atom+xml'
href='http://www.google.com/books/feeds/users/me/volumes'/>
<link rel='alternate' type='text/html'
href='http://books.google.com/books?
id=tywvv3ULal0C&dq=php&ie=ISO-8859-1'/>
<link rel='self' type='application/atom+xml'
href='http://www.google.com/books/feeds/volumes/tywvv3ULal0C'/>
<gbs:embeddability value='http://schemas.google.com/books/2008#embeddable'/>
<gbs:openAccess value='http://schemas.google.com/books/2008#disabled'/>
<gbs:viewability value='http://schemas.google.com/books/2008#view_partial'/>
<dc:creator>Rasmus Lerdorf</dc:creator>
<dc:creator>Kevin Tatroe</dc:creator>
<dc:creator>Peter MacIntyre</dc:creator>
<dc:date>2006</dc:date>
<dc:description>With style tips and practical programming advice,
this book will help you become not just a PHP programmer,
but a &quot;good&quot; PHP programmer.</dc:description>
<dc:format>521 pages</dc:format>
<dc:format>book</dc:format>
<dc:identifier>tywvv3ULal0C</dc:identifier>
<dc:identifier>ISBN:0596006810</dc:identifier>
<dc:identifier>ISBN:9780596006815</dc:identifier>
<dc:publisher>O'Reilly Media, Inc.</dc:publisher>
<dc:subject>Computers</dc:subject>
<dc:title>Programming PHP</dc:title>
</entry>
<entry>
...
</entry>
</feed>

 

快速浏览该输出以熟悉其主要元素:

  • Google Book Search Data API 通过一个包含请求数据的 Atom 提要来响应 REST 请求。在多数情况下,最外边的 <feed> 元素包含一些 <link> 元素,这些元素又包含结果集的当前页面、下一个页面和上一个页面的 URL;一些 <openSearch:> 元素,这些元素包含该搜索的摘要统计。
  • 最外边的 <feed> 元素包含一个或多个 <entry> 元素,每个元素表示匹配搜索查询的一本书或 “卷”。每个<entry> 包含关于它表示的图书的其他信息,包括标题、说明、出版日期、作者和出版商。每个 <entry> 还包含一些 <link> 元素,这些元素提供的 URL 链接可链接到详细的图书信息、缩略图、预览(如果有)以及评论和评分等注释。
  • 每个 entry 中带有名称空间的 <dc:> 元素都包含特殊的注释。这些元素对应于来自 Dublin Core Metadata Initiative (DCMI) 的元素,DCMI 为简单的信息标记和访问提供一组可重用的标准定义。如 清单 1 所示,这些元素包含关于每本图书的标题、作者、出版商、格式、主题和 ISBN 标识符的信息。

并非所有的 Google Book Search Data API 函数都可以以这种方式公开访问。尽管您无需验证就可以访问搜索函数,但仅当您是一位能够提供有效 Google Accounts 用户名和密码的验证用户,才能访问其他修改数据的函数(包括用于添加评论和标签,或向用户图书馆添加图书的函数)。本文将展示这两种类型的函数示例。

执行 Book Search 查询

现在您已经知道了如何通过公共 REST API 访问 Google Book Search 结果,下面将介绍如何从 PHP 应用程序内部完成相同的任务。显然,一种方法是使用 PHP 的内置 XML 处理扩展(SimpleXML、DOM 或 XMLReader)来解析由 Google Book Search 返回的 XML 提要并从中提取相关的信息片段。但是,这不是很方便,尤其是当您处理具有名称空间的信息的大型提要或大型卷册时。因此,本文将使用一种完全不同的方 法:Zend Framework 的 Zend_Gdata 客户端库,这个库是专门为试图集成 PHP 应用程序和 Google Data APIs 的开发人员设计的。

Zend_Gdata 库可以作为 Zend Framework 的一部分下载,也可以作为一个独立包单独下载(参见 参考 资料 部分的链接)。该库包含一个专门用于处理 Google Book Search Data API 的模块,提供预定义的类和方法来简化数据访问和验证。该库不仅为您的应用程序提供一个经过社区测试的坚实的代码基,而且,当您使用它时,您可以专注于核心 应用程序函数,而不是导航 XML 树或处理自定义名称空间的具体细节。

清 单 2 展示如何使用 Zend_Gdata 客户端库来检索和解析通过 Google Book Search Data API 检索的图书搜索结果的一个提要:


清单 2. 使用 Zend_Gdata 库检索搜索结果

				
<?php
// load Zend Gdata libraries
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata_Books');
Zend_Loader::loadClass('Zend_Gdata_Books_VolumeQuery');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');

// set credentials for ClientLogin authentication
$user = "xxx@gmail.com";
$pass = "secret";

try {
// perform login
$client = Zend_Gdata_ClientLogin::getHttpClient(
$user, $pass, 'print');
$client->setHeaders('X-Forwarded-For', $_SERVER['REMOTE_ADDR']);
$books = new Zend_Gdata_Books($client);

// prepare and execute search query
$query = new Zend_Gdata_Books_VolumeQuery;
$query->setQuery(urlencode('robert crais'));
$feed = $books->getVolumeFeed($query);
} catch (Exception $e) {
die('ERROR:' . $e->getMessage());
}
?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Searching for book titles</title>
</head>
<body>

<h2><?php echo $feed->title; ?></h2>
<div>
<?php echo $feed->totalResults; ?> result(s) found.
</div>

<div id="results")
<ol>
<?php foreach ($feed as $entry): ?>
<li>
<?php echo $entry->getTitle(); ?>
</li>
<?php endforeach; ?>
</ol>
</div>

</body>
</html>

 

清 单 2 首先加载 Zend 类库,然后初始化 Zend_Http_Client 类的一个实例。该客户端由一些用户凭证提供,这些用户凭证是创建到 Google Book Search 服务的验证连接所需要的。创建验证连接后,Zend_Gdata_Books 类的新实例被初始化;这个类充当与 Google Book Search Data API 的所有后续交互的控制点。

您最有可能使用的 Zend_Gdata_Books 方法是 getVolumeFeed() 方法,该方法返回匹配搜索查询的图书标题的提要。该方法通过一个已配置 Zend_Gdata_Books_VolumeQuery 类的实例传递,查询字符串通过 setQuery() 类方法设置。对 getVolumeFeed() 方法的响应是一个类似于 清单 1 中显示的 Atom 提要,该提要将自动解析并转换为一个 Zend_Gdata_Books_VolumeEntry 对象数组,每个对象表示提要中的一个 <entry>。现在,您可以轻松迭代这个数组,使用对象属性检索每个条目 的细节,并将其转换为 HTML 页面。

图 1 展示了您可能看到的输出 — 匹配关键字 “robert crais” 的所有图书列表:


图 1. 一个简单 Google Books API 搜索的结果
一个搜索 “robert+crais” 的 Google Books API 搜索的结果,显示 400 个结果中的前  10 个

有了上述基本理解之后,您可以很轻松地修改 清单 2 使其更具交互性。清单 3 添加了一个搜索表单,该表单可用于执行一个用户提供的图书搜索查询:


清单 3. 检索与用户所提供关键字匹配的搜索结果

				
<?php
if (isset($_POST['submit'])) {
// load Zend Gdata libraries
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata_Books');
Zend_Loader::loadClass('Zend_Gdata_Books_VolumeQuery');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');

// set credentials for ClientLogin authentication
$user = "xxx@gmail.com";
$pass = "secret";

try {
// perform login
$client = Zend_Gdata_ClientLogin::getHttpClient(
$user, $pass, 'print');
$client->setHeaders('X-Forwarded-For', $_SERVER['REMOTE_ADDR']);
$books = new Zend_Gdata_Books($client);

// prepare and execute search query
$query = new Zend_Gdata_Books_VolumeQuery;
$query->setQuery(urlencode($_POST['q']));
$feed = $books->getVolumeFeed($query);
} catch (Exception $e) {
die('ERROR:' . $e->getMessage());
}
}
?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Searching for book titles</title>
</head>
<body>

<h2>Search</h2>
<form method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>">
Search for:
<input type="text" name="q" value="<?php echo isset($_POST['q']) ?
$_POST['q'] : ''; ?>" />
<input type="submit" name="submit" value="Go" />
</form>

<?php if (isset($feed)): ?>
<h2>Search results for '<?php echo $_POST['q']; ?>'</h2>
<div>
<?php echo $feed->totalResults; ?> result(s) found.
</div>

<div id="results")
<ol>
<?php foreach ($feed as $entry): ?>
<li>
<a href="<?php echo $entry->getInfoLink()->getHref(); ?>">
<?php echo $entry->getTitle(); ?></a>
</li>
<?php endforeach; ?>
</ol>
</div>
<?php endif; ?>

</body>
</html>

 

图 2 展示了搜索 “indian cooking” 生成的结果示例:


图 2. 一个简单 Google Books API 搜索的结果
一个搜索 “indian cooking” 的 Google Books API 搜索的结果,显示 574  个结果中的前 10 个

您可能已经注意到了 清单 3 中增加的内容:它使用了 Zend_Gdata_Books_VolumeEntry::getInfoLink() 方法,该方法返回一个指向 Google Book Search Web 站点上图书信息页面的链接,单击该链接将把用户重定向到一个包含详细图书说明和用户评论等信息的页面。图 3 显示了该页面的一个示例:


图 3. 一个包含图书细节的页面
Google Books 图书细节页面,包含 Dennis Lehane 撰写的图书 “book Gone,  baby, gone” 和相关图书

还有一点值得注意:传递到 Google Book Search Data API 的查询字符串必须是 URL 编码字符串。在前面的清单中,您应该已经看到,URL 编码是通过 PHP 的 urlencode() 方法完成的。

检索详细图书信息

清单 1 所示,除图书标题之外,Google Book Search Data API 返回的卷提要包含相当多的信息:作者和出版商名称、ISBN 编号、页数、主题等。使用 Zend_Gdata_Books,所有这些信息都被表示为一组对象,您可以在一个 PHP 脚本范围内访问并操作这些对象来创建更便于提供信息的搜索结果页面。

清 单 4 展示了如何检索图书细节:


清单 4. 检索图书细节

				
<?php
if (isset($_POST['submit'])) {
// load Zend Gdata libraries
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata_Books');
Zend_Loader::loadClass('Zend_Gdata_Books_VolumeQuery');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');

// set credentials for ClientLogin authentication
$user = "xxx@gmail.com";
$pass = "secret";

try {
// perform login
$client = Zend_Gdata_ClientLogin::getHttpClient(
$user, $pass, 'print');
$client->setHeaders('X-Forwarded-For', $_SERVER['REMOTE_ADDR']);
$books = new Zend_Gdata_Books($client);

// prepare and execute search query
$query = new Zend_Gdata_Books_VolumeQuery;
$query->setQuery(urlencode($_POST['q']));
$feed = $books->getVolumeFeed($query);
} catch (Exception $e) {
die('ERROR:' . $e->getMessage());
}
}
?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Searching for book titles</title>
<style>
.entry {
height: 120px;
border-bottom: dashed silver 2px;
padding-top: 10px;
}

.thumbnail {
float: left;
border: solid black 2px;
padding: 2px;
margin-right: 10px;
}

.desc {
font-style: italic;
}

.small {
font-size: smaller;
}
</style>
</head>
<body>

<h2>Search</h2>
<form method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>">
Search for:
<input type="text" name="q" value="<?php echo isset($_POST['q']) ?
$_POST['q'] : ''; ?>" />
<input type="submit" name="submit" value="Go" />
</form>

<?php if (isset($feed)): ?>
<h2>Search results for '<?php echo $_POST['q']; ?>'</h2>
<div>
<?php echo $feed->totalResults; ?> result(s) found.
</div>

<div id="results">
<?php $x = 1; ?>
<?php foreach ($feed as $entry): ?>

<?php
//print_r($entry);
$book = new stdClass;

// get title
if (is_array($entry->getTitles())) {
foreach ($entry->getTitles() as $title) {
$book->titles[] = $title->getText();
}
}

// get authors
if (is_array($entry->getCreators())) {
foreach ($entry->getCreators() as $creator) {
$book->authors[] = $creator->getText();
}
}

// get publishers
if (is_array($entry->getPublishers())) {
foreach ($entry->getPublishers() as $publisher) {
$book->publishers[] = $publisher->getText();
}
}

// get publication date
if (is_array($entry->getDates())) {
$arr = $entry->getDates();
$book->pubdate = (is_object($arr[0])) ?
$arr[0]->getText() : 'Unspecified';
}

// get ISBN numbers
if (is_array($entry->getIdentifiers())) {
foreach ($entry->getIdentifiers() as $id) {
if (preg_match('/ISBN/', $id->getText())) {
$book->isbn[] = $id->getText();
}
}
}

// get first subject
if (is_array($entry->getSubjects())) {
$arr = $entry->getSubjects();
$book->subject = is_object($arr[0]) ?
$arr[0]->getText() : 'Unspecified';
}

// get first description
if (is_array($entry->getDescriptions())) {
$arr = $entry->getDescriptions();
$book->desc = is_object($arr[0]) ?
$arr[0]->getText() : 'No description available';
}
?>

<div class="entry">
<div class="thumbnail">
<img src="<?php echo ($entry->getThumbnailLink()) ?
$entry->getThumbnailLink()->getHref() : ''; ?>" />
</div>
<div class="data">
<?php echo $x; ?>. <?php echo ucwords(@implode(': ',
$book->titles)); ?><br/>
<?php echo @implode(', ', $book->authors); ?> |
<?php echo @implode(', ', $book->publishers); ?> |
<?php echo $book->subject; ?> |
<?php echo date('d M Y', strtotime($book->pubdate)); ?> <br/>
<span class="desc"><?php echo $book->desc; ?></span> <br/>
<span class="small"><?php echo @implode(', ', $book->isbn); ?> |
<a href="<?php echo $entry->getInfoLink()->getHref(); ?>">
More information</a>
</span>
</div>
</div>
<?php $x++; ?>
<?php endforeach; ?>
<?php endif; ?>

</body>
</html>

 

清 单 4 介绍了 Zend_Gdata_Books_VolumeEntry 类的几个新方法。以下是其中几个重要方法的简单列表:

  • getDates() 方法返回图书出版日期。
  • getCreators() 方法返回作者姓名。
  • getPublishers() 方法返回出版商名称。
  • getSubjects()getDescriptions() 方法返回图书主题和说明。
  • getTitles() 方法返回图书标题(和副标题,如果有的话)。
  • getIdentifiers() 方法返回图书的 ISBN-10 和 ISBN-13 编号数组,以及 Google Book Search 记录 ID。
  • getThumbnailLink() 方法返回图书封面缩略图的 URL。
  • getVolumeId() 返回 Google Book Search 服务中的唯一卷标识符。

所有这些方法都返回一个对象数组,对应于各个 Dublin Core metadata 元素;接下来将由开发人员访问每个数组的元素并使用其中的信息来重新组织搜索结果。


图 4. 一个 Google Books API 搜索的结果,搜索结果经过增强,可包含一个封面图像和其他图书数据
一个 Google Books API 搜索的增强结果,包含一个封面图像和两本书的其他图书数据

使用查询过滤器

您也许已经注意到,在前面的所有示例中,搜索结果提要只包含 10 个条目,尽管 <openSearch:> 元素实际表明匹配值的数量要高得多。这是因为,在默认情况下,Google Book Search 提要限制为每个提要 10 个匹配值。这个限制并非一层不变的,您可以对您的 REST 查询添加以下参数,轻松定制 API 输出:

  • start-index 参数指定提要中的条目的起始偏移。
  • max-results 参数指定提要中的条目数量。
  • min-viewability 参数指定提要条目是否只包含那些存在部分或完整预览的图书。

除了这些参数之外,您还可以对一个搜索查询添加其他过滤器,根据标题、作者、出版商、语言、主题、说明或 ISBN 编号进行搜索。清单 5 展示了如何添加过滤器:


清单 5. 通过作者、标题和可见性过滤搜索结果

				
<?php
if (isset($_POST['submit'])) {
// load Zend Gdata libraries
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata_Books');
Zend_Loader::loadClass('Zend_Gdata_Books_VolumeQuery');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');

// set credentials for ClientLogin authentication
$user = "xxx@gmail.com";
$pass = "secret";

try {
// perform login
$client = Zend_Gdata_ClientLogin::getHttpClient(
$user, $pass, 'print');
$client->setHeaders('X-Forwarded-For', $_SERVER['REMOTE_ADDR']);
$books = new Zend_Gdata_Books($client);

// prepare and execute search query
$query = new Zend_Gdata_Books_VolumeQuery;
$queryStr = '';
if (!empty($_POST['title'])) {
$queryStr .= '+intitle:'.urlencode($_POST['title']);
}
if (!empty($_POST['author'])) {
$queryStr .= '+inauthor:'.urlencode($_POST['author']);
}
$query->setQuery($queryStr);
$query->setMinViewability($_POST['v']);
$query->setMaxResults(20);

$feed = $books->getVolumeFeed($query);
} catch (Exception $e) {
die('ERROR:' . $e->getMessage());
}
}
?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Searching for book titles</title>
<style>
.entry {
height: 120px;
border-bottom: dashed silver 2px;
padding-top: 10px;
}

.thumbnail {
float: left;
border: solid black 2px;
padding: 2px;
margin-right: 10px;
}

.desc {
font-style: italic;
}

.small {
font-size: smaller;
}
</style>
</head>
<body>

<h2>Search</h2>
<form method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>">
Title:
<input type="text" name="title" value="<?php echo isset($_POST['title']) ?
$_POST['title'] : ''; ?>" />

Author:
<input type="text" name="author" value="<?php echo isset($_POST['author']) ?
$_POST['author'] : ''; ?>" />

Viewability:
<select name="v">
<option value="none">Any</option>
<option value="partial_view">Partial</option>
<option value="full_view">Full</option>
</select>

<input type="submit" name="submit" value="Go" />
</form>

<?php if (isset($feed)): ?>
<h2>Search results for '<?php echo $query->getQuery(); ?>'
</h2>
<div>
<?php echo $feed->totalResults; ?> result(s) found.
</div>

<div id="results">
<?php $x = 1; ?>
<?php foreach ($feed as $entry): ?>

<?php
//print_r($entry);
$book = new stdClass;

// get title
if (is_array($entry->getTitles())) {
foreach ($entry->getTitles() as $title) {
$book->titles[] = $title->getText();
}
}

// get authors
if (is_array($entry->getCreators())) {
foreach ($entry->getCreators() as $creator) {
$book->authors[] = $creator->getText();
}
}

// get publishers
if (is_array($entry->getPublishers())) {
foreach ($entry->getPublishers() as $publisher) {
$book->publishers[] = $publisher->getText();
}
}

// get publication date
if (is_array($entry->getDates())) {
$arr = $entry->getDates();
$book->pubdate = (is_object($arr[0])) ?
$arr[0]->getText() : 'Unspecified';
}

// get ISBN numbers
if (is_array($entry->getIdentifiers())) {
foreach ($entry->getIdentifiers() as $id) {
if (preg_match('/ISBN/', $id->getText())) {
$book->isbn[] = $id->getText();
}
}
}

// get first subject
if (is_array($entry->getSubjects())) {
$arr = $entry->getSubjects();
$book->subject = is_object($arr[0]) ?
$arr[0]->getText() : 'Unspecified';
}

// get first description
if (is_array($entry->getDescriptions())) {
$arr = $entry->getDescriptions();
$book->desc = is_object($arr[0]) ?
$arr[0]->getText() : 'No description available';
}
?>

<div class="entry">
<div class="thumbnail">
<img src="<?php echo ($entry->getThumbnailLink()) ?
$entry->getThumbnailLink()->getHref() : ''; ?>" />
</div>
<div class="data">
<?php echo $x; ?>. <?php echo ucwords(@implode(': ',
$book->titles)); ?><br/>
<?php echo @implode(', ', $book->authors); ?> |
<?php echo @implode(', ', $book->publishers); ?> |
<?php echo $book->subject; ?> |
<?php echo date('d M Y', strtotime($book->pubdate)); ?> <br/>
<span class="desc"><?php echo $book->desc; ?></span>
<br/>
<span class="small"><?php echo @implode(', ', $book->isbn); ?> |
<a href="<?php echo $entry->getInfoLink()->getHref(); ?>">
More information</a>
</span>
</div>
</div>
<?php $x++; ?>
<?php endforeach; ?>
<?php endif; ?>

</body>
</html>

 

清单 4 的这个修订版中,用户将看到针对标题、作者和可见性的单独的搜索字段。根据收到的输入参数,搜索查询由适当的限定符构造并传输到 Google Book Search API。注意 setMinViewability()setMaxResults() 方法,它们为 min-viewabilitymax-results 输入参数提供面向对象的接口。

图 5图 6 展示了 清单 5 的输出,前者显示了一个标题搜索的结果,后者显示了一个 “作者/标题” 联合搜索的结果。


图 5. 一个 Google Books 搜索的结果,根据标题过滤
一个搜索 “+intitle:php” 的 Google Books API 搜索的结果,显示了根据标题过滤的  310 个结果中的两个

图 6. 一个 Google Books API 结果,根据作者姓名和标题名称过滤
一个搜索 “+intitle:php+inauthor:vikram+vaswani” 的 Google Books  API 搜索的结果,显示了 10 个已过滤项目中的两个

访问用户图书馆

除了支持公共搜索之外,Google Book Search 还允许验证用户创建他们自己的虚拟图书馆。这个 “My Library” 特性允许用户评论图书并对图书添加标签,创建并与其他用户共享图书集合。与其他 Google Book Search 特性一样,该特性通过 Google Book Search Data API 提供。

要查看 “My Library” 的工作方式,使用您的 Google Account 登录 Google Book Search,输入搜索词汇,然后使用每个搜索结果旁边的 “Add to my library” 链接来创建虚拟图书馆,您可以通过每个页面右上角的 “My Library” 链接来随时访问这个图书馆。图 7 展示了一个用户图书馆示例:


图 7. Google Books Web 站点上的一个用户图书馆
Google Books Web 站点上的一个用户图书馆,显示了 3 本带有封面缩略图和细节的图书

验证用户的图书馆的内容也可以作为一个 Atom 提要获取,以便在 PHP 应用程序中解析。与 清单 1 中展示的卷提要一样,该提要包含一组 <entry> 元素,每个元素表示用户图书馆中的一本书。该提要可以通过 Zend_Gdata_Books 对象的 getUserLibraryFeed() 方法解析为一组 PHP 对象,从而用于生成图书馆的 HTML 视图。清单 6 展示了如何检索用户图书馆的内容:


清单 6. 检索用户图书馆的内容

				
<?php
// load Zend Gdata libraries
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata_Books');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');

// set credentials for ClientLogin authentication
$user = "xxx@gmail.com";
$pass = "secret";

try {
// perform login
$client = Zend_Gdata_ClientLogin::getHttpClient(
$user, $pass, 'print');
$client->setHeaders('X-Forwarded-For', $_SERVER['REMOTE_ADDR']);
$books = new Zend_Gdata_Books($client);

// get authenticated user's library feed
$feed = $books->getUserLibraryFeed();
} catch (Exception $e) {
die('ERROR:' . $e->getMessage());
}
?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Displaying a user's library</title>
<style>
.entry {
height: 120px;
border-bottom: dashed silver 2px;
padding-top: 10px;
}

.thumbnail {
float: left;
border: solid black 2px;
padding: 2px;
margin-right: 10px;
}

.desc {
font-style: italic;
}

.small {
font-size: smaller;
}
</style>
</head>
<body>
<?php if (isset($feed)): ?>
<h2>My Library</h2>
<div>
<?php echo $feed->totalResults; ?> result(s) found.
</div>

<div id="results">
<?php $x = 1; ?>
<?php foreach ($feed as $entry): ?>

<?php
$book = new stdClass;

// get title
if (is_array($entry->getTitles())) {
foreach ($entry->getTitles() as $title) {
$book->titles[] = $title->getText();
}
}

// get authors
if (is_array($entry->getCreators())) {
foreach ($entry->getCreators() as $creator) {
$book->authors[] = $creator->getText();
}
}

// get publishers
if (is_array($entry->getPublishers())) {
foreach ($entry->getPublishers() as $publisher) {
$book->publishers[] = $publisher->getText();
}
}

// get publication date
if (is_array($entry->getDates())) {
$arr = $entry->getDates();
$book->pubdate = (is_object($arr[0])) ?
$arr[0]->getText() : 'Unspecified';
}

// get ISBN numbers
if (is_array($entry->getIdentifiers())) {
foreach ($entry->getIdentifiers() as $id) {
if (preg_match('/ISBN/', $id->getText())) {
$book->isbn[] = $id->getText();
}
}
}

// get first subject
if (is_array($entry->getSubjects())) {
$arr = $entry->getSubjects();
$book->subject = is_object($arr[0]) ?
$arr[0]->getText() : 'Unspecified';
}

// get first description
if (is_array($entry->getDescriptions())) {
$arr = $entry->getDescriptions();
$book->desc = is_object($arr[0]) ?
$arr[0]->getText() : 'No description available';
}
?>

<div class="entry">
<div class="thumbnail">
<img src="<?php echo ($entry->getThumbnailLink()) ?
$entry->getThumbnailLink()->getHref() : ''; ?>" />
</div>
<div class="data">
<?php echo $x; ?>. <?php echo ucwords(@implode(': ',
$book->titles)); ?><br/>
<?php echo @implode(', ', $book->authors); ?> |
<?php echo @implode(', ', $book->publishers); ?> |
<?php echo $book->subject; ?> |
<?php echo date('d M Y', strtotime($book->pubdate)); ?> <br/>
<span class="desc"><?php echo $book->desc; ?></span>
<br/>
<span class="small"><?php echo @implode(', ', $book->isbn); ?> |
Added to library on: <?php echo ($entry->getPublished()) ?
date('d M Y', strtotime($entry->getPublished()->getText())) : '';
?> |
<a href="<?php echo $entry->getInfoLink()->getHref(); ?>"
>More information</a>
</span>
</div>
</div>
<?php $x++; ?>
<?php endforeach; ?>
<?php endif; ?>

</body>
</html>

 

清单 6 中的大部分内容已在本文前面的小节中出现过。这个脚本打开一个到 Google Book Search 服务的验证连接,初始化 Zend_Gdata_Books 对象,然后使用 getUserLibraryFeed() 方法来检索提要,该提要包含用户图书馆中的图书列表。该提要以 PHP 对象数组的形式表示,您现在可以轻松迭代这个数组,处理每个条目并使用适当的 HTML 标记来显示结果。

图 8 展示了一个结果示例:


图 8. 对用户图书馆提要的 Google Books API 请求结果
对用户图书馆提要的 Google Books API 请求结果,显示了 3 本带有封面缩略图和细节的图书

向用户图书馆添加图书

您已经了解了如何检索用户图书馆的内容......但如何向用户图书馆添加新标题呢?使用 Google Book Search Data API,您只需 POST 一个包含图书的唯一卷标识符的 XML 编码 <entry> 到用户的图书馆提要。清单 7 展示了这样一个 POST 请求示例:


清单 7. 向用户图书馆添加一本图书的 POST 请求示例

				
POST /books/feeds/users/me/collections/library/volumes HTTP/1.1
Host: books.google.com
Connection: close
User-Agent: MyCompany-MyApp-1.0 Zend_Framework_Gdata/1.9.0
authorization: GoogleLogin
Content-Type: application/atom+xml
Accept-encoding: identity
Content-Length: 97

<atom:entry xmlns:atom="http://www.w3.org/2005/Atom">
<atom:id>BOOK_VOLUME_ID_HERE</atom:id>
</atom:entry>

 

要使用 Zend_Gdata_Books 完成相同的任务,初始化一个新的 Zend_Gdata_Books_VolumeEntry 对象,向它分配一个卷标识符,然后使用 insertVolume() 方法将它附加到用户的图书馆提要。清单 8 展示了相关代码:


清单 8. 向用户图书馆添加图书

				
<?php
// load Zend Gdata libraries
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata_Books');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');

// set credentials for ClientLogin authentication
$user = "xxx@gmail.com";
$pass = "secret";

try {
// perform login
$client = Zend_Gdata_ClientLogin::getHttpClient(
$user, $pass, 'print');
$books = new Zend_Gdata_Books($client);

// add book to user's library using volume ID
$id = 'BOOK_VOLUME_ID_HERE';
$entry = new Zend_Gdata_Books_VolumeEntry();
$entry->setId(new Zend_Gdata_App_Extension_Id($id));
$books->insertVolume(
$entry,
Zend_Gdata_Books::MY_LIBRARY_FEED_URI
);

// display success message
echo "Volume added successfully with ID: $id";
} catch (Exception $e) {
die('ERROR:' . $e->getMessage());
}
?>

添加图书评论和标签

除允许用户向其图书馆添加图书外,Google Book Search API 还允许用户向图书添加评论和标签。评论和标签存储为 Google 所称的 “注释(annotation)”,如果您回顾一下 清单 1, 您将看到每个条目都包含一个注释 URL。因此,要向一本图书添加评论或标签,您只需为该图书创建一个 XML 编码的 <entry>, 将评论和/或标签附加到这个条目,然后将整个条目 POST 到概述的注释 URL。

清 单 9 展示了这样一个 POST 请求示例:


清单 9. 一个用于添加图书评论的 POST 请求示例

				
POST /books/feeds/users/me/volumes HTTP/1.1
Host: www.google.com
Connection: close
User-Agent: MyCompany-MyApp-1.0 Zend_Framework_Gdata/1.9.0
authorization: GoogleLogin
Accept-encoding: identity
Content-Type: application/atom+xml
Content-Length: 3291

<atom:entry xmlns:atom="http://www.w3.org/2005/Atom">
<openAccess xmlns="http://schemas.google.com/books/2008"
value="http://schemas.google.com/books/2008#disabled"/>
<atom:category term="http://schemas.google.com/books/2008#volume"
scheme="http://schemas.google.com/g/2005#kind"/>
<atom:id>http://www.google.com/books/feeds/volumes/BOOK_VOLUME_ID_HERE
</atom:id>
<atom:link href="http://bks7.books.google.com/books?id=BOOK_VOLUME_ID_HERE
&printsec=frontcover&img=1&zoom=5&sig=ACfU3U0ayCK47roiq
7r_hf_Iy-tQ&source=gbs_gdata"
rel="http://schemas.google.com/books/2008/thumbnail" type="image/x-unknown"/>
<atom:link href="http://books.google.com/books?id=BOOK_VOLUME_ID_HERE
&ie=ISO-8859-1&source=gbs_gdata"
rel="http://schemas.google.com/books/2008/info" type="text/html"/>
<atom:link href="http://www.google.com/books/feeds/users/me/volumes"
rel="http://schemas.google.com/books/2008/annotation"
type="application/atom+xml"/>
<atom:link href="http://books.google.com/books?id=BOOK_VOLUME_ID_HERE
&ie=ISO-8859-1" rel="alternate" type="text/html"/>
<atom:link href="http://www.google.com/books/feeds/volumes/BOOK_VOLUME_ID_HERE"
rel="self" type="application/atom+xml"/>
<atom:title type="text">The Two Minute Rule</atom:title>
<atom:updated>2009-12-28T10:15:44.000Z</atom:updated>
<dc:creator xmlns:dc="http://purl.org/dc/terms">Robert Crais</dc:creator>
<dc:date xmlns:dc="http://purl.org/dc/terms">2006-01-01</dc:date>
<dc:format xmlns:dc="http://purl.org/dc/terms">Dimensions 10.8x17.2x3.0 cm
</dc:format>
<dc:format xmlns:dc="http://purl.org/dc/terms">465 pages</dc:format>
<dc:format xmlns:dc="http://purl.org/dc/terms">book</dc:format>
<dc:identifier xmlns:dc="http://purl.org/dc/terms">BOOK_VOLUME_ID_HERE
</dc:identifier>
<dc:identifier xmlns:dc="http://purl.org/dc/terms">ISBN:1111111111
</dc:identifier>
<dc:identifier xmlns:dc="http://purl.org/dc/terms">ISBN:1111111111111
</dc:identifier>
<dc:language xmlns:dc="http://purl.org/dc/terms">en</dc:language>
<dc:publisher xmlns:dc="http://purl.org/dc/terms">Pocket Books
</dc:publisher>
<dc:subject xmlns:dc="http://purl.org/dc/terms">Fiction / Action & Adventure
</dc:subject>
<dc:subject xmlns:dc="http://purl.org/dc/terms">Fiction / Suspense
</dc:subject>
<dc:subject xmlns:dc="http://purl.org/dc/terms">Fiction / Action & Adventure
</dc:subject>
<dc:title xmlns:dc="http://purl.org/dc/terms">The Two Minute Rule
</dc:title>
<gbs:embeddability xmlns:gbs="http://schemas.google.com/books/2008"
value="http://schemas.google.com/books/2008#not_embeddable"/>
<gd:rating xmlns:gd="http://schemas.google.com/g/2005" min="1" max="5"
average="4.20"/>
<gbs:review xmlns:gbs="http://schemas.google.com/books/2008">
This book is amazing - v!</gbs:review>
<gbs:viewability xmlns:gbs="http://schemas.google.com/books/2008"
value="http://schemas.google.com/books/2008#view_no_pages"/>
</atom:entry>

 

清 单 10 展示了一个使用目标图书的卷标识符和 Zend_Gdata 库来添加评论的示例:


清单 10. 添加一条图书评论

				
<?php
// load Zend Gdata libraries
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata_Books');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');

// set credentials for ClientLogin authentication
$user = "xxx@gmail.com";
$pass = "secret";

try {
// perform login
$client = Zend_Gdata_ClientLogin::getHttpClient(
$user, $pass, 'print');
$books = new Zend_Gdata_Books($client);

// add review to book
$id = 'BOOK_VOLUME_ID_HERE';
$entry = $books->getVolumeEntry($id);
$review = new Zend_Gdata_Books_Extension_Review();
$review->setText("This book is amazing - v!");
$entry->setReview($review);
$books->insertVolume(
$entry,
$entry->getAnnotationLink()->getHref()
);

// display success message
echo "Review successfully added with ID: $id";
} catch (Exception $e) {
die('ERROR:' . $e->getMessage());
}
?>

 

清单 10 中,getVolumeEntry() 方法用于通过图书的卷标识符来检索该图书的当前条目,然后,一个包含评论文本的 Zend_Gdata_Books_Extension_Review 对象通过 setReview() 方法附加到该条目。然后 insertVolume() 方法负责创建 POST 请求并更新 Google 服务器上的相应条目。

类似地,Google Book Search 还允许用户使用描述性的关键字为每本书设置标签。这是一个有用的社区特性,能够生成更有效、更相关的搜索结果。清单 11 通过一个示例展示如何通过编程方式将这些标签附加到一个图书条目:


清单 11. 添加图书标签

				
<?php
// load Zend Gdata libraries
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata_Books');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');

// set credentials for ClientLogin authentication
$user = "xxx@gmail.com";
$pass = "secret";

try {
// perform login
$client = Zend_Gdata_ClientLogin::getHttpClient(
$user, $pass, 'print');
$books = new Zend_Gdata_Books($client);

// add labels to book
$id = 'BOOK_VOLUME_ID_HERE';
$entry = $books->getVolumeEntry($id);
$entry->setCategory(array(
new Zend_Gdata_App_Extension_Category('crime',
'http://schemas.google.com/books/2008/labels'),
new Zend_Gdata_App_Extension_Category('suspense',
'http://schemas.google.com/books/2008/labels'),
new Zend_Gdata_App_Extension_Category('elvis cole',
'http://schemas.google.com/books/2008/labels')
));
$books->insertVolume(
$entry,
$entry->getAnnotationLink()->getHref()
);

// display success message
echo "Labels successfully added for ID: $id";
} catch (Exception $e) {
die('ERROR:' . $e->getMessage());
}
?>

 

清单 11 中,每个标签都表示为一个 Zend_Gdata_App_Extension_Category 实例,然后使用 setCategory() 方法将这些实例附加到图书条目,然后调用 insertVolume() 方法来将这些标签添加到 Google Book Search 数据库中的图书条目。

添加到 Google Book Search 的评论和标签并不是私有的,它们对所有 Internet 用户可见,用户可以通过 Google Books Web 站点或一个公共提要 URL 访问它们。为说明这一点,假设您可以查看一个特定用户创建的所有评论和标签,您只需在以下 URL 访问该用户的注释提要:http://books.google.com/books/feeds/users/USER_ID/volumes。注意, 这个 URL 中的 USER_ID 必须是该用户在 Google Book Search 上的唯一识别号,而不是该用户的 Google Accounts 用户名。您可以在该用户的 “My Library” URL 链接中找到 Google Book Search 识别号。

结束语

尽管还处于开发阶段,Google Book Search Data API 仍然为开发人员将图书搜索结果集成到 Web 应用程序中提供了无限可能性。本文中的示例向您介绍了 Google Book Search 卷提要,展示了如何通过关键字、作者和标题搜索视频,如何从搜索结果提要提取图书元数据,包括出版商和作者信息、缩略图和 ISBN 编号。本文还简要介绍了 Google Book Search 的一些社区特性,展示如何以编程方式向图书条目添加评论和书签。

这些示例证明,当开发人员创建新的 Web 应用程序时,Google Book Search REST API 能够向他们提供大量的灵活性和自由度。如果您试图混搭图书搜索数据和来自其他 Web 服务的数据,或者只需构建一个针对作者、出版商或消费者的定制搜索界面,这个 API 都非常有用。试用这个 API 一段时间,看看您将有什么新的想法!

加载中
返回顶部
顶部