 |  |
|
난이도 : 중급 Vikram Vaswani, Founder, Melonfire
옮긴이: 박재호 이해영 dwkorea@kr.ibm.com
2008 년 9 월 02 일 유튜브(YouTube) 동영상 공유 사이트는 REST 기반 API로 공개된 사이트 내용을 웹 개발자들에게 공개합니다. PHP가 제공하는 SimpleXML 확장 기능을 사용하면 이 API가 내놓는 XML 피드를 처리하여 PHP 응용 프로그램에 통합하기 쉬워집니다. 이번 기사에서는 유튜브 API를 소개하고, API를 사용하여 사용자가 올린 동영상을 찾아보고, 동영상 메타데이터와 덧글과 사용자 반응을 가져오고, 키워드 검색을 수행하는 방법도 살펴봅니다.
소개
온라인 동영상 공유 사이트로는 유튜브가 단연 독보적인 인기를 끌고 있다. 매일 새로 올라오는 동영상 수가 수십만 개에 이르며, 하루 페이지 조회 수만도 수십억 회에 이른다. 유튜브는 홈 동영상뿐만 아니라 음악 동영상, TV 쇼 클립, 개봉할 영화 예고편, 애니메이션 클립 등 온갓 동영상을 제공한다. 사용자는 동영상에 키워드 태그를 추가할 수 있으며, 특정 시점에서 가장 인기 있는 동영상을 감상할 수도 있다.
 |
자주 쓰이는 약어 소개
- API: application programming interface
- HTTP: Hypertext Transfer Protocol
- PEAR: PHP Extension and Application Repository
- PHP: PHP Hypertext Preprocessor
- REST: Representational state transfer
- RSS: Really Simple Syndication
- URL: Uniform Resource Locator
- XML: Extensible Markup Language
|
|
유튜브가 제공하는 멋진 기능 중 하나가 유튜브 API다. 유튜브 API는 개발자가 유튜브 동영상 자료에 접근하고 검색하도록 지원하는 REST 기반 API로, XML을 인식하는 응용 프로그램이라면 어디서나 유튜브 동영상을 프로그램에 통합할 수 있다. API를 사용하는 방법도 별로 어렵지 않다. 응용 프로그램은 REST 요청을 보낸 후 XML 응답을 받는다. 그런 다음, 이 XML 응답을 분석해 동영상 자료를 응용 프로그램 인터페이스에 적절히 추가하면 그만이다. PHP를 사용한다면 유튜브 PHP 클라이언트 라이브러리를 사용해도 좋다. 아니면 REST 질의로 돌아오는 XML 응답을 직접 분석해도 괜찮다.
이 기사에서는 후자를 택한다. 즉, 유튜브 API를 사용하여 공개 동영상 정보를 가져온 후 SimpleXML 확장 기능을 사용하여 XML 응답을 분석한다. 그런 다음, 가져온 내용을 PHP 응용 프로그램에 통합한다. 구체적으로는 1) 특정한 범주에 속하는 동영상 목록을 가져오는 법, 2) 키워드로 동영상을 검색하는 법, 3) 축소판 이미지와 통계 등 동영상 메타데이터를 가져오는 법, 4) 사용자 프로필 정보를 가져오는 법을 살펴본다.
유튜브 자료 형식 이해하기
PHP 코드로 뛰어들기 전에 유튜브 API부터 살펴보자. 여느 REST 기반 서비스와 마찬가지로 유튜브 API 역시 특정 URL로 HTTP 요청을 보낸다. 필요한 몇 가지 매개변수를 추가하여 HTTP 요청을 보내면, 유튜브 서버는 Atom 형식이나 RSS 형식으로 응답한다. XML을 인식하는 클라이언트라면 Atom 형식이나 RSS 형식을 문제 없이 처리한다.
동작 원리를 살펴보려면, 웹 브라우저에서 http://gdata.youtube.com/feeds/api/standardfeeds/most_viewed 사이트를 띄워본다. 이 REST 메서드는 현재 조회수가 가장 높은 유튜브 동영상 목록을 반환한다. (결과 페이지에서 소스 코드 보기로 확인이 가능한) 브라우저에 뜨는 XML 응답 정보는 관련 동영상 정보를 포함한다. 결과 예제는 Listing 1을 참조한다.
Listing 1. 유튜브 API가 반환한 예제 피드
<?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:gml='http://www.opengis.net/gml' xmlns:georss='http://www.georss.org/georss'
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/standardfeeds/most_viewed</id>
<updated>2008-03-06T14:43:27.000-08:00</updated>
<category scheme='http://schemas.google.com/g/2005#kind'
term='http://gdata.youtube.com/schemas/2007#video'/>
<title type='text'>Most Viewed</title>
<logo>http://www.youtube.com/img/pic_youtubelogo_123x63.gif</logo>
<link rel='alternate' type='text/html' href='http://www.youtube.com/browse?s=mp'/>
<link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml'
href='http://gdata.youtube.com/feeds/api/standardfeeds/most_viewed'/>
<link rel='self' type='application/atom+xml'
href='http://gdata.youtube.com/feeds/api/standardfeeds/most_viewed
?start-index=1&max-results=5'/>
<link rel='next' type='application/atom+xml'
href='http://gdata.youtube.com/feeds/api/standardfeeds/most_viewed
?start-index=6&max-results=5'/>
<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>94</openSearch:totalResults>
<openSearch:startIndex>1</openSearch:startIndex>
<openSearch:itemsPerPage>5</openSearch:itemsPerPage>
<entry>
<id>http://gdata.youtube.com/feeds/api/videos/dMH0bHeiRNg</id>
<published>2006-04-06T14:30:53.000-07:00</published>
<updated>2008-03-12T00:22:25.000-07:00</updated>
<category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat'
term='Dancing'/>
<category scheme='http://schemas.google.com/g/2005#kind'
term='http://gdata.youtube.com/schemas/2007#video'/>
<category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat'
term='comedy'/>
<category scheme='http://gdata.youtube.com/schemas/2007/categories.cat'
term='Comedy' label='Comedy'/>
<title type='text'>Evolution of Dance</title>
<content type='text'>The funniest 6 minutes you will ever see!
Remember how many of these you have done!
Judson Laipply is dancing -
http://www.evolutionofdance.com -
for more info including song list!</content>
<link rel='alternate' type='text/html'
href='http://www.youtube.com/watch?v=dMH0bHeiRNg'/>
<link rel='http://gdata.youtube.com/schemas/2007#video.responses'
type='application/atom+xml'
href='http://gdata.youtube.com/feeds/api/videos/dMH0bHeiRNg/responses'/>
<link rel='http://gdata.youtube.com/schemas/2007#video.related'
type='application/atom+xml'
href='http://gdata.youtube.com/feeds/api/videos/dMH0bHeiRNg/related'/>
<link rel='self' type='application/atom+xml'
href='http://gdata.youtube.com/feeds/api/standardfeeds/most_viewed/dMH0bHeiRNg'/>
<author>
<name>judsonlaipply</name>
<uri>http://gdata.youtube.com/feeds/api/users/judsonlaipply</uri>
</author>
<media:group>
<media:title type='plain'>Evolution of Dance</media:title>
<media:description type='plain'>The funniest 6 minutes you will ever see!
Remember how many of these you have done!
Judson Laipply is dancing -
http://www.evolutionofdance.com -
for more info including song list!</media:description>
<media:keywords>comedy, Dancing</media:keywords>
<yt:duration seconds='360'/>
<media:category label='Comedy'
scheme='http://gdata.youtube.com/schemas/2007/categories.cat'>Comedy
</media:category>
<media:content
url='http://www.youtube.com/v/dMH0bHeiRNg' type='application/x-shockwave-flash'
medium='video' isDefault='true' expression='full' duration='360' yt:format='5'/>
<media:content
url='rtsp://rtsp2.youtube.com/ChoLENy73wIaEQnYRKJ3bPTBdBMYDSANFEgGDA==
/0/0/0/video.3gp' type='video/3gpp' medium='video' expression='full' duration='360'
yt:format='1'/>
<media:content
url='rtsp://rtsp2.youtube.com/ChoLENy73wIaEQnYRKJ3bPTBdBMYESARFEgGDA==
/0/0/0/video.3gp' type='video/3gpp' medium='video' expression='full' duration='360'
yt:format='6'/>
<media:player url='http://www.youtube.com/watch?v=dMH0bHeiRNg'/>
<media:thumbnail
url='http://img.youtube.com/vi/dMH0bHeiRNg/2.jpg' height='97' width='130'
time='00:03:00'/>
<media:thumbnail
url='http://img.youtube.com/vi/dMH0bHeiRNg/1.jpg' height='97' width='130'
time='00:01:30'/>
<media:thumbnail
url='http://img.youtube.com/vi/dMH0bHeiRNg/3.jpg' height='97' width='130'
time='00:04:30'/>
<media:thumbnail
url='http://img.youtube.com/vi/dMH0bHeiRNg/0.jpg' height='240' width='320'
time='00:03:00'/>
</media:group>
<yt:statistics viewCount='78060679' favoriteCount='400468'/>
<gd:rating min='1' max='5' numRaters='276123' average='4.65'/>
<gd:comments>
<gd:feedLink href='http://gdata.youtube.com/feeds/api/videos/dMH0bHeiRNg
/comments' countHint='124130'/>
</gd:comments>
</entry>
<entry>
<id>http://gdata.youtube.com/feeds/api/videos/cQ25-glGRzI</id>
<published>2007-02-27T15:08:01.000-08:00</published>
<updated>2008-03-12T00:46:25.000-07:00</updated>
...
</entry>
</feed>
|
결과를 간략히 훑어보면서 XML 구조를 이해하자.
- 클라이언트가 유튜브 API에 REST 요청을 보내면, 유튜브 API는 요청한 정보가 담긴 피드를 반환한다. 유튜브 API가 반환하는 피드 종류는 다양하다. 동영상 피드, 동영상 덧글 피드, 사용자 재생목록 피드, 사용자 연락처 피드 등이다. 그러므로 대다수 XML 응답은 루트 요소가 <feed> 엘리먼트다. <feed> 엘리먼트는 <link> 엘리먼트와 <openSearch:> 엘리먼트를 포함한다. <link> 엘리먼트는 현재 페이지 URL, 이전 페이지 URL, 다음 페이지 URL을 포함하며, <openSearch:>는 검색에 대한 요약 통계 정보를 포함한다.
- 가장 바깥에 있는 <feed> 엘리먼트는 <entry> 엘리먼트를 한 개 이상 포함한다. 각 <entry> 엘리먼트는 요청한 조건에 일치하는 동영상 정보를 포함한다. 여기에는 범주, 제목, 설명, 게시일, 작성자, 길이 등이 포함된다. 각 <entry> 엘리먼트 역시 <link> 엘리먼트를 포함한다. 이 <link> 엘리먼트는 동영상 URL, 동영상 응답 URL, 기타 유튜브에 있는 관련 동영상 URL을 포함한다.
- 각 <entry> 엘리먼트는 <media:group> 엘리먼트 하나를 포함한다. <media:group> 엘리먼트는 해당 동영상 정보를 포함한다. 여기에는 제목, 설명, 사용 가능한 형식, 축소판 이미지 링크, 동영상 재생기 링크 등을 포함한다.
- 각 <entry> 엘리먼트는 <yt:statistics> 엘리먼트를 하나 포함한다. <yt:statistics> 엘리먼트는 동영상 통계 정보를 포함한다. <gd:rating> 엘리먼트는 동영상 평균 평가 점수와 평가에 참여한 전체 사용자 수를 제공한다.
위에서 보듯이, 유튜브 API가 반환하는 피드는 상당한 정보를 제공한다. 십중팔구 클라이언트가 필요로 하는 정보보다 훨씬 많다. 이렇듯 유튜브 API가 풍부한 정보를 제공하는 덕택에 개발자 입장에서는 신이 난다. 새롭고 창의적인 응용 프로그램을 개발할 기회가 더욱 커지니 말이다.
모든 유튜브 API를 위와 같은 방식으로 사용하지는 못한다. 일부 API는 개발자 키나 인증 토큰이 필요하다. 동영상을 올리거나, 사용자 프로필 정보를 수정하거나, 평가 점수를 매기거나, 덧글을 다는 등 사이트 내용을 수정하는 API가 여기에 해당한다. 이런 API에 접근하려면 유튜브에서 개발자로 등록한다(링크는 참고자료에서 제공한다). 이 기사에서는 개발자 키가 필요 없는 API만 다룬다. 잠시 후에 보겠지만, 공개 API만으로도 한동안은 즐거우리라.
동영상 목록 가져오기
이제 본격적으로 PHP 코드에서 유튜브 데이터 API를 사용하는 예를 살펴보자. Listing 2는 Listing 1 피드를 받아서 필요한 정보를 추출한 후 웹 페이지로 출력한다. 여기서는 SimpleXML 확장 기능을 사용한다.
Listing 2. 조회수가 가장 높은 유튜브 동영상을 열거하는 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>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
// 피드 URL 설정
$feedURL = 'http://gdata.youtube.com/feeds/api/standardfeeds/most_viewed';
// 피드를 SimpleXML 객체로 읽음
$sxml = simplexml_load_file($feedURL);
?>
<h1><?php echo $sxml->title; ?></h1>
<?php
// 피드에서 항목을 순회함
foreach ($sxml->entry as $entry) {
// get nodes in media: namespace for media information
$media = $entry->children('http://search.yahoo.com/mrss/');
// 비디오 재생기 URL 얻기
$attrs = $media->group->player->attributes();
$watch = $attrs['url'];
// 동영상 축소판 이미지 얻기
$attrs = $media->group->thumbnail[0]->attributes();
$thumbnail = $attrs['url'];
// 동영상 길이를 얻기 위해 <yt:duration> 노드 탐색
$yt = $media->children('http://gdata.youtube.com/schemas/2007');
$attrs = $yt->duration->attributes();
$length = $attrs['seconds'];
// 접근 횟수 통계를 얻기 위해 <yt:stats> 노드 탐색
$yt = $entry->children('http://gdata.youtube.com/schemas/2007');
$attrs = $yt->statistics->attributes();
$viewCount = $attrs['viewCount'];
// 동영상 평가 점수를 얻기 위해 <gd:rating> 노드 탐색
$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. 조회수가 가장 높은 동영상 목록
Listing 2를 살펴보자. 가장 먼저, simplexml_load_file() 객체를 사용하여 피드 URL로 요청을 보낸 후 결과를 SimpleXML 객체로 변환한다. 그런 다음, foreach() 루프를 돌면서 <entry> 요소를 찾아서 그림 1에 출력한 정보를 추출한다. 각 값을 얻는 방법은 다음과 같다.
- 동영상 메타데이터는 <entry> 노드 내 <media:group> 노드 아래 있다. SimpleXML의
children() 메서드와 media: 이름 공간을 조합하면 $media 변수에 노드 집합을 반환한다. 그런 다음, $media->group->title로 동영상 제목을, $media->group->description으로 동영상 설명을 각각 가져온다.
- <media:thumbnail> 엘리먼트의
url 속성은 동영상 축소판 이미지 URL을 포함한다. 이 값을 찾으려면 $media->group->thumbnail[0]->attributes() 메서드를 호출한 후 결과 속성 배열에서 url 키를 참조한다.
- <media:player> 엘리먼트의
url 속성은 동영상 재생기 URL을 포함한다. 이 값을 찾으려면 $media->group->player->attributes() 메서드를 호출한 후 결과 속성 배열에서 url 키를 참조한다.
- <media:group> 엘리먼트 내 <yt:duration> 엘리먼트는 동영상 길이(초)를 포함한다.
$media->children() 메서드와 yt: 이름 공간을 조합하여 SimpleXML 객체로 반환되는 <yt:duration> 노드를 찾는다. 이 객체에서 attributes() 메서드를 호출하여 seconds 속성 값을 찾는다. 동영상 길이를 분 단위로 얻으려면 이 값을 60으로 나눈다.
- 비슷한 방법으로 <yt:stats> 엘리먼트에 있는 동영상 접근 횟수 통계를 찾는다. <yt:duration> 엘리먼트와 마찬가지로
yt: 이름 공간을 지정하여 $media->children() 메서드를 호출한다. 반환되는 SimpleXML 객체에 attributes() 메서드를 호출한 후 viewCount 속성 값을 찾는다.
- 동영상 평가 점수는 <entry> 엘리먼트 내 <gd:ratings> 엘리먼트에 있다. 앞서와 비슷하게,
gd: 이름 공간을 지정하여 $entry->children() 메서드를 호출한다. 평균 평가 점수를 얻으려면 반환되는 SimpleXML 객체에서 average 속성 값을 찾는다.
모든 정보를 추출했다면 이제 가공해 웹 페이지에 뿌려준다. Listing 2 상단에서 정의한 간단한 CSS 규칙이 웹 페이지 모양을 결정한다.
Listing 2에서 사용한 피드 URL은 유튜브가 개발자에게 제공하는 표준 피드 중 하나다. 이 외에도 많이 사용하는 피드는 다음과 같다.
- 평가 점수가 가장 놓은 동영상:
http://gdata.youtube.com/feeds/api/standardfeeds/top_rated
- 최근에 올라온 동영상:
http://gdata.youtube.com/feeds/api/standardfeeds/most_recent
- 가장 많이 연결된 동영상:
http://gdata.youtube.com/feeds/api/standardfeeds/most_linked
표준 피드 목록 전체를 보려면 YouTube Data API Developer's Guide를 참고한다. 링크는 참고자료에서 제공한다.
특정 범주에 속하는 동영상 목록 가져오기
앞 절에서 소개한 표준 피드 외에도 유튜브는 특정 범주에 속하는 동영상 목록을 가져오는 피드를 제공한다. 구체적으로는 URL 뒤에 카테고리 이름을 추가하면 된다. 예를 들면, http://gdata.youtube.com/feeds/api/videos/-/Travel/ 혹은 http://gdata.youtube.com/feeds/api/videos/-/Tech/와 같이 사용한다.
Listing 3은 여행(Travel) 범주에 속하는 동영상 목록을 가져와서 표시한다.
Listing 3. 여행(Travel) 범주에 속하는 동영상 목록을 표시하는 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>Listing videos in a category</title>
</head>
<body>
<?php
// set feed URL
// add category
$feedURL = 'http://gdata.youtube.com/feeds/api/videos/-/Travel/';
// 피드를 SimpleXML 객체로 읽어들임
$sxml = simplexml_load_file($feedURL);
// opensearch: 이름 공간에서 요약 횟수 얻기
$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
// 범주에 속한 항목 순회
// 각 항목 세부 내역 출력
foreach ($sxml->entry as $entry) {
// 매체 정보를 위한 media: 이름 공간에서 노드 얻기
$media = $entry->children('http://search.yahoo.com/mrss/');
// 동영상 재생기 URL 얻기
$attrs = $media->group->player->attributes();
$watch = $attrs['url'];
// 동영상 길이를 얻기 위해 <yt:duration> 노드 탐색
$yt = $media->children('http://gdata.youtube.com/schemas/2007');
$attrs = $yt->duration->attributes();
$length = $attrs['seconds'];
// 동영상 평가 점수를 얻기 위해 <gd:rating> 노드 탐색
$gd = $entry->children('http://schemas.google.com/g/2005');
if ($gd->rating) {
$attrs = $gd->rating->attributes();
$rating = $attrs['average'];
} else {
$rating = 0;
}
// 레코드 출력
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>
|
Listing 2와 마찬가지로, Listing 3도 가장 먼저 특정한 피드 URL로 GET 요청을 보낸 후 응답을 SimpleXML 객체로 변환한다. 여기서는 여행(Travel) 범주에 해당하는 피드 URL을 사용한다. 그런 다음, <entry> 엘리먼트를 하나하나 찾아서 제목, 설명, 길이, 평가 점수 정보를 추출한 후 페이지에 순서대로 출력한다. Listing 3은 새로운 기능 하나가 더 있다. <openSearch:> 요소에서 요약 정보를 추출하여 현재 범주 이름과 현재 범주에 속하는 총 동영상 개수를 출력한다.
결과는 그림 2와 같다.
그림 2. 여행(Travel) 범주에 속하는 동영상 목록
유튜브가 사용하는 범주 목록은 http://gdata.youtube.com/schemas/2007/categories.cat 페이지를 참조한다. Listing 4는 유튜브 범주 목록을 보여주는 간단한 예다.
Listing 4. 유튜브 범주 목록 예
<?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>
|
이 파일을 가져와 분석한 후 유튜브 API를 사용해 각 범주에서 조회수가 가장 높은 동영상 다섯 개를 찾아보자. 코드는 별로 어렵지 않다. 자세한 내용은 Listing 5를 참조한다.
Listing 5. 각 범주에서 조회수가 가장 높은 동영상 다섯 개를 출력하는 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>Listing videos in different categories</title>
</head>
<body>
<?php
// 범주 목록을 포함하는 XML 피드를 위해 URL을 설정
$catURL = 'http://gdata.youtube.com/schemas/2007/categories.cat';
// atom: 이름 공간을 사용해 범주 목록 인출
// 주의: 성능 개선을 위해 이 목록을 캐시할 수 있다.
// 그다지 자주 변경되지 않기 때문이다.
$cxml = simplexml_load_file($catURL);
$cxml->registerXPathNamespace('atom', 'http://www.w3.org/2005/Atom');
$categories = $cxml->xpath('//atom:category');
// 범주 목록 순회
foreach ($categories as $c) {
// 각 범주 별
// 피드 URL 설정
$feedURL = "http://gdata.youtube.com/feeds/api/videos/-/{$c['term']}
?max-results=5&orderby=viewCount";
// 피드를 SimpleXML 객체에 읽어들임
$sxml = simplexml_load_file($feedURL);
// opensearch: 이름 공간으로 요약 횟수 얻기
$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";
// 범주에 속한 항목 순회
// 각 항목 세부 내역 출력
foreach ($sxml->entry as $entry) {
// 매체 정보를 위한 media: 이름 공간에서 노드 얻기
$media = $entry->children('http://search.yahoo.com/mrss/');
// 동영상 재생기 URL 얻기
$attrs = $media->group->player->attributes();
$watch = $attrs['url'];
// 동영상 길이를 얻기 위해 <yt:duration> 노드 탐색
$yt = $media->children('http://gdata.youtube.com/schemas/2007');
$attrs = $yt->duration->attributes();
$length = $attrs['seconds'];
// 동영상 평가 점수를 얻기 위해 <gd:rating> 노드 탐색
$gd = $entry->children('http://schemas.google.com/g/2005');
if ($gd->rating) {
$attrs = $gd->rating->attributes();
$rating = $attrs['average'];
} else {
$rating = 0;
}
// 레코드 출력
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>
|
Listing 5는 가장 먼저 http://gdata.youtube.com/schemas/2007/categories.cat 파일로 XML로 만들어진 범주 목록을 인출한다. 그러고 나서 SimpleXML의 xpath() 함수를 사용하여 유튜브 범주 이름을 모두 추출한다. 다음으로, foreach() 루프를 돌면서 각 범주에서 조회수가 가장 높은 동영상 목록을 가져온다. 각 범주를 위한 피드 URL을 루프 내에서 동적으로 생성한다. foreach() 루프 안에 있는 코드는 Listing 3과 거의 흡사하므로 파악이 어렵지 않다. 단, 피드 URL에 다음과 같은 매개변수 두 개를 더 추가한다.
max-results 매개변수는 피드가 반환할 결과 수를 지정한다.
orderby 매개변수는 결과가 정렬되는 기준을 지정한다. 조회수(viewCount), 언어(language), 평가 점수(rating), 게시일(published) 중 하나를 지정한다.
이제 그림 3을 살펴보자. Listing 5를 실행한 결과다.
그림 3. 다양한 범주에서 가져온 동영상 목록
키워드로 검색하기
유튜브에서는 사용자들이 동영상에 키워드 태그를 달아 동영상 내용을 좀 더 상세하게 분류한다. 따라서 키워드로 동영상을 검색하는 유튜브 API도 존재한다. API를 사용 방식은 범주 검색과 비슷하다. 예를 들어, 'boat'라는 태그가 달린 동영상을 검색하려면 http://gdata.youtube.com/feeds/api/videos/-/boat에 GET 요청을 보낸다. 비슷하게, 'westminster'나 'london'이라는 태그가 달린 동영상을 검색하려면 http://gdata.youtube.com/feeds/api/videos/-/westminster/london에 GET 요청을 보낸다. 두 피트 URL 모두 Listing 1과 비슷한 형식으로 응답을 보낸다. 즉, <feed> 엘리먼트가 <entry> 엘리먼트 여럿을 포함하고 각 <entry> 엘리먼트는 동영상 정보를 포함한다.
두 말할 필요 없이, 사용자가 키워드를 입력하면 유튜브 동영상을 찾아주는 간단한 PHP 검색 엔진을 구현하는 작업은 아주 쉽다. Listing 6은 이런 검색 엔진을 구현한 PHP 코드다.
Listing 6. 유튜브 동영상을 키워드로 검색하는 검색 엔진
<!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 (!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
// 폼을 제출했다면
} else {
// 검색 키워드를 점검한다.
// 공백을 잘라낸다.
// 다중 키워드는 /로 분리한다.
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));
}
// 최대 결과 설정
if (!isset($_POST['i']) || empty($_POST['i'])) {
$i = 25;
} else {
$i = $_POST['i'];
}
// 피드 URL 생성
$feedURL = "http://gdata.youtube.com/feeds/api/videos/-/{$q}
?orderby=viewCount&max-results={$i}";
// 피드를 SimpleXML 객체에 읽어들임
$sxml = simplexml_load_file($feedURL);
// opensearch: 이름 공간으로 요약 횟수 얻기
$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
// 결과 집합에서 항목 순회
// 각 항목 세부 내역 출력
foreach ($sxml->entry as $entry) {
// 매체 정보를 위한 media: 이름 공간에서 노드 얻기
$media = $entry->children('http://search.yahoo.com/mrss/');
// 동영상 재생기 URL 얻기
$attrs = $media->group->player->attributes();
$watch = $attrs['url'];
// 동영상 축소판 이미지 얻기
$attrs = $media->group->thumbnail[0]->attributes();
$thumbnail = $attrs['url'];
// 동영상 길이를 얻기 위해 <yt:duration> 노드 탐색
$yt = $media->children('http://gdata.youtube.com/schemas/2007');
$attrs = $yt->duration->attributes();
$length = $attrs['seconds'];
// 동영상 평가 점수를 얻기 위해 <gd:rating> 노드 탐색
$gd = $entry->children('http://schemas.google.com/g/2005');
if ($gd->rating) {
$attrs = $gd->rating->attributes();
$rating = $attrs['average'];
} else {
$rating = 0;
}
// 레코드 출력
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>
|
Listing 6은 두 부분으로 나뉜다. 하나는 키워드를 입력받는 폼이고, 다른 하나는 결과를 표시하는 페이지다. 첫 번째 부분은 아주 간단하다. 그림 4와 같이, 사용자가 키워드를 입력하고 결과 개수를 지정할 폼을 출력한다.
그림 4. 키워드 검색 폼
일단 사용자가 폼을 제출하면 스크립트는 입력한 키워드 개수가 한 개 이상인지를 점검한다. 사용자가 키워드를 입력하지 않은 채 폼을 제출했다면 스크립트는 오류 메시지를 출력하고 종료한다. 키워드가 있다면 유튜브 피드 URL에 적절히 첨부하여 유튜브 API로 REST 요청을 보낸다. 그런 다음, 유튜브 API가 반환하는 XML 문서를 SimpleXML 객체로 변환한다. 다음으로, 앞서 Listing 2, 3, 5에서 살펴본 방식으로, 동영상 정보를 추출하여 결과를 출력한다.
Listing 6에서는 두 가지를 눈여겨 살핀다.
- 사용자가 검색 폼에 입력한 키워드는 적절히 형식을 변경하여 피드 URL 뒤에 첨부된다. 가장 먼저,
trim() 함수를 호출하여 문자열 앞뒤에서 공백을 제거한다. 그런 다음, ereg_replace() 함수를 사용하여 문자열 사이에 있는 공백을 슬래시(/)로 교체한다.
- Listing 6은 키워드로 검색한 결과 동영상 수를 출력한다. 그뿐만 아니라, <openSearch:startIndex> 엘리먼트와 <openSearch:itemsPerPage> 엘리먼트에서 현재 출력하는 페이지의 시작 색인과 끝 색인을 추출해 출력한다.
그림 5는 Listing 6이 생성한 검색 결과 페이지 예다.
그림 5. 검색 결과 페이지
기타 검색 매개변수 사용하기
다음 매개변수를 활용하면 좀 더 정확한 검색을 수행할 수 있다.
- 매개변수
start-index: 피드에 시작 색인을 지정한다.
- 매개변수
vq: URL로 인코딩된 검색어를 지정한다.
- 매개변수
format: 특정한 형식의 동영상만 반환한다.
- 매개변수
lr: 특정한 형식의 동영상만 반환한다.
- 매개변수
time: 특정한 기간에 게시된 동영상만 반환한다(일부 표준 피드에서만 사용이 가능하다).
Listing 7은 Listing 6을 개선한 검색 엔진이다. 매개변수 몇 개를 추가했으며, PEAR에 있는 Pager 클래스를 사용하여 페이지를 관리한다.
Listing 7. 유튜브 동영상을 키워드로 검색하는 검색 엔진(개선)
<!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 (!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
// 폼을 제출했다면
} else {
// 검색 키워드를 점검한다.
// 공백을 잘라낸다.
// 다중 키워드는 /로 분리한다.
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);
}
// 페이지당 최대 결과를 설정
if (!isset($_GET['i']) || empty($_GET['i'])) {
$i = 25;
} else {
$i = htmlentities($_GET['i']);
}
// 정렬 순서 설정
if (!isset($_GET['s']) || empty($_GET['s'])) {
$s = 'viewCount';
} else {
$s = htmlentities($_GET['s']);
}
// 색인 시작 지점 설정
if (!isset($_GET['pageID']) || $_GET['pageID'] <= 0) {
$o = 1;
} else {
$pageID = htmlentities($_GET['pageID']);
$o = (($pageID-1) * $i)+1;
}
// 피드 URL 생성
$feedURL = "http://gdata.youtube.com/feeds/api/videos
?vq={$vq}&orderby={$s}&max-results={$i}&start-index={$o}";
// 피드를 SimpleXML 객체에 읽어들임
$sxml = simplexml_load_file($feedURL);
// opensearch: 이름 공간으로 요약 횟수 얻기
$counts = $sxml->children('http://a9.com/-/spec/opensearchrss/1.0/');
$total = $counts->totalResults;
$startOffset = $counts->startIndex;
$endOffset = ($startOffset-1) + $counts->itemsPerPage;
// Pager 클래스 인클루드
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
// 결과 집합에서 항목 순회
// 각 항목 세부 내역 출력
foreach ($sxml->entry as $entry) {
// 매체 정보를 위한 media: 이름 공간에서 노드 얻기
$media = $entry->children('http://search.yahoo.com/mrss/');
// 동영상 재생기 URL 얻기
$attrs = $media->group->player->attributes();
$watch = $attrs['url'];
// 동영상 축소판 이미지 얻기
$attrs = $media->group->thumbnail[0]->attributes();
$thumbnail = $attrs['url'];
// 동영상 길이를 얻기 위해 <yt:duration> 노드 탐색
$yt = $media->children('http://gdata.youtube.com/schemas/2007');
$attrs = $yt->duration->attributes();
$length = $attrs['seconds'];
// 접근 횟수 통계를 얻기 위해 <yt:stats> 노드 탐색
$yt = $entry->children('http://gdata.youtube.com/schemas/2007');
$attrs = $yt->statistics->attributes();
$viewCount = $attrs['viewCount'];
// 동영상 평가 점수를 얻기 위해 <gd:rating> 노드 탐색
$gd = $entry->children('http://schemas.google.com/g/2005');
if ($gd->rating) {
$attrs = $gd->rating->attributes();
$rating = $attrs['average'];
} else {
$rating = 0;
}
// 비디오 ID 얻기
$arr = explode('/',$entry->id);
$id = $arr[count($arr)-1];
// 레코드 출력
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. 개선한 검색 폼
사용자가 폼을 제출하면 스크립트는 매개변수를 적절하게 피드 URL에 첨부한 후 XML 결과 집합을 처리하여 표시한다. 페이지 관리는 PEAR에 있는 Pager 클래스가 맡는다. Pager 클래스는 <openSearch:totalResults> 엘리먼트 값과 $_GET['pageID'] 변수를 사용하여 이전 페이지 링크와 다음 페이지 링크를 자동으로 생성한다. 사용자가 페이지 링크를 클릭하면 스크립트는 $_GET['pageID'] 변수 값으로 시작 색인을 다시 계산한 후 피드 URL에 추가하여 새로운 결과를 가져온다.
그림 7은 페이지 링크를 추가한 검색 결과다.
그림 7. 검색 결과 페이지
위 그림에서 More Information 링크에 주목한다. 이 링크는 다른 PHP 스크립트를 가리키는데, 링크를 클릭하면 해당 동영상에 대한 정보(덧글, 동영상 응답, 작성자 프로필, 관련 동영상 등)가 표시된다. 이 정보를 획득하는 구체적인 방법은 다음 절에서 다룬다.
덧글, 동영상 응답, 관련 동영상 가져오기
Listing 1에서 <entry> 엘리먼트 하나를 자세히 살펴보면, 각 <entry> 엘리먼트는 (동영상 메타데이터 외에도) 덧글이나 동영상 응답이나 관련 동영상을 가져오는 피드 링크를 포함한다. 각각을 좀 더 자세하게 들여다보자.
덧글은 다른 유튜브 사용자들이 동영상에 단 의견이다. 덧글을 가져오려면 덧글 피드를 사용한다. 덧글 피드는 <gd:comments> 엘리먼트에 있다. 다음 예를 참조한다.
<gd:comments>
<gd:feedLink href='http://gdata.youtube.com/feeds/api/videos/dMH0bHeiRNg/comments'
countHint='124130'/>
</gd:comments>
|
위 덧글 피드 URL에 GET 요청을 보내면 새로운 XML 형식이 반환된다. Listing 8은 덧글 피드 URL이 반환한 XML 정보로, 사용자가 동영상에 추가한 덧글을 모두 포함한다.
Listing 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>
|
동영상 응답은 다른 유튜브 사용자들이 올린 동영상 답변이다. 동영상 응답을 가져오려면 <link rel='http://gdata.youtube.com/schemas/2007#video.responses' ...> 엘리먼트에 있는 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'/>
|
마찬가지로, 관련 동영상을 가져오려면 <link rel='http://gdata.youtube.com/schemas/2007#video.related' ...> 엘리먼트에 있는 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'/>
|
이제 필요한 피드 URL을 모두 파악했으니, 동영상 정보를 상세히 출력하는 페이지를 만들어보자. Listing 9에서 보듯이, 별로 어렵지 않다.
Listing 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
// 동영상 <entry>를 해석하는 함수
function parseVideoEntry($entry) {
$obj= new stdClass;
// 매체 정보를 위한 media: 이름 공간에서 노드 얻기
$media = $entry->children('http://search.yahoo.com/mrss/');
$obj->title = $media->group->title;
$obj->description = $media->group->description;
// 동영상 재생기 URL 얻기
$attrs = $media->group->player->attributes();
$obj->watchURL = $attrs['url'];
// 동영상 축소판 이미지 얻기
$attrs = $media->group->thumbnail[0]->attributes();
$obj->thumbnailURL = $attrs['url'];
// 동영상 길이를 얻기 위해 <yt:duration> 노드 탐색
$yt = $media->children('http://gdata.youtube.com/schemas/2007');
$attrs = $yt->duration->attributes();
$obj->length = $attrs['seconds'];
// 접근 횟수 통계를 얻기 위해 <yt:stats> 노드 탐색
$yt = $entry->children('http://gdata.youtube.com/schemas/2007');
$attrs = $yt->statistics->attributes();
$obj->viewCount = $attrs['viewCount'];
// 동영상 평가 점수를 얻기 위해 <gd:rating> 노드 탐색
$gd = $entry->children('http://schemas.google.com/g/2005');
if ($gd->rating) {
$attrs = $gd->rating->attributes();
$obj->rating = $attrs['average'];
} else {
$obj->rating = 0;
}
// 동영상 덧글을 얻기 위해 <gd: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'];
}
// 동영상 반응을 위한 피드 URL 얻기
$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'];
}
// 연관된 동영상을 위한 피드 URL 얻기
$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 $obj;
}
// $_GET에서 동영상 ID 얻기
if (!isset($_GET['id'])) {
die ('ERROR: Missing video ID');
} else {
$vid = $_GET['id'];
}
// 동영상 자료 피드 URL 설정
$feedURL = 'http://gdata.youtube.com/feeds/api/videos/' . $vid;
// 피드를 SimpleXML 객체에 읽어들임
$entry = simplexml_load_file($feedURL);
// 동영상 항목 해석
$video = parseVideoEntry($entry);
// 주 동영상 레코드 출력
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";
// '동영상 덧글' 피드를 SimpleXML 객체에 읽어들임
// 각 덧글을 해석해 출력
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";
}
// '동영상 반응' 피드를 SimpleXML 객체에 읽어들임
// 각 반응을 해석해 출력
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";
}
}
// '연관된 동영상' 피드를 SimpleXML 객체에 읽어들임
// 각 동영상 항목을 해석해 출력
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>
|
Listing 9는 언뜻 보기에는 복잡해 보이지만, 사실은 아주 간단하다. 우선, parseVideoEntry() 함수를 정의하는데, 이 함수는 SimpleXML <entry> 객체를 입력으로 받아 (앞서 Listing 2, 3, 6에서 살펴본 방식으로) 정보를 추출하여 동영상의 핵심 속성에 대응하는 stdClass 객체 속성에 저장한 후 stdClass 객체 자체를 반환한다. 이렇게 논리를 캡슐화(encapsulation)하면 코드를 이해하기가 쉬워지고 재사용하기도 편해진다. 또한 스크립트 자체를 이해하기도 쉬워진다.
이제 실제 코드를 살펴보자. 가장 먼저, 동영상 피드 URL에 동영상 ID를 키로 첨부하여 GET 요청을 보낸다. (여기서 동영상 ID는 Listing 7에서 More Information 링크를 클릭하면 $_GET 배열로 전달된다.) 동영상 피드 URL이 반환한 <entry> 엘리먼트 결과를 SimpleXML 객체로 변환한 후 parseVideoEntry() 함수로 넘긴다. 그러면 parseVideoEntry() 함수는 상세한 동영상 정보가 담긴 stdClass 객체를 반환한다.
이것으로 끝이 아니다. parseVideoEntry()는 (축소판 이미지, 제목, 설명 등) 기본적인 동영상 정보 외에 덧글 피드 URL, 동영상 응답 피드 URL, 관련 동영상 피드 URL도 추출한다. 스크립트는 각 피드 URL에 GET 요청을 보낸 후 결과 <entry> 요소를 분석하여 (동영상 응답과 관련 동영상의 덧글, 축소판 이미지, 메타데이터 등) 다양한 정보를 결과 페이지에 출력한다. 예는 그림 8을 참조한다.
그림 8. 덧글, 동영상 응답, 관련 동영상을 표시하는 웹 페이지
작성자 프로필 가져오기
유튜브 API를 사용하면 동영상을 올린 작성자 정보도 가져올 수 있다. Listing 1을 자세히 살펴보면 <author> 엘리먼트가 있다. 이 엘리먼트는 동영상 소유자의 사용자 이름과 프로필 피드 URL을 포함한다. 다음 예를 참조한다.
<author>
<name>judsonlaipply</name>
<uri>http://gdata.youtube.com/feeds/api/users/judsonlaipply</uri>
</author>
|
위 피드 URL에 GET 요청을 보내면 Listing 10과 같은 XML이 반환된다.
Listing 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>
|
위에서 보듯이, Listing 10은 (사용자 이름, 위치, 나이, 성별 등) 간단한 사용자 프로필과 더불어 사용자가 즐겨찾는 동영상 피드 URL, 가입한 피드 URL, 친구들 피드, URL, (인증이 필요한) 메시지 상자 피드 URL, 사용자가 올린 동영상 목록 피드 URL을 제공한다. 그러므로 Listing 10에서 정보를 추출하여 Listing 9 결과 페이지에 추가하기는 아주 쉽다. 코드는 Listing 11을 참고한다.
Listing 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
// 동영상 <entry>를 해석하는 함수
function parseVideoEntry($entry) {
$obj= new stdClass;
// 작성자 이름과 피드 URL 얻기
$obj->author = $entry->author->name;
$obj->authorURL = $entry->author->uri;
// 매체 정보를 위한 media: 이름 공간에서 노드 얻기
$media = $entry->children('http://search.yahoo.com/mrss/');
$obj->title = $media->group->title;
$obj->description = $media->group->description;
// 동영상 재생기 URL 얻기
$attrs = $media->group->player->attributes();
$obj->watchURL = $attrs['url'];
// 동영상 축소판 이미지 얻기
$attrs = $media->group->thumbnail[0]->attributes();
$obj->thumbnailURL = $attrs['url'];
// 동영상 길이를 얻기 위해 <yt:duration> 노드 탐색
$yt = $media->children('http://gdata.youtube.com/schemas/2007');
$attrs = $yt->duration->attributes();
$obj->length = $attrs['seconds'];
// 접근 횟수 통계를 얻기 위해 <yt:stats> 노드 탐색
$yt = $entry->children('http://gdata.youtube.com/schemas/2007');
$attrs = $yt->statistics->attributes();
$obj->viewCount = $attrs['viewCount'];
// 동영상 평가 점수를 얻기 위해 <gd:rating> 노드 탐색
$gd = $entry->children('http://schemas.google.com/g/2005');
if ($gd->rating) {
$attrs = $gd->rating->attributes();
$obj->rating = $attrs['average'];
} else {
$obj->rating = 0;
}
// 동영상 덧글을 얻기 위해 <gd: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'];
}
// 동영상 반응을 위한 피드 URL 얻기
$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'];
}
// 연관된 동영상을 위한 피드 URL 얻기
$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 $obj;
}
// $_GET에서 동영상 ID 얻기
if (!isset($_GET['id'])) {
die ('ERROR: Missing video ID');
} else {
$vid = $_GET['id'];
}
// 동영상 자료 피드 URL 설정
$feedURL = 'http://gdata.youtube.com/feeds/api/videos/' . $vid;
// 피드를 SimpleXML 객체에 읽어들임
$entry = simplexml_load_file($feedURL);
// 동영상 항목 해석
$video = parseVideoEntry($entry);
// 주 동영상 레코드 출력
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";
// '작성자 프로필' 피드를 SimpleXML 객체에 읽어들임
// 작성자 프로필을 해석해 출력
$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";
// '동영상 덧글' 피드를 SimpleXML 객체에 읽어들임
// 각 덧글을 해석해 출력
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";
}
// '동영상 반응' 피드를 SimpleXML 객체에 읽어들임
// 각 반응을 해석해 출력
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";
}
}
// '연관된 동영상' 피드를 SimpleXML 객체에 읽어들임
// 각 동영상 항목을 해석해 출력
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>
|
Listing 11에서는 parseVideoEntry() 함수를 변경하여 stdClass 객체에 작성자 이름과 작성자 피드 URL을 추가했다. 스크립트 내에서도 이 피드 URL에 GET 요청을 보낸 후 응답에서 동영상 소유자 이름과 나이와 위치와 성별을 추출하여 페이지에 출력한다. 위 스크립트에서는 사용자가 즐겨찾는 동영상이나 친구 정보와 구독 정보를 가져오지 않지만, <author> 엘리먼트 안에 있는 <link> 엘리먼트를 사용하면 구현하기가 어렵지 않다.
Listing 11이 출력하는 페이지는 그림 9와 같다.
그림 9. 작성자 정보를 출력하는 웹 페이지
요약
지금까지 PHP 응용 프로그램에서 SimpleXML과 유튜브 API를 이용하여 동영상 정보를 가져오는 방법을 살펴보았다. 이 기사에서 살펴본 내용은 다음과 같다.
- 유튜브 피드 소개
- 범주나 키워드로 동영상 가져오기
- 동영상 메타데이터 추출하기
- 덧글, 동영상 응답, 관련 동영상, 작성자 프로필 등 동영상 정보 가져오기
위 예제에서 살펴보았듯이, 유튜브 REST API는 개발자들에게 상당한 유연성과 자유를 제공하므로 새로운 응용 프로그램을 개발할 기회와 가능성이 풍부하다. 특정 유튜브 공동체가 사용할 인터페이스를 구현하거나 유튜브 자료를 다른 웹 사이트 자료와 섞어(mash-up) 새로운 서비스를 만들 때 편리하다.
다운로드 하십시오 | 설명 | 이름 | 크기 | 다운로드 방식 |
|---|
| 이 글의 코드 예제 | x-youtubeapi-code.zip | 8KB | HTTP |
|---|
참고자료 교육
제품 및 기술 얻기
토론
필자소개
기사에 대한 평가
 |
| 이 문서 북마킹 하기
|
|  |