谈到在线视频共享,目前最流行的站点非 YouTube 莫属,该站点每天会增加几十亿个页面视图和数十万个视频文件。它不仅仅提供家庭录像:如今,YouTube 还提供音乐节目录像、电视节目剪辑、即将上映的影片片花、动漫剪辑等大量内容。它还允许用户使用关键字为视频添加标记,在任何时刻都可以观看最受欢迎的视频文件。
YouTube 的一个出色特性就是它的 YouTube Data API,该特性允许开发人员通过基于 REST 的 API 访问和搜索 YouTube 视频数据,并将数据集成到自己的支持 XML 的应用程序中。这个过程实现起来并不困难 — 编写应用程序级别的代码发送 REST 请求、解析和解码响应,然后将结果数据集成到应用程序接口。如果使用 PHP,那么您可以通过 YouTube 的 PHP Client Library 执行这些任务,也可以手动解析 REST 请求的 XML 响应。
本文将讨论后一种方法,展示如何通过 YouTube API 访问公共内容并使用 SimpleXML 将这些内容集成到您的 PHP 应用程序中。本文提供的示例将检索特定类别中的视频清单;根据关键字搜索视频;检索视频元数据,包括缩略图和统计信息;并访问用户个人信息。
了解 YouTube 数据格式
在深入研究 PHP 代码之前,首先简单介绍一下 YouTube Data API。与所有基于 REST 的服务一样,首先从一个发送到指定资源的 HTTP 请求开始。这个 HTTP 请求包含一个查询,具有一个或多个输入参数;服务器使用 Atom 或 RSS 格式的响应回复该查询,该响应适合在支持 XML 的任何客户机内进行解析。
http://gdata.youtube.com/feeds/api/standardfeeds/most_viewed
清单 1. YouTube API 生成的示例提要
快速查看这些输出,熟悉其中的主要元素:
- YouTube Data API 对 REST 请求的响应是一个包含所请求数据的提要。提要的内容多种多样:视频提要、视频评论提要、用户播放列表提要、用户联系方式提要,等等。因此,在大多数情况下,XML 响应包含一个 <feed> 元素作为根元素。<feed> 元素包含 <link> 元素,后者包含当前、上一个、下一个结果集页面的 URL,以及一个包含搜索的摘要统计数据的 <openSearch:> 元素。
- 最外层的 <feed> 元素封装了一个或多个 <entry> 元素,分别代表一个与查询匹配的视频。每个 <entry> 包含它所表示的视频的更多信息,包括类别、标题、描述、发布日期、作者和持续时间。每个 <entry> 中还包含 <link> 元素,通过其中的链接可以查看视频、视频回复和 YouTube Web 站点中其他相关的视频。
- 每个 <entry> 中的 <media:group> 元素包含关于视频的详细信息:标题、描述、各种格式的可用性、缩略图链接和视频播放器链接。
- 每个 <entry> 中的 <yt:statistics> 元素提供视频观看者的统计信息,而 <gd:rating> 元素提供该视频的平均得分,以及对该视频进行打分的用户总数。
这无疑包含了大量信息 — 可能超出了您的想象。正是这些丰富的信息库使 YouTube Data API 变得引人注目,因为它允许开发人员自由地构建创新性的应用程序。
并不是所有的 YouTube Data API 功能都可以通过这种方式进行公开访问。某些功能 — 尤其是修改站点数据的功能,包括上传视频、编辑内容或用户个人信息、添加评论或为视频打分 — 只能通过开发锁(developer key)和身份验证令牌访问。要访问这些 API 功能,需要在 YouTube 注册为一名开发人员(参加 参考资料 中的链接)。本文讨论的内容仅限于那些不需要开发锁的 API 功能;您不久将看到,这些功能足以让您应接不暇!
回页首
检索视频清单
现在让我们查看一个使用 PHP 处理 YouTube Data API 提要的示例。清单 2 从 清单 1 获取提要并使用 SimpleXML 从中提取有关的数据片段,然后将它格式化到一个 Web 页面:
清单 2. 使用一个 PHP 脚本列出观看次数最多的 YouTube 视频
<!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>Listing most viewed videos</title>
<style type="text/css">
div.item {
border-top: solid black 1px;
margin: 10px;
padding: 2px;
width: auto;
padding-bottom: 20px;
}
span.thumbnail {
float: left;
margin-right: 20px;
padding: 2px;
border: solid silver 1px;
font-size: x-small;
text-align: center
}
span.attr {
font-weight: bolder;
}
span.title {
font-weight: bolder;
font-size: x-large
}
img {
border: 0px;
}
a {
color: brown;
text-decoration: none;
}
</style>
</head>
<body>
<?php
// set feed URL
$feedURL = 'http://gdata.youtube.com/feeds/api/standardfeeds/most_viewed';
// read feed into SimpleXML object
$sxml = simplexml_load_file($feedURL);
?>
<h1><?php echo $sxml->title; ?></h1>
<?php
// iterate over entries in feed
foreach ($sxml->entry as $entry) {
// get nodes in media: namespace for media information
$media = $entry->children('http://search.yahoo.com/mrss/');
// get video player URL
$attrs = $media->group->player->attributes();
$watch = $attrs['url'];
// get video thumbnail
$attrs = $media->group->thumbnail[0]->attributes();
$thumbnail = $attrs['url'];
// get <yt:duration> node for video length
$yt = $media->children('http://gdata.youtube.com/schemas/2007');
$attrs = $yt->duration->attributes();
$length = $attrs['seconds'];
// get <yt:stats> node for viewer statistics
$yt = $entry->children('http://gdata.youtube.com/schemas/2007');
$attrs = $yt->statistics->attributes();
$viewCount = $attrs['viewCount'];
// get <gd:rating> node for video ratings
$gd = $entry->children('http://schemas.google.com/g/2005');
if ($gd->rating) {
$attrs = $gd->rating->attributes();
$rating = $attrs['average'];
} else {
$rating = 0;
}
?>
<div class="item">
<span class="title">
<a href="<?php echo $watch; ?>"><?php echo $media->group->title; ?></a>
</span>
<p><?php echo $media->group->description; ?></p>
<p>
<span class="thumbnail">
<a href="<?php echo $watch; ?>"><img src="<?php echo $thumbnail;?>" /></a>
<br/>click to view
</span>
<span class="attr">By:</span> <?php echo $entry->author->name; ?> <br/>
<span class="attr">Duration:</span> <?php printf('%0.2f', $length/60); ?>
min. <br/>
<span class="attr">Views:</span> <?php echo $viewCount; ?> <br/>
<span class="attr">Rating:</span> <?php echo $rating; ?>
</p>
</div>
<?php
}
?>
</body>
</html>
图 1 演示了可能看到的输出:
图 1. 观看最频繁的视频列表
simplexml_load_file()foreach()
children()media:$media$media->group->title$media->group->descriptionurl$media->group->thumbnail[0]->attributes()urlurl$media->group->player->attributes()url$media->children()yt:attributes()secondsattributes()viewCountchildren()average
获得了所有信息之后,进行格式化并在 Web 页面上显示就非常简单了;清单 2 顶部的简单 CSS 规则负责完成这些任务。
注意,清单 2 中使用的提要 URL 是 YouTube 提供给开发人员的各种标准提要之一。其他常用的类型包括:
http://gdata.youtube.com/feeds/api/standardfeeds/top_ratedhttp://gdata.youtube.com/feeds/api/standardfeeds/most_recenthttp://gdata.youtube.com/feeds/api/standardfeeds/most_linked
要访问标准提要的完整列表,请参考 参考资料 中提供的 YouTube Data API 开发者指南。
回页首
处理视频类别
http://gdata.youtube.com/feeds/api/videos/-/Travel/http://gdata.youtube.com/feeds/api/videos/-/Tech/
清单 3 演示了如何检索并列出出 Travel 中的视频:
清单 3. 使用一个 PHP 脚本列出属于 Travel 类别的 YouTube 视频
<!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>Listing videos in a category</title>
</head>
<body>
<?php
// set feed URL
// add category
$feedURL = 'http://gdata.youtube.com/feeds/api/videos/-/Travel/';
// read feed into SimpleXML object
$sxml = simplexml_load_file($feedURL);
// get summary counts from opensearch: namespace
$counts = $sxml->children('http://a9.com/-/spec/opensearchrss/1.0/');
$total = $counts->totalResults;
?>
<h1><?php echo $sxml->title; ?></h1>
<?php echo $total; ?> items found.
<p/>
<ol>
<?php
// iterate over entries in category
// print each entry's details
foreach ($sxml->entry as $entry) {
// get nodes in media: namespace for media information
$media = $entry->children('http://search.yahoo.com/mrss/');
// get video player URL
$attrs = $media->group->player->attributes();
$watch = $attrs['url'];
// get <yt:duration> node for video length
$yt = $media->children('http://gdata.youtube.com/schemas/2007');
$attrs = $yt->duration->attributes();
$length = $attrs['seconds'];
// get <gd:rating> node for video ratings
$gd = $entry->children('http://schemas.google.com/g/2005');
if ($gd->rating) {
$attrs = $gd->rating->attributes();
$rating = $attrs['average'];
} else {
$rating = 0;
}
// print record
echo "<li>\n";
echo "<a href=\"{$watch}\">{$media->group->title}</a>
<br/>\n";
echo sprintf("%0.2f", $length/60) . " min. | {$rating} user rating
<br/>\n";
echo "{$media->group->description}<p/>\n";
echo "<p/></li>\n";
}
?>
</ol>
</body>
</html>
与 清单 2 类似,清单 3 首先向 Travel 类别的提要 URL 发送一个 GET 请求,然后将响应转换为一个 SimpleXML 对象。随后遍历集合中的 <entry> 元素以检索标题、描述、持续时间和用户评分,最后将信息格式化为一个有序列表。清单 3 还添加了一项新特性:查看 XML 响应中 <openSearch:> 元素包含的摘要数据,输出类别中找到的视频总数。
图 2 演示了输出的外观:
图 2. Travel 类别的视频列表
http://gdata.youtube.com/schemas/2007/categories.cat
清单 4. 一个示例 YouTube 类别列表
<?xml version='1.0' encoding='UTF-8'?>
<app:categories xmlns:app='http://www.w3.org/2007/app'
xmlns:atom='http://www.w3.org/2005/Atom'
xmlns:yt='http://gdata.youtube.com/schemas/2007' fixed='yes'
scheme='http://gdata.youtube.com/schemas/2007/categories.cat'>
<atom:category term='Film' label='Film & Animation' xml:lang='en-US'>
<yt:browsable/>
<yt:assignable/>
</atom:category>
<atom:category term='Autos' label='Autos & Vehicles' xml:lang='en-US'>
<yt:browsable/>
<yt:assignable/>
</atom:category>
...
</app:categories>
编写一个 PHP 脚本,使用它检索和解析文件,然后使用 YouTube API 检索每个类别中观看次数最多的前五个视频,这个任务并不困难。清单 5 演示了这种脚本:
清单 5. 使用一个 PHP 脚本列出各个类别中观看最多的前五个 YouTube 视频
<!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>Listing videos in different categories</title>
</head>
<body>
<?php
// set URL for XML feed containing category list
$catURL = 'http://gdata.youtube.com/schemas/2007/categories.cat';
// retrieve category list using atom: namespace
// note: you can cache this list to improve performance,
// as it doesn't change very often!
$cxml = simplexml_load_file($catURL);
$cxml->registerXPathNamespace('atom', 'http://www.w3.org/2005/Atom');
$categories = $cxml->xpath('//atom:category');
// iterate over category list
foreach ($categories as $c) {
// for each category
// set feed URL
$feedURL = "http://gdata.youtube.com/feeds/api/videos/-/{$c['term']}
?max-results=5&orderby=viewCount";
// read feed into SimpleXML object
$sxml = simplexml_load_file($feedURL);
// get summary counts from opensearch: namespace
$counts = $sxml->children('http://a9.com/-/spec/opensearchrss/1.0/');
$total = $counts->totalResults;
?>
<h1><?php echo $c['label']; ?></h1>
<?php echo $total; ?> items found.
<p/>
<?php
echo "<ol>\n";
// iterate over entries in category
// print each entry's details
foreach ($sxml->entry as $entry) {
// get nodes in media: namespace for media information
$media = $entry->children('http://search.yahoo.com/mrss/');
// get video player URL
$attrs = $media->group->player->attributes();
$watch = $attrs['url'];
// get <yt:duration> node for video length
$yt = $media->children('http://gdata.youtube.com/schemas/2007');
$attrs = $yt->duration->attributes();
$length = $attrs['seconds'];
// get <gd:rating> node for video ratings
$gd = $entry->children('http://schemas.google.com/g/2005');
if ($gd->rating) {
$attrs = $gd->rating->attributes();
$rating = $attrs['average'];
} else {
$rating = 0;
}
// print record
echo "<li>\n";
echo "<a href=\"{$watch}\">{$media->group->title}</a>
<br/>\n";
echo sprintf("%0.2f", $length/60) . " min. | {$rating} user rating
<br/>\n";
echo substr($media->group->description,0,50) . "...<p/>\n";
echo "<p/></li>\n";
}
echo "</ol>\n";
}
?>
</body>
</html>
http://gdata.youtube.com/schemas/2007/categories.catxpath()foreach()
max-resultsorderbyviewCountratingpublished
现在,看看 图 3,它包含由 清单 5 生成的样例输出:
图 3. 显示来自各个类别的视频的 Web 页面
回页首
执行关键字搜索
http://gdata.youtube.com/feeds/api/videos/-/boathttp://gdata.youtube.com/feeds/api/videos/-/westminster/london
无疑,这大大简化了使用 PHP 构建简单的搜索引擎的过程,使用这个搜索引擎可以挖掘 YouTube 数据库,搜索与用户提供的关键字匹配的视频。清单 6 展示了这样一种搜索引擎:
清单 6. 一个简单的针对 YouTube 视频的关键字搜索引擎
<!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 videos by keyword</title>
<style>
img {
padding: 2px;
margin-bottom: 15px;
border: solid 1px silver;
}
td {
vertical-align: top;
}
td.line {
border-bottom: solid 1px black;
}
</style>
</head>
<body>
<?php
// if form not submitted
// display search box
if (!isset($_POST['submit'])) {
?>
<h1>Keyword search</h1>
<form method="post" action="<?php echo
htmlentities($_SERVER['PHP_SELF']); ?>">
Keywords: <br/>
<input type="text" name="q" />
<p/>
Items to display: <br/>
<select name="i">
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
</select>
<p/>
<input type="submit" name="submit" value="Search"/>
</form>
<?php
// if form submitted
} else {
// check for search keywords
// trim whitespace
// separate multiple keywords with /
if (!isset($_POST['q']) || empty($_POST['q'])) {
die ('ERROR: Please enter one or more search keywords');
} else {
$q = $_POST['q'];
$q = ereg_replace('[[:space:]]+', '/', trim($q));
}
// set max results
if (!isset($_POST['i']) || empty($_POST['i'])) {
$i = 25;
} else {
$i = $_POST['i'];
}
// generate feed URL
$feedURL = "http://gdata.youtube.com/feeds/api/videos/-/{$q}
?orderby=viewCount&max-results={$i}";
// read feed into SimpleXML object
$sxml = simplexml_load_file($feedURL);
// get summary counts from opensearch: namespace
$counts = $sxml->children('http://a9.com/-/spec/opensearchrss/1.0/');
$total = $counts->totalResults;
$startOffset = $counts->startIndex;
$endOffset = ($startOffset-1) + $counts->itemsPerPage;
?>
<h1>Search results</h1>
<?php echo $total; ?> items found. Showing items
<?php echo $startOffset; ?> to <?php echo $endOffset; ?>:
<p/>
<table>
<?php
// iterate over entries in resultset
// print each entry's details
foreach ($sxml->entry as $entry) {
// get nodes in media: namespace for media information
$media = $entry->children('http://search.yahoo.com/mrss/');
// get video player URL
$attrs = $media->group->player->attributes();
$watch = $attrs['url'];
// get video thumbnail
$attrs = $media->group->thumbnail[0]->attributes();
$thumbnail = $attrs['url'];
// get <yt:duration> node for video length
$yt = $media->children('http://gdata.youtube.com/schemas/2007');
$attrs = $yt->duration->attributes();
$length = $attrs['seconds'];
// get <gd:rating> node for video ratings
$gd = $entry->children('http://schemas.google.com/g/2005');
if ($gd->rating) {
$attrs = $gd->rating->attributes();
$rating = $attrs['average'];
} else {
$rating = 0;
}
// print record
echo "<tr><td colspan=\"2\" class=\"line\"></td>
</tr>\n";
echo "<tr>\n";
echo "<td><a href=\"{$watch}\"><img src=\"$thumbnail\"/></a></td>\n";
echo "<td><a href=\"{$watch}\">
{$media->group->title}</a><br/>\n";
echo sprintf("%0.2f", $length/60) . " min. | {$rating} user
rating<br/>\n";
echo $media->group->description . "</td>\n";
echo "</tr>\n";
}
}
?>
</table>
</body>
</html>
清单 6 包含两个部分,一个表单和它的结果页面,使用一个条件检验分隔。第一部分非常简单。它所做的就是生成一个表单(参见 图 4)以供用户输入搜索关键字并指定要在输出页面中显示的结果数。
图 4. 一个关键字搜索表单
当用户提交该表单后,脚本首先检查是否输入了一个或多个搜索关键字,如果一个也没有输入,则中断脚本处理并显示一条错误消息。如果给出了搜索关键字,脚本将把关键字内插到一个 YouTube 提要 URL 中并生成一个对 YouTube API 的 REST 请求。然后将产生的 XML 文档转换为一个 SimpleXML 对象,并使用前面的清单 2、3 和 5 中介绍的技术进行遍历以生成一个结果页面。
清单 6 中有两点值得注意:
trim()ereg_replace()
图 5 演示了 清单 6 生成的示例搜索结果页面:
图 5. 搜索结果页面
回页首
使用额外的搜索参数
要进行更精确的搜索,考虑向您的搜索查询添加下面这些参数:
start-indexvqformatlrtime
清单 7 使用这些额外的参数对 清单 6 中的搜索引擎脚本进行了增强,并使用 PEAR 中的 Pager 类添加了分页支持:
清单 7. 针对 YouTube 视频的增强的关键字搜索引擎
<!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 videos by keyword</title>
<style>
img {
padding: 2px;
margin-bottom: 15px;
border: solid 1px silver;
}
td {
vertical-align: top;
}
td.line {
border-bottom: solid 1px black;
}
</style>
</head>
<body>
<?php
// if form not submitted
// display search box
if (!isset($_GET['submit'])) {
?>
<h1>Keyword search</h1>
<form method="get" action="<?php echo
htmlentities($_SERVER['PHP_SELF']); ?>">
Keywords: <br/>
<input type="text" name="vq" />
<p/>
Items sorted by: <br/>
<select name="s">
<option value="viewCount">User views</option>
<option value="rating">User rating</option>
<option value="published">Publication time</option>
</select>
<p/>
Items per page: <br/>
<select name="i">
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
</select>
<p/>
<input type="submit" name="submit" value="Search"/>
</form>
<?php
// if form submitted
} else {
// check for search keywords
// trim whitespace
// encode search string
if (!isset($_GET['vq']) || empty($_GET['vq'])) {
die ('ERROR: Please enter one or more search keywords');
} else {
$vq = $_GET['vq'];
$vq = ereg_replace('[[:space:]]+', ' ', trim($vq));
$vq = urlencode($vq);
}
// set max results per page
if (!isset($_GET['i']) || empty($_GET['i'])) {
$i = 25;
} else {
$i = htmlentities($_GET['i']);
}
// set sort critera
if (!isset($_GET['s']) || empty($_GET['s'])) {
$s = 'viewCount';
} else {
$s = htmlentities($_GET['s']);
}
// set start index
if (!isset($_GET['pageID']) || $_GET['pageID'] <= 0) {
$o = 1;
} else {
$pageID = htmlentities($_GET['pageID']);
$o = (($pageID-1) * $i)+1;
}
// generate feed URL
$feedURL = "http://gdata.youtube.com/feeds/api/videos
?vq={$vq}&orderby={$s}&max-results={$i}&start-index={$o}";
// read feed into SimpleXML object
$sxml = simplexml_load_file($feedURL);
// get summary counts from opensearch: namespace
$counts = $sxml->children('http://a9.com/-/spec/opensearchrss/1.0/');
$total = $counts->totalResults;
$startOffset = $counts->startIndex;
$endOffset = ($startOffset-1) + $counts->itemsPerPage;
// include Pager class
require_once 'Pager/Pager.php';
$params = array(
'mode' => 'Jumping',
'perPage' => $i,
'delta' => 5,
'totalItems' => $total,
);
$pager = & Pager::factory($params);
$links = $pager->getLinks();
?>
<h1>Search results</h1>
<?php echo $total; ?> items found.
Showing items <?php echo $startOffset; ?> to
<?php echo $endOffset; ?>:
<p/>
<?php
// print page links
echo $links['all'];
?>
<table>
<?php
// iterate over entries in resultset
// print each entry's details
foreach ($sxml->entry as $entry) {
// get nodes in media: namespace for media information
$media = $entry->children('http://search.yahoo.com/mrss/');
// get video player URL
$attrs = $media->group->player->attributes();
$watch = $attrs['url'];
// get video thumbnail
$attrs = $media->group->thumbnail[0]->attributes();
$thumbnail = $attrs['url'];
// get <yt:duration> node for video length
$yt = $media->children('http://gdata.youtube.com/schemas/2007');
$attrs = $yt->duration->attributes();
$length = $attrs['seconds'];
// get <yt:stats> node for viewer statistics
$yt = $entry->children('http://gdata.youtube.com/schemas/2007');
$attrs = $yt->statistics->attributes();
$viewCount = $attrs['viewCount'];
// get <gd:rating> node for video ratings
$gd = $entry->children('http://schemas.google.com/g/2005');
if ($gd->rating) {
$attrs = $gd->rating->attributes();
$rating = $attrs['average'];
} else {
$rating = 0;
}
// get video ID
$arr = explode('/',$entry->id);
$id = $arr[count($arr)-1];
// print record
echo "<tr><td colspan=\"2\" class=\"line\"></td></tr>\n";
echo "<tr>\n";
echo "<td><a href=\"{$watch}\">
<img src=\"$thumbnail\"/></a></td>\n";
echo "<td><a href=\"{$watch}\">
{$media->group->title}</a><br/>\n";
echo sprintf("%0.2f", $length/60) . " min. | {$rating} user rating |
{$viewCount} views<br/>\n";
echo $media->group->description . "<br/>\n";
echo "<a href=\"details.php?id=$id\">More information</a>
</td>\n";
echo "</tr>\n";
}
}
?>
</table>
</body>
</html>
经过增强的表单现在包含额外的排序参数,如 图 6 所示:
图 6. 增强后的关键字搜索表单
$_GET['pageID']$_GET['pageID']
图 7 演示了修改后的输出,其中显示了页面链接:
图 7. 搜索结果页面
注意每个条目旁边的 More information 链接。该链接指向另一个 PHP 脚本,该脚本显示关于该视频的额外信息,包括它的评论、视频回复、作者信息和相关视频。下一节将讨论如何获取这些信息。
回页首
处理评论、视频回复和相关视频
如果仔细查看 清单 1 中的样例视频条目,将会发现除了视频元数据外,每个视频 <entry> 还包含针对评论、视频回复和相关视频的提要的链接。让我们具体讨论一下每一种情况。
评论是其他 YouTube 用户针对视频发布的反馈。您可以通过视频的评论提要访问视频的评论。这种提要的 URL 位于 <gd:comments> 元素中。下面是一个例子:
<gd:comments>
<gd:feedLink href='http://gdata.youtube.com/feeds/api/videos/dMH0bHeiRNg/comments'
countHint='124130'/>
</gd:comments>
通过一个 GET 请求检索这个链接将生成一个新的 XML 提要。在 清单 8 中,您可以观察到这种提要,其中包含由用户发布的视频评论。
清单 8. 评论提要示例
<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns='http://www.w3.org/2005/Atom'
xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'>
<id>http://gdata.youtube.com/feeds/api/videos/dMH0bHeiRNg/comments</id>
<updated>2008-03-13T11:58:12.729Z</updated>
<category scheme='http://schemas.google.com/g/2005#kind'
term='http://gdata.youtube.com/schemas/2007#comment'/>
<title type='text'>Comments on 'Evolution of Dance'</title>
<logo>http://www.youtube.com/img/pic_youtubelogo_123x63.gif</logo>
<link rel='related' type='application/atom+xml'
href='http://gdata.youtube.com/feeds/api/videos/dMH0bHeiRNg'/>
<link rel='alternate' type='text/html'
href='http://www.youtube.com/watch?v=dMH0bHeiRNg'/>
<link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml'
href='http://gdata.youtube.com/feeds/api/videos/dMH0bHeiRNg/comments'/>
<link rel='self' type='application/atom+xml'
href='http://gdata.youtube.com/feeds/api/videos/dMH0bHeiRNg/
comments?start-index=1&max-results=25'/>
<link rel='next' type='application/atom+xml'
href='http://gdata.youtube.com/feeds/api/videos/dMH0bHeiRNg/
comments?start-index=26&max-results=25'/>
<author>
<name>YouTube</name>
<uri>http://www.youtube.com/</uri>
</author>
<generator version='beta' uri='http://gdata.youtube.com/'>
YouTube data API</generator>
<openSearch:totalResults>124353</openSearch:totalResults>
<openSearch:startIndex>1</openSearch:startIndex>
<openSearch:itemsPerPage>25</openSearch:itemsPerPage>
<entry>
<id>http://gdata.youtube.com/feeds/api/videos/dMH0bHeiRNg/comments/
206D45A8B1191817</id>
<published>2008-03-13T04:57:22.000-07:00</published>
<updated>2008-03-13T04:57:22.000-07:00</updated>
<category scheme='http://schemas.google.com/g/2005#kind'
term='http://gdata.youtube.com/schemas/2007#comment'/>
<title type='text'>how can you say ...</title>
<content type='text'>how can you say that????!??!!!</content>
<link rel='related' type='application/atom+xml'
href='http://gdata.youtube.com/feeds/api/videos/dMH0bHeiRNg'/>
<link rel='http://gdata.youtube.com/schemas/2007#in-reply-to'
type='application/atom+xml'
href='http://gdata.youtube.com/feeds/api/videos/dMH0bHeiRNg/comments/
1DA6D946967C23A6'/>
<link rel='alternate' type='text/html'
href='http://www.youtube.com/watch?v=dMH0bHeiRNg'/>
<link rel='self' type='application/atom+xml'
href='http://gdata.youtube.com/feeds/api/videos/dMH0bHeiRNg/comments/
206D45A8B1191817'/>
<author>
<name>stez407</name>
<uri>http://gdata.youtube.com/feeds/api/users/stez407</uri>
</author>
</entry>
<entry>
...
</entry>
</feed>
href
<link rel='http://gdata.youtube.com/schemas/2007#video.responses'
type='application/atom+xml'
href='http://gdata.youtube.com/feeds/api/videos/dMH0bHeiRNg/responses'/>
href
<link rel='http://gdata.youtube.com/schemas/2007#video.related'
type='application/atom+xml'
href='http://gdata.youtube.com/feeds/api/videos/dMH0bHeiRNg/related'/>
准备好所有这些信息之后,可以轻松地创建一个更详细的视频信息 Web 页面。如 清单 9 所示:
清单 9. 检索视频详细信息的 PHP 脚本
<!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>Retrieving video details</title>
<style>
img {
padding: 2px;
margin-bottom: 15px;
border: solid 1px silver;
}
td {
vertical-align: top;
}
</style>
</head>
<body>
<?php
// function to parse a video <entry>
function parseVideoEntry($entry) {
$obj= new stdClass;
// get nodes in media: namespace for media information
$media = $entry->children('http://search.yahoo.com/mrss/');
$obj->title = $media->group->title;
$obj->description = $media->group->description;
// get video player URL
$attrs = $media->group->player->attributes();
$obj->watchURL = $attrs['url'];
// get video thumbnail
$attrs = $media->group->thumbnail[0]->attributes();
$obj->thumbnailURL = $attrs['url'];
// get <yt:duration> node for video length
$yt = $media->children('http://gdata.youtube.com/schemas/2007');
$attrs = $yt->duration->attributes();
$obj->length = $attrs['seconds'];
// get <yt:stats> node for viewer statistics
$yt = $entry->children('http://gdata.youtube.com/schemas/2007');
$attrs = $yt->statistics->attributes();
$obj->viewCount = $attrs['viewCount'];
// get <gd:rating> node for video ratings
$gd = $entry->children('http://schemas.google.com/g/2005');
if ($gd->rating) {
$attrs = $gd->rating->attributes();
$obj->rating = $attrs['average'];
} else {
$obj->rating = 0;
}
// get <gd:comments> node for video comments
$gd = $entry->children('http://schemas.google.com/g/2005');
if ($gd->comments->feedLink) {
$attrs = $gd->comments->feedLink->attributes();
$obj->commentsURL = $attrs['href'];
$obj->commentsCount = $attrs['countHint'];
}
// get feed URL for video responses
$entry->registerXPathNamespace('feed', 'http://www.w3.org/2005/Atom');
$nodeset = $entry->xpath("feed:link[@rel='http://gdata.youtube.com/schemas/
2007#video.responses']");
if (count($nodeset) > 0) {
$obj->responsesURL = $nodeset[0]['href'];
}
// get feed URL for related videos
$entry->registerXPathNamespace('feed', 'http://www.w3.org/2005/Atom');
$nodeset = $entry->xpath("feed:link[@rel='http://gdata.youtube.com/schemas/
2007#video.related']");
if (count($nodeset) > 0) {
$obj->relatedURL = $nodeset[0]['href'];
}
// return object to caller
return $obj;
}
// get video ID from $_GET
if (!isset($_GET['id'])) {
die ('ERROR: Missing video ID');
} else {
$vid = $_GET['id'];
}
// set video data feed URL
$feedURL = 'http://gdata.youtube.com/feeds/api/videos/' . $vid;
// read feed into SimpleXML object
$entry = simplexml_load_file($feedURL);
// parse video entry
$video = parseVideoEntry($entry);
// display main video record
echo "<table>\n";
echo "<tr>\n";
echo "<td><a href=\"{$video->watchURL}\">
<img src=\"$video->thumbnailURL\"/></a></td>\n";
echo "<td><a href=\"{$video->watchURL}\">{$video->title}</a>
<br/>\n";
echo sprintf("%0.2f", $video->length/60) . " min. | {$video->rating}
user rating | {$video->viewCount} views<br/>\n";
echo $video->description . "</td>\n";
echo "</tr>\n";
// read 'video comments' feed into SimpleXML object
// parse and display each comment
if ($video->commentsURL && $video->commentsCount > 0) {
$commentsFeed = simplexml_load_file($video->commentsURL);
echo "<tr><td colspan=\"2\"><h3>" . $commentsFeed->title .
"</h3></td></tr>\n";
echo "<tr><td colspan=\"2\"><ol>\n";
foreach ($commentsFeed->entry as $comment) {
echo "<li>" . $comment->content . "</li>\n";
}
echo "</ol></td></tr>\n";
}
// read 'video responses' feed into SimpleXML object
// parse and display each video entry
if ($video->responsesURL) {
$responseFeed = simplexml_load_file($video->responsesURL);
echo "<tr><td colspan=\"2\"><h3>" .
$responseFeed->title . "</h3></td></tr>\n";
foreach ($responseFeed->entry as $response) {
$responseVideo = parseVideoEntry($response);
echo "<tr>\n";
echo "<td><a href=\"{$responseVideo->watchURL}\">
<img src=\"$responseVideo->thumbnailURL\"/></a></td>\n";
echo "<td><a href=\"{$responseVideo->watchURL}\"
>{$responseVideo->title}</a><br/>\n";
echo sprintf("%0.2f", $responseVideo->length/60) . " min. |
{$responseVideo->rating} user rating | {$responseVideo->viewCount}
views<br/>\n";
echo $responseVideo->description . "</td>\n";
echo "</tr>\n";
}
}
// read 'related videos' feed into SimpleXML object
// parse and display each video entry
if ($video->relatedURL) {
$relatedFeed = simplexml_load_file($video->relatedURL);
echo "<tr><td colspan=\"2\"><h3>" .
$relatedFeed->title . "</h3></td></tr>\n";
foreach ($relatedFeed->entry as $related) {
$relatedVideo = parseVideoEntry($related);
echo "<tr>\n";
echo "<td><a href=\"{$relatedVideo->watchURL}\">
<img src=\"$relatedVideo->thumbnailURL\"/></a></td>\n";
echo "<td><a href=\"{$relatedVideo->watchURL}\">
{$relatedVideo->title}</a><br/>\n";
echo sprintf("%0.2f", $relatedVideo->length/60) . " min. |
{$relatedVideo->rating} user rating | {$relatedVideo->viewCount}
views<br/>\n";
echo $relatedVideo->description . "</td>\n";
echo "</tr>\n";
}
}
echo "</table>\n";
?>
</body>
</html>
parseVideoEntry()stdClass
parseVideoEntry()stdClass
parseVideoEntry()
图 8. 显示视频评论、回复和相关视频的 Web 页面
回页首
访问作者信息
YouTube API 还允许开发人员访问上传特定视频的用户的个人信息。仔细查看 清单 1,您会发现 <author> 元素。该元素提供了视频所有者的用户名,以及用户个人信息提要的 URL。下面是一个例子:
<author>
<name>judsonlaipply</name>
<uri>http://gdata.youtube.com/feeds/api/users/judsonlaipply</uri>
</author>
对这个 URL 的 GET 请求将生成类似 清单 10 所示的 XML 提要:
清单 10. 作者提要示例
<?xml version='1.0' encoding='UTF-8'?>
<entry xmlns='http://www.w3.org/2005/Atom'
xmlns:media='http://search.yahoo.com/mrss/'
xmlns:yt='http://gdata.youtube.com/schemas/2007'
xmlns:gd='http://schemas.google.com/g/2005'>
<id>http://gdata.youtube.com/feeds/api/users/judsonlaipply</id>
<published>2006-03-23T07:20:45.000-08:00</published>
<updated>2008-03-13T09:33:36.000-07:00</updated>
<category scheme='http://gdata.youtube.com/schemas/2007/channeltypes.cat'
term='Standard'/>
<category scheme='http://schemas.google.com/g/2005#kind'
term='http://gdata.youtube.com/schemas/2007#userProfile'/>
<title type='text'>judsonlaipply Channel</title>
<link rel='http://gdata.youtube.com/schemas/2007#featured-video'
type='application/atom+xml' href='http://gdata.youtube.com/feeds/api/
videos/61heClTFc5w'/>
<link rel='alternate' type='text/html'
href='http://www.youtube.com/profile?user=judsonlaipply'/>
<link rel='self' type='application/atom+xml'
href='http://gdata.youtube.com/feeds/api/users/judsonlaipply'/>
<author>
<name>judsonlaipply</name>
<uri>http://gdata.youtube.com/feeds/api/users/judsonlaipply</uri>
</author>
<yt:age>31</yt:age>
<yt:username>judsonlaipply</yt:username>
<yt:gender>m</yt:gender>
<yt:location>US</yt:location>
<media:thumbnail url='http://i.ytimg.com/vi/61heClTFc5w/default.jpg'/>
<yt:statistics viewCount='1120502' videoWatchCount='858'
subscriberCount='21800' lastWebAccess='2008-03-11T15:36:18.000-07:00'/>
<gd:feedLink rel='http://gdata.youtube.com/schemas/2007#user.favorites'
href='http://gdata.youtube.com/feeds/api/users/judsonlaipply/favorites'
countHint='0'/>
<gd:feedLink rel='http://gdata.youtube.com/schemas/2007#user.contacts'
href='http://gdata.youtube.com/feeds/api/users/judsonlaipply/contacts'
countHint='2331'/>
<gd:feedLink rel='http://gdata.youtube.com/schemas/2007#user.inbox'
href='http://gdata.youtube.com/feeds/api/users/judsonlaipply/inbox'
countHint='20143'/>
<gd:feedLink rel='http://gdata.youtube.com/schemas/2007#user.playlists'
href='http://gdata.youtube.com/feeds/api/users/judsonlaipply/playlists'/>
<gd:feedLink rel='http://gdata.youtube.com/schemas/2007#user.subscriptions'
href='http://gdata.youtube.com/feeds/api/users/judsonlaipply/subscriptions'
countHint='1'/>
<gd:feedLink rel='http://gdata.youtube.com/schemas/2007#user.uploads'
href='http://gdata.youtube.com/feeds/api/users/judsonlaipply/uploads' countHint='2'/>
</entry>
如您所见,清单 10 提供了一份用户基本个人信息 — 用户姓名、位置、年龄和性别 — 以及各种 URL,包括用户最喜爱的视频、订阅、联系方式、收件箱(要求身份验证),以及上传的视频列表。因此可以很轻松地解析 清单 10 中的数据并将一些信息添加到 清单 9 生成的页面中。Listing 11 展示了这一变化:
清单 11. 检索详细作者信息的 PHP 脚本
<!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>Retrieving video details</title>
<style>
img {
padding: 2px;
margin-bottom: 15px;
border: solid 1px silver;
}
td {
vertical-align: top;
}
</style>
</head>
<body>
<?php
// function to parse a video <entry>
function parseVideoEntry($entry) {
$obj= new stdClass;
// get author name and feed URL
$obj->author = $entry->author->name;
$obj->authorURL = $entry->author->uri;
// get nodes in media: namespace for media information
$media = $entry->children('http://search.yahoo.com/mrss/');
$obj->title = $media->group->title;
$obj->description = $media->group->description;
// get video player URL
$attrs = $media->group->player->attributes();
$obj->watchURL = $attrs['url'];
// get video thumbnail
$attrs = $media->group->thumbnail[0]->attributes();
$obj->thumbnailURL = $attrs['url'];
// get <yt:duration> node for video length
$yt = $media->children('http://gdata.youtube.com/schemas/2007');
$attrs = $yt->duration->attributes();
$obj->length = $attrs['seconds'];
// get <yt:stats> node for viewer statistics
$yt = $entry->children('http://gdata.youtube.com/schemas/2007');
$attrs = $yt->statistics->attributes();
$obj->viewCount = $attrs['viewCount'];
// get <gd:rating> node for video ratings
$gd = $entry->children('http://schemas.google.com/g/2005');
if ($gd->rating) {
$attrs = $gd->rating->attributes();
$obj->rating = $attrs['average'];
} else {
$obj->rating = 0;
}
// get <gd:comments> node for video comments
$gd = $entry->children('http://schemas.google.com/g/2005');
if ($gd->comments->feedLink) {
$attrs = $gd->comments->feedLink->attributes();
$obj->commentsURL = $attrs['href'];
$obj->commentsCount = $attrs['countHint'];
}
// get feed URL for video responses
$entry->registerXPathNamespace('feed', 'http://www.w3.org/2005/Atom');
$nodeset = $entry->xpath("feed:link[@rel='http://gdata.youtube.com/
schemas/2007#video.responses']");
if (count($nodeset) > 0) {
$obj->responsesURL = $nodeset[0]['href'];
}
// get feed URL for related videos
$entry->registerXPathNamespace('feed', 'http://www.w3.org/2005/Atom');
$nodeset = $entry->xpath("feed:link[@rel='http://gdata.youtube.com/
schemas/2007#video.related']");
if (count($nodeset) > 0) {
$obj->relatedURL = $nodeset[0]['href'];
}
// return object to caller
return $obj;
}
// get video ID from $_GET
if (!isset($_GET['id'])) {
die ('ERROR: Missing video ID');
} else {
$vid = $_GET['id'];
}
// set video data feed URL
$feedURL = 'http://gdata.youtube.com/feeds/api/videos/' . $vid;
// read feed into SimpleXML object
$entry = simplexml_load_file($feedURL);
// parse video entry
$video = parseVideoEntry($entry);
// display main video record
echo "<table>\n";
echo "<tr>\n";
echo "<td><a href=\"{$video->watchURL}\">
<img src=\"$video->thumbnailURL\"/></a></td>\n";
echo "<td><a href=\"{$video->watchURL}\">{$video->title}</a>
<br/>\n";
echo sprintf("%0.2f", $video->length/60) . " min.
| {$video->rating} user rating | {$video->viewCount} views<br/>\n";
echo $video->description . "</td>\n";
echo "</tr>\n";
// read 'author profile feed' into SimpleXML object
// parse and display author bio
$authorFeed = simplexml_load_file($video->authorURL);
echo "<tr><td colspan=\"2\"><h3>Author information</h3>
</td></tr>\n";
$authorData = $authorFeed->children('http://gdata.youtube.com/schemas/2007');
echo "<tr><td>Username:</td><td>" . $video->author .
"</td></tr>\n";
echo "<tr><td>Age:</td><td>" . $authorData->age .
"</td></tr>\n";
echo "<tr><td>Gender:</td><td>" .
strtoupper($authorData->gender) . "</td></tr>\n";
echo "<tr><td>Location:</td><td>" . $authorData->location
. "</td></tr>\n";
// read 'video comments' feed into SimpleXML object
// parse and display each comment
if ($video->commentsURL && $video->commentsCount > 0) {
$commentsFeed = simplexml_load_file($video->commentsURL);
echo "<tr><td colspan=\"2\"><h3>" .
$commentsFeed->title . "</h3></td></tr>\n";
echo "<tr><td colspan=\"2\"><ol>\n";
foreach ($commentsFeed->entry as $comment) {
echo "<li>" . $comment->content . "</li>\n";
}
echo "</ol></td></tr>\n";
}
// read 'video responses' feed into SimpleXML object
// parse and display each video entry
if ($video->responsesURL) {
$responseFeed = simplexml_load_file($video->responsesURL);
echo "<tr><td colspan=\"2\"><h3>" .
$responseFeed->title . "</h3></td></tr>\n";
foreach ($responseFeed->entry as $response) {
$responseVideo = parseVideoEntry($response);
echo "<tr>\n";
echo "<td><a href=\"{$responseVideo->watchURL}\">
<img src=\"$responseVideo->thumbnailURL\"/></a></td>\n";
echo "<td><a href=\"{$responseVideo->watchURL}\">
{$responseVideo->title}</a><br/>\n";
echo sprintf("%0.2f", $responseVideo->length/60) . " min. |
{$responseVideo->rating} user rating | {$responseVideo->viewCount}
views<br/>\n";
echo $responseVideo->description . "</td>\n";
echo "</tr>\n";
}
}
// read 'related videos' feed into SimpleXML object
// parse and display each video entry
if ($video->relatedURL) {
$relatedFeed = simplexml_load_file($video->relatedURL);
echo "<tr><td colspan=\"2\"><h3>" .
$relatedFeed->title . "</h3></td></tr>\n";
foreach ($relatedFeed->entry as $related) {
$relatedVideo = parseVideoEntry($related);
echo "<tr>\n";
echo "<td><a href=\"{$relatedVideo->watchURL}\">
<img src=\"$relatedVideo->thumbnailURL\"/></a></td>\n";
echo "<td><a href=\"{$relatedVideo->watchURL}\">
{$relatedVideo->title}</a><br/>\n";
echo sprintf("%0.2f", $relatedVideo->length/60) . " min. |
{$relatedVideo->rating} user rating | {$relatedVideo->viewCount}
views<br/>\n";
echo $relatedVideo->description . "</td>\n";
echo "</tr>\n";
}
}
echo "</table>\n";
?>
</body>
</html>
parseVideoEntry()
图 9 显示了修改后的输出:
图 9. 显示作者信息的 Web 页面
回页首
结束语
本文介绍了如何使用 SimpleXML 将 YouTube 服务中的数据集成到 PHP 应用程序中。本文中的示例演示了以下内容:
- 介绍 YouTube 的提要
- 展示如何按照类别和关键字搜索视频
- 解释如何提取视频的元数据
- 演示如何获取辅助信息,例如评论、相关视频、视频回复和作者个人信息
这些示例证明,YouTube REST API 为开发人员提供了极好的灵活性,使他们可以自由创建新的 Web 应用程序。如果尝试将 YouTube 数据与来自其他 Web 服务的数据聚合,或者仅仅是想为 YouTube 社区构建一个定制的接口,那么 YouTube REST API 将非常有用。
下载
| 描述 | 名字 | 大小 | 下载方法 |
|---|---|---|---|
| 本文的样例代码 | x-youtubeapi-code.zip | 8KB |

