mercredi 26 décembre 2007
Transformer un site web en un livre
Par Puyb, mercredi 26 décembre 2007 à 13:46 :: General
Le livre sur Django rédigé de manière collaborative entre les auteurs de Django et les internautes est disponible en ligne sous la licence GNU Free Documentation. Il est publié en ligne sous forme d'une série de pages HTML... Pas très pratique pour le lire tranquillement dans le canapé... J'ai donc décidé de l'imprimer...
J'aurais pu me contenter de l'imprimer en utilisant la fonction imprimer de mon butineur favoris, mais :
J'ai décider d'utiliser les fonctions du CSS pour faire quelque chose de potable... Le CSS, on l'utilise souvent pour afficher des choses à l'écran, mais on l'utilise rarement pour imprimer. Pourtant, dans sa version 3 (qui n'est malheureusement pas assez supporté par les butineurs), on peut avoir un contrôle assez précis de ce que l'on imprime.
On peut par exemple gérer les entêtes et pieds de pages, les pages recto/verso, les sauts de page, la numérotation des chapitres et figures, les notes en pieds de page, etc...
Mais si les navigateurs ne supporte pas comme il faut le CSS 3, quel est l'intérêt de tout ça... Et bien, il existe un outils (propriétaire, mais gratuit pour un usage personnel) nommé prince, qui permet de générer un PDF à partir d'un fichier HTML et qui comprend correctement le CSS 3... Parfait, donc à l'attaque...
En premier lieu, je me suis fait un scripts python pour extraire le contenu des différents chapitres du livre depuis le site djangobook.com. Ce script utilise le module BeautifulSoup, qui permet une manipulation aisé des fichiers HTML (un peu à la manières des fonctions de manipulation du DOM en javascript). Le script récupère successivement toutes les pages, extrait le contenu du div ayant la classe "yui-b" et aglomère le tout dans un fichier res.html.
Le fichier ainsi généré est loin d'être conforme xhtml, c'est pour cela qu'un deuxième script se charge de le rendre un peu plus conforme, mais aussi de générer les liens correspondant à la table des matières.
A partir de là, j'obtiens un fichier html à peu près correct (il y a encore plein de balises avec des id identiques...). Je peux donc y appliquer un CSS pour l'imprimer. Je suis parti du fichier CSS donner dans l'excellent article sur A List Appart. J'ai modifier la taille des pages, les marges, et la façon d'afficher les illustrations, les légendes ou les notes. Au bout d'une heure de tatonement, je suis parvenu à obtenir le résultat suivant, et je suis plutôt content de moi...
Update 20/02/2008 : J'ai fait quelques corrections...
J'aurais pu me contenter de l'imprimer en utilisant la fonction imprimer de mon butineur favoris, mais :
- Le résultat n'aurait pas été très bon...
- Ça n'aurait pas été très marrant...
J'ai décider d'utiliser les fonctions du CSS pour faire quelque chose de potable... Le CSS, on l'utilise souvent pour afficher des choses à l'écran, mais on l'utilise rarement pour imprimer. Pourtant, dans sa version 3 (qui n'est malheureusement pas assez supporté par les butineurs), on peut avoir un contrôle assez précis de ce que l'on imprime.
On peut par exemple gérer les entêtes et pieds de pages, les pages recto/verso, les sauts de page, la numérotation des chapitres et figures, les notes en pieds de page, etc...
Mais si les navigateurs ne supporte pas comme il faut le CSS 3, quel est l'intérêt de tout ça... Et bien, il existe un outils (propriétaire, mais gratuit pour un usage personnel) nommé prince, qui permet de générer un PDF à partir d'un fichier HTML et qui comprend correctement le CSS 3... Parfait, donc à l'attaque...
En premier lieu, je me suis fait un scripts python pour extraire le contenu des différents chapitres du livre depuis le site djangobook.com. Ce script utilise le module BeautifulSoup, qui permet une manipulation aisé des fichiers HTML (un peu à la manières des fonctions de manipulation du DOM en javascript). Le script récupère successivement toutes les pages, extrait le contenu du div ayant la classe "yui-b" et aglomère le tout dans un fichier res.html.
from BeautifulSoup import BeautifulSoup as Soup
from urllib import urlopen
f=file('res.html', 'w')
base_url = 'http://www.djangobook.com/en/1.0/'
base_url = 'http://www.djangobook.com/en/1.0/' + 'chapter%02d/'
for i in range(1, 21):
#] + ['appendix%s/' % chr(i) for i in range(ord('A'), ord('I'))]:
print i
soup = Soup(urlopen(base_url % i))
content = soup.find('div', attrs={'class': 'yui-b'})
f.write(content.renderContents())
f.close()
from BeautifulSoup import BeautifulSoup
from BeautifulSoup import Tag, NavigableString
soup = BeautifulSoup(file('res.html'))
hs = soup.findAll(['h%d' % i for i in range(4)])
ll = 2
toc = Tag(soup, 'div')
toc['class'] = 'toc'
soup.insert(0, toc)
t = Tag(soup, 'h1')
t.insert(0, NavigableString('Table of Contents'))
toc.insert(0, t)
t = Tag(soup, 'ul')
t['class'] = 'toc'
toc.insert(1, t)
for i in range(len(hs)):
h = hs[i]
h['id'] = 'chapter%d' % i
l = int(h.name[1])
h.name = 'h%d' % (l - 1)
if l > ll:
t = Tag(soup, 'ul')
t2.insert(9999999, t)
elif l < ll:
for j in range(ll - l):
t = t.parent.parent
t2 = Tag(soup, 'li')
t3 = Tag(soup, 'a')
t3['href'] = '#chapter%d' % i
t4 = NavigableString(h.renderContents())
t.insert(9999999, t2)
t2.insert(0, t3)
t3.insert(0, t4)
ll = l
cns = soup.findAll(attrs={'class': lambda x: x and 'cn' in x.split()})
for i in range(len(cns)):
cns[i]['id'] = 'cn%s' % i
for i in soup.findAll(attrs={'class': lambda x: x and 'system-messages' in x.split()}):
i.extract()
print """<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>The Definitive Guide to Django: Web Development Done Right</title>
<meta name="author" content="Adrian Holovaty, Jacob Kaplan-Moss"/>
<link rel="stylesheet" type="text/css" href="boom.css"/>
</head>
<body>"""
print soup.prettify()
print """</body>
</html>"""
A partir de là, j'obtiens un fichier html à peu près correct (il y a encore plein de balises avec des id identiques...). Je peux donc y appliquer un CSS pour l'imprimer. Je suis parti du fichier CSS donner dans l'excellent article sur A List Appart. J'ai modifier la taille des pages, les marges, et la façon d'afficher les illustrations, les légendes ou les notes. Au bout d'une heure de tatonement, je suis parvenu à obtenir le résultat suivant, et je suis plutôt content de moi...
Update 20/02/2008 : J'ai fait quelques corrections...