gen.
13
2010

Portades de llibres

Actualment estic treballant per la Xarxa de Biblioteques Públiques de la província de Barcelona. Una de les coses divertides que he fet és muntar una aplicació que busca les portades dels llibres en el nostre repositoris de portades i després en altres repositoris externs gratuïts.

L’aplicació te una ordre de prioritats i va saltant de repositori en repositori fins que troba la portada del llibre.

A més te un sistema de cache que guarda les portades durant un cert temps i s’estalvia de fer tota la cerca per tota la xarxa cada cop que algú demana una portada.

Per si algú li és útil a continuació explicaré com trobar les portades de llibres en diferents mides a partir del ISBN (codi del llibre) amb PHP en els següents repositoris externs gratuïts.

1. Amazon
2. Library Thing
3. Open Library
4. Google Books

Nota: Tots els scripts que escric són versions simplificades i tenen un propòsit instructiu. No em faig responsable de la seva utilització en un lloc real tot i que, en teoria, haurien de funcionar correctament xD

Amazon

El repositori de Amazon és el millor i força senzill. Per aconseguir la portada d’un llibre simplement has de construir una URL de la següent forma.


//ISBN exemple
$isbn=’1555705189’;

//Imatge petita, uns 50 x 75px
$url["petita"]='http://images.amazon.com/images/P/'.$isbn.'01.THUMBZZZ';
//Imatge mitjana, uns 110 x 160px
$url["mitjana"]='http://images.amazon.com/images/P/'.$isbn.'01.MZZZZZZ';
//Imatge gran, uns 320 x 500 px
$url["gran"]='http://images.amazon.com/images/P/'.$isbn.'.01.LZZZZZZ';

El que nosaltres fem, com que tenim el sistema de cache, es agafar la imatge més gran el primer cop, utilitzant la llibreria GD2 per generar les imatges derivades en les mides que necessitem, guardar-les en el nostre servidor i després anar servint les derivades de la cache del servidor en la mida adequada.

Per als repositoris extern tenim configurada la cache amb una vida de una setmana, així que si Amazon canvia una portada triguem una setmana en enterar-nos però podem servir imatges molt ràpidament perquè no hem d’esperar a que Amazon ens torni la portada cada cop que algú la vol veure.

Què passa si Amazon no te la portada del llibre que els demanem?

Doncs Amazon ens retorna un gif transparent 1×1 píxel com a portada.

Evidentment el que volem es detectar-ho, descartar la imatge i saltar al següent repositori per seguir buscant la portada en altres llocs.

Hi ha moltes maneres de fer això però una de les mes ràpides es connectar-nos utilitzant Curl i llegir la capçalera de la petició HTTP sense ni tan sols baixar-nos la imatge.

Podríem fer la petició HTTP, rebre la resposta i mirar la mida de la imatge però seria una mica mes lent.

La gestió de peticions HTTP la tenim plenament desenvolupada en cUrl així que podem treure partit d’aquesta llibreria per fer això i per altres altres coses com: com simular la connexió des d’un navegador per evitar que el repositori extern ens bloquegi o controlar millor el timeout i evitar que una caiguda dels servidors de Amazon o certs problemes en la xarxa provoquin lentituds en la nostre aplicació de portades.

La nostre solució per assegurar un servei relativament ràpid i constant es descartar totes les imatges mes petites de 1000 bytes (per eliminar els gifs 1×1 transparents d’imatge no trobada y les imatges utra-petites en mala qualitat) i descartar també les peticions HTTP que porten més d’un segon i encara no s’han resolt per impedir que problemes de lentitud en repositoris externs ens afectin a nosaltres excessivament… i tota aquesta validació la fem sense baixar-nos realment la imatge, només demanant la capçalera de la petició HTTP a l’Apache.

A mode d’exemple, podríem construint una funció de validació de la imatge via cUrl i retorna true o false segons si troba la imatge, aquesta es adequada i el servidor extern respon en un temps acceptable (menys d’un segon).


function validaImatge($url)
{
$check=false;
$ch = curl_init($url);
//per rendiment no necessitem descarregar el cos
curl_setopt($ch, CURLOPT_NOBODY, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
//Volem que ens torni la capçalera
curl_setopt($ch, CURLOPT_HEADER, 1);
//Volem que salti si hi ha redireccions
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
//Timeout de 1 segon
curl_setopt($ch, CURLOPT_TIMEOUT, 1);
//Simulem una connexió des d’una navegador per evitar bloquejos
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 5.1; es-ES; rv:1.9.1.4) Gecko/20091016 Firefox/3.5.4");
$data = curl_exec($ch);

if ($data !== false) {
$info = curl_getinfo($ch);
if ($info[http_code]==200 && $info[download_content_length]>1000)
{
$check=true;
}
}
curl_close($ch);
return $check;
}

Problemes d’Amazon, els ISBNs?

Amazon te alguns problemes coneguts amb la cerca per ISBN.

Per exemple, els llibres tenen dos sistemes de codis, l’antic el ISBN de 10 dígits i el nou, el ISBN de 13 dígits.

En teoria tot ISBN de 10 dígits te el seu equivalent de 13 dígits (al revés no), pel que qualsevol repositori hauria de ser capaç de trobar la portada d’un llibre tant si li poses el ISBN 10 dígits com el 13 dígits, però mentre que amb alguns repositoris com el Google Books sí que funciona així amb Amazon no.

Quan cerques la portada d’un llibre amb el ISBN de 13 dígits molts cops no troba la portada, i en canvi si proves el seu ISBN equivalent de 10 dígits si que et torna una portada.

Així que… com solucionem aquesta limitació d’Amazon? Doncs no ens queda més remei que cercar la portada del llibre amb el ISBN que tinguem i si aquest és el de 13 dígits i no ha trobat la portada calcular el seu ISBN equivalent de 10 dígits i buscar-la a mirar si amb aquest tenim sort.

Calcular el ISBN de 10 dígits a partir del de 13 dígits podem fer servir quelcom tan senzill com això:


//ISBN exemple 13 dígits
$isbn='9780739360385';
echo "ISBN: $isbn ";

//Amazon, no troba ISBN10 i ISBN13 indistintament i cal cercar portades
//Si es un ISBN-13 amb equivalent a ISBN-10 el calculem
if (strlen($isbn)==13 && substr($isbn,0,3)=='978' && is_numeric(substr($isbn,3,9)))
{
$isbn10=substr($isbn,3,9);
for ($i = 0; $i < 9; $i++) { $sum=$sum+substr($isbn10,$i,1)*$i;
}
$numcontrol=$sum % 10;
if ($numcontrol==10){$numcontrol='x';}
$isbn10=$isbn10.$numcontrol;
echo " ISBN-10 $isbn10";
}

Així podem afegir en les rutines de la nostre aplicació que quan es tracti d’Amazon quan busqui amb el ISBN13 sinó troba la portada del llibre busqui també amb el ISBN10 a veure si tenim sort.

Library Thing

Aquesta gent tenen una API que funciona igual que el servei de Amazon però t’has de registrar (http://www.librarything.es/) i posar el teu codi d’usuari en la URL.

A més tenen una limitació en el nombre d’imatges que et serveixen per usuari (100 imatges per dia), en aquest moment la cache resultarà molt útil! Millor poder agafar 1000 imatges diferents cada dia que no gastar la teva quota servint 1000 vegades la mateixa imatge!


//ISBN exemple
$isbn=’1555705189’;
//La teva clau d’usuari de la Library thing
$key=’************************’;

//Imatge petita
$url["petita"]= 'http://covers.librarything.com/devkey/'.$key.'/small/isbn/'.$idisbn;
//Imatge mitjana
$url["mitjana"]='http://covers.librarything.com/devkey/'.$key.'/medium/isbn/'.$idisbn;
//Imatge gran
$url["gran"]='http://covers.librarything.com/devkey/'.$key.'/large/isbn/'.$idisbn;

Evidentment, si volguéssim la mateixa funció d’abans de validaImatge() també funcionaria en aquest cas.

En cas de no trobar una portada fan el mateix que Amazon, tornar-te un gif transparent de 1×1 píxel.

Problemes de la Library Thing, errors d’idioma?

Aquest repositori de portades esta construït pels usuaris així que podem patir alguns errors d’usuaris que pugen portades equivocades, etc… al tractar-se d’una comunitat nosaltres mateixos podem contribuir-hi i intentar esmenar els errors/problemes.

A nosaltres el problema que mes ens ha afectat són les portades d’edicions equivocades. Es força comú que busquis la portada d’un llibre de l’edició en català o castellà i et retorni la portada de l’edició anglesa i com que tenim bibliotecaris molt exigents aquest tipus de problemes han provocat que al final desactivéssim aquest repositori, una llàstima perquè realment tenen moltes portades.

Open Library

La Open Library es un altre projecte obert de comunitat d’usuaris molt semblant al de la Library Thing.

La seva API funciona de forma molt semblant i quan no troba una portada, enlloc de retornar-te un píxel transparent simplement no et retorna res.


//ISBN exemple
$isbn='1555705189';

$url["petita"]='http://covers.openlibrary.org/b/isbn/'.$isbn.'-S.jpg';
$url["mitjana"]='http://covers.openlibrary.org/b/isbn/'.$isbn.'-M.jpg';
$url["gran"]='http://covers.openlibrary.org/b/isbn/'.$isbn.'-L.jpg';

Problemes de la Open Library, la petició http?

A més, aquest repositori te una altre diferència amb la resta. No sé si te a veure amb el fet que utilitza Lighthttpd enlloc d’Apache però resulta que no retorna la mida del fitxer en la capçalera de la petició HTTP.

Això pot ser que no t’afecti en res segons la teva implementació o pot ser un problema si vols utilitzar una funció exactament com la funció de validaImatge() que he posat anteriorment perquè en aquest cas sí que necessites descarregar el cos de la petició HTTP per saber si el que t’està tornant te les característiques que t’interesen o no.

En aquest cas hauríem d’eliminar el paràmetres de la connexió d’abans:

curl_setopt($ch, CURLOPT_NOBODY, 1);

Per especificar-li que sí que volem el cos de la petició http, i després, si volem midar la miad, enlloc de comprovar el tamany del fitxer que ens anuncien en la capçalera $info[download_content_length] hauríem de mirar la mida del cos de la petició http descarregada $info[size_download].

Per exemple, podríem arreglar la nostre funció anterior de validació de la imatge perquè funciones sempre descarregant tot el body i anés correctament amb qualsevol d’aquests tres repositoris (renunciant a una certa millora del rendiment en el cas de Amazon i Library Thing):


function validaImatge($url)
{
$check=false;
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 1);
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 5.1; es-ES; rv:1.9.1.4) Gecko/20091016 Firefox/3.5.4");
$data = curl_exec($ch);

if ($data !== false) {
$info = curl_getinfo($ch);
if ($info[http_code]==200 && $info[size_download]>1000)
{
$check=true;
}
}
curl_close($ch);
return $check;
}

o enlloc de passar-li només la URL li podem donar informació a la nostre funció sobre el repositori i obtenir el millor dels dos mons. Fer la comprovació ràpida de la capçalera per Amazon y la Library Thing i la comprovació un pel mes lenta del cos de la petició per la Open Library:


//Valida que es recupera una imatge correcta en aquella URL
function validaDades($url,$repo) {
$check=false;
$ch = curl_init($url);
//Tots els repositoris menys la open library tornen el tamany del fitxer en el header
//per rendiment no necessitem descarregar el cos en alguns casos
if ($repo!='olib'){
curl_setopt($ch, CURLOPT_NOBODY, 1);
}
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 5.1; es-ES; rv:1.9.1.4) Gecko/20091016 Firefox/3.5.4");
$data = curl_exec($ch);

if ($data !== false) {
$info = curl_getinfo($ch);
if ($info[http_code]==200 && ($info[download_content_length]>1000 || $info[size_download]>1000)) {
$check=true;
}
}
curl_close($ch);
return $check;
}

Problemes de la Open Library, imatges indegudes?

Un altre problema greu de la Open Library es el mal us que en fan alguns usuaris. Sembla que hi ha alguns usuaris malintencionats que es dediquen a penjar imatges inadequades amb insults, xenòfoges, etc. Suposo que per fer alguna gracieta.

A diferència de la Library Thing o d’altres comunitats virtuals aquí ni tan sols necessites estar registrat per contribuir amb una nova portada.

El resultat final és que els vàndals ho tenen molt fàcil per fer les seves gracietes. En el moment en que es detecta una imatge indeguda es esborrada amb una certa celeritat però sempre corres un cert risc que un usuari busqui un llibre i aparegui alguna imatge indeguda.

Google Books

Aquest potser es el repositori mes complicat d’implementar amb PHP. Tenen una API senzilla pensada per funcionar amb Javascript així que si vols treballar amb PHP has de fer certs malabarismes.

La seva alternativa es una llibreria per PHP per accedir a la seva informació carregant una classe en PHP anomenada Zend_Gdata_Books (http://code.google.com/intl/es-ES/apis/books/docs/gdata/developers_guide_php.html)

Jo us explicaré com aconseguir la portada del llibre de Googl Books a través del seu WebService de RSS.

No és la forma recomanada ni suportada per Google però pot ser una alternativa si no et vols instal·lar totes les seves llibreries i el seu framework.

La idea consisteix en buscar el llibre a través dels seus webservices i després mirar quina URL te la portada, i treballar una mica aquesta URL per obtenir les diferents mides de la portada.

La URL en qüestió on hi ha la informació del llibre en XML és:


//ISBN exemple
$isbn='9780739360385';

$url1='http://books.google.com/books/feeds/volumes?q=isbn:'.$isbn;

Aquí podem parcejar el XML per transformar-ho en un array o en un objecte o podem anar a saco i utilitzar expressions regulars per trobar la URL de la imatge de la portada del llibre si aquesta existeix (que es el que faré aquí sota).

No es un script elegant però hauria de funcionar:


//ISBN exemple
$isbn='9780739360385';

//Url del feed de Google
$data='http://books.google.com/books/feeds/volumes?q=isbn:'.$isbn;

//Recuperem el XML
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $data);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 5.1; es-ES; rv:1.9.1.4) Gecko/20091016 Firefox/3.5.4");
$data = curl_exec($ch);
curl_close($ch);

if ($data !== false) {
//Busquem la imatge derivada
preg_match("/
]*thumbnail[^>]*>/",$data,$match1);
preg_match("/href='[^']*'/",$match1[0],$match2);
$origen=array('href=','\'','&');
$desti=array('','','&');
$data = str_replace($origen,$desti,$match2[0]);
if (strlen(trim($data))>0){
echo "Portada petita: <img src="'$data'" alt="" />";
$data = str_replace('zoom=5','zoom=1',$data);
echo "Portada mitjana: <img src="'$data'" alt="" />";
$data] = str_replace('zoom=5','',$data);
echo "Portada gran: <img src="'$data'" alt="" />";
} else {
echo "Error: No te imatge de portada";
}
} else {
echo "Error: URL incorrecte o llibre no trobat";
}

A partir d’aquí podem fer moltíssimes coses, per exemple, un cercador de portades de llibre a partir del ISBN seria tan senzill com, per exemple, fer:


//ISBN
$isbn='1555705189';
//Clau d’usuari de la Library Thing
$key='***********';

//Amazon
$repo[0][0]='Amazon';
$repo[0][1]='http://images.amazon.com/images/P/'.$isbn.'.01.THUMBZZZ';
$repo[0][2]='http://images.amazon.com/images/P/'.$isbn.'.01.MZZZZZZ';
$repo[0][3]='http://images.amazon.com/images/P/'.$isbn.'.01.LZZZZZZ';

//Open Library
$repo[1][0]='Open Library';
$repo[1][1]='http://covers.openlibrary.org/b/isbn/'.$isbn.'-S.jpg';
$repo[1][2]='http://covers.openlibrary.org/b/isbn/'.$isbn.'-M.jpg';
$repo[1][3]='http://covers.openlibrary.org/b/isbn/'.$isbn.'-L.jpg';

//Library Thing
$repo[2][0]='Library Thing';
$repo[2][1]='http://covers.librarything.com/devkey/'.$key.'/small/isbn/'.$isbn;
$repo[2][2]='http://covers.librarything.com/devkey/'.$key.'/medium/isbn/'.$isbn;
$repo[2][3]='http://covers.librarything.com/devkey/'.$key.'/large/isbn/'.$isbn;

//Google Books
$repo[3][0]='Google Books';
$data='http://books.google.com/books/feeds/volumes?q=isbn:'.$isbn;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $data);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 5.1; es-ES; rv:1.9.1.4) Gecko/20091016 Firefox/3.5.4");
$data = curl_exec($ch);
curl_close($ch);

if ($data !== false) {
preg_match("/
]*thumbnail[^>]*>/",$data,$match1);
preg_match("/href='[^']*'/",$match1[0],$match2);
$origen=array('href=','\'','&');
$desti=array('','','&');
$data = str_replace($origen,$desti,$match2[0]);

if (strlen(trim($data))>0) {
$repo[3][1] = $data;
$repo[3][2] = str_replace('zoom=5','zoom=1',$data);
$repo[3][3] = str_replace('zoom=5','',$data);
}
}

//Genera el HTML amb totes les imatges
//Bucle per cada repositori
if (isset($repo)){
foreach ($repo as $control) {
//Bucle per cada element del repositori
$titol=true;
foreach ($control as $item) {
if ($titol) {
$contingut .='<h2>'.$item.'</h2>';
$titol=false;
} else {
$contingut .= '<img src="'.$item.'" alt="" /> ';
}
}
$contingut .= '<br>';
}
}

echo '<html><body>'.$contingut.'</body></html>';

I avui ja n’hi ha prou de parlar del món dels repositoris gratuïts de portades de llibres 😀

Written by in: General |

No hi ha comentaris »

RSS feed for comments on this post. TrackBack URL


Leave a Reply

Powered by WordPress | Theme: Aeros 2.0 by TheBuckmaker.com

Aneu a la barra d'eines