Site original : Sam & Max: Python, Django, Git et du cul
Le dernier exercice avait bien été apprécié, alors je remet ça.
Consigne :
Créer un script de brute force de passwords Unix par dictionnaire.
Puisqu’on est pas non plus des Kevin Mitnick en puissance, on va supposer que vous êtes connectés sur la machine, que vous avez les droits root dessus et que vous avez localisé les mots de passe comme étant dans “/etc/shadow”.
Votre script va vérifier si il possède un dictionnaire de mots de passe. Si ce n’est pas le cas, il va télécharger celui-ci et le décompresser : http://xato.net/files/10k%20most%20common.zip.
Ensuite vous parcourez le fichier shadow, et vous essayez de trouver quel mot de passe se cache derrière chaque hash. Si il n’y a pas de hash, vous pouvez ignorer l’utilisateur.
Exemple de sortie:
Processing password for user "root": no password hash to process. Processing password for user "daemon": no password hash to process. Processing password for user "bin": no password hash to process. Processing password for user "sys": no password hash to process. Processing password for user "www-data": no password hash to process. Processing password for user "sam": fail to break password. Processing password for user "test": password is "cheese". Processing password for user "messagebus": no password hash to process. Processing password for user "avahi-autoipd": no password hash to process. Processing password for user "avahi": no password hash to process. ...
Afin de simplifier l’exercice, il n’est pas demandé de gestion d’erreur ou de passage en a paramètre du script.
Comme d’habitude, il n’y a pas de solution ultimate de la mort qui tue, c’est juste pour le fun.
Cog est un outil Python en ligne de commande qui permet d’insérer du code Python dans un fichier, afin qu’il génère une partie de ce fichier.
A priori, ça ressemble à un langage de template. Là où ça diffère, c’est que cog ne cherche pas à générer un fichier différent, il insère le code généré dans le fichier original, et garde le code de génération.
Exemple, vous avez envie d’insérer un warning en haut de plusieurs fichiers de code. Vos choix :
L’alternative des bricoleurs qui ont juste besoin de quelques insertions, c’est Cog. Par exemple, votre fichier contient :
# [[[cog for l in open('warning.txt'): cog.out("# " + l) ]]] # [[[end]]] import vostrucs vostrucs.faire_vos_machins()
Après un cog.py -r votre_fichier.py
, votre fichier sera :
# [[[cog for l in open('warning.txt'): cog.out("# " + l) ]]] # Attention, ceci est un avertissmement super important. # Ce logiciel ne vient avec aucune garantie. Il vomira dans votre salon. # Violera votre femme, lui collera l'hépatite C et offrira un CD de one direction # à vos enfants. # [[[end]]] import vostrucs vostrucs.faire_vos_machins()
cog vient avec plusieurs options, par exemple la possibilité de retirer le code de génération ou de lire le précédent texte généré depuis le code de génération.
La véritable force de l’outil c’est qu’il peut utiliser n’importe quel module Python, et donc générer du texte à partir d’un contenu en ligne ou un fichier CSV. Il est d’ailleurs né parce que l’auteur était codeur C et avait besoin de générer du code à partir d’un fichier XML.
Le seul défaut de Cog, c’est que c’est verbeux. Et moche.
On a tendance à utiliser l’un pour l’autre, et inversement. Moi-même je le fais régulièrement, y compris dans plein d’articles de ce blog. Mais il y a pourtant une différence sémantique, bien que dans la pratique on s’y retrouve.
Les paramètres sont les noms que l’on spécifie dans la signature de la fonction :
def ma_fonction(arg1, arg2='valeur', *args): # faire un truc
Ici arg1
, arg2
et args
sont des paramètres. Ce sont les noms des différents points d’entrée de données dans la fonction.
Les arguments sont les valeurs passées à l’appel de la fonction :
ma_fonction('foo', arg2='bar')
'foo'
et 'bar'
sont des arguments. C’est ce que l’on passe via les différents points d’entrée dans la fonction, au moment de son utilisation.
Maintenant, pour les tatillons, ce n’est valable que pour ceux qui aiment les anglicismes. Car en français, normalement, on a que le mot “paramètre”, et on utilise :
Certaines personnes, qui aiment bien embrouiller les choses, parlent d’”argument muet” pour traduire “parameter”. Mais on va pas trop se plaindre, car les anglais ont encore plus de synonymes la con pour parler des paramètres et arguments.
Bref, en résumé : on définit des paramètres, et on passe des arguments. Je continuerai probablement à mélanger les deux moi-même, alors détendez-vous du slip.
vendredi 24 janvier 2014 [15:43:09] Sam: … ho bordel ça buzz sur FB et twitter ton article -> 20 000 vues en 2 jours. Putain de record. Essentiellement parce qu'on a été posté par Korben. [15:43:26] Max: mmm nan je pense que c FB [15:43:26] Sam: hey quand même non !!! http://sitedeculdemax.com/sprite.jpg ça déchirrrre !!! -> elle est pas black black non plus, plutôt metisse [15:43:30] Sam: mais c'est vrai que c'est beau [15:43:46] Sam: oui fb, mais fb nous a connu grace à korben [15:43:47] Max: hehe [15:43:53] Max: ha ok [15:44:07] Sam: suis en train de penser à un truc, faudrait remplacer la recherche du blog par le widget de recherche google car la recherche wordpress est nulle et fait bouriner le serveur, et que google lui a tout indexé comme il faut, à voir si ça cadre avec ta deontologie :) -> y a peut être une solution alternative [15:44:20] Sam: il me smeble qu'il y a des services pour ça [15:44:25] Sam: autre que google [15:44:33] Sam: car c'est vrai que là c'est super nul [15:45:06] Max: je dois pouvoir faire mieux en terme de detection de blur, je viens de trouver des scripts pour detecter les lignes et quadrillage, faudrait que j'adapte ça à mes images et que je floutte ces parties pour ne prendre en compte que les formes arrondies (nichons, fesses, tete de salope), à mon avis ça pourrait améliorer [15:45:22] Sam: lol [15:45:24] Max: ben si t'as un autre truc dis, mais là ça sert à rien c clair [15:45:25] Sam: que les formes arondies [15:45:50] Max: vi [15:45:57] Max: pour detecter ça a pas l'air trop dur [15:46:33] Max: apres j'aime pas opencv, pas reussi à installer sur mon mac, il compile plein de trucs, c lourd comme machine, y a ptet moyen avec PIL, faut voir [15:48:07] Max: http://sitedeculdemax.com [15:48:08] Max: c en prod là [15:48:29] Max: mais faut attendre que je traite toutes les autres images, ça tourne sur les serveurs pour recalculer blur, etc [15:48:44] Max: me tâte meme pour rescreenshooter les anciennes video :) [15:48:54] Sam: tu sais, si aucune image n'atteind un certain seuil de qualité [15:49:04] Sam: tu devrais exclure la video des 3 premières pages [15:49:12] Max: c fait deja [15:49:15] Sam: car là il y a 3 / 4 degueu qui ont rien à foutre là [15:49:24] Max: la premiere page prend que les top quality [15:49:30] Max: ouais mais certaines passe à travers :/ [15:49:37] Max: genre quand y a des gros pixel [15:49:44] Max: c compté comme quadrillage [15:49:49] Sam: logique [15:49:53] Max: d'où ma necessité d'améliorer [15:50:00] Max: pareil si y a du carelage, du fishnet, etc [15:50:14] Max: du coup ça fausse la moyenne [15:52:13] Max: c quand meme bon là http://sitedeculdemax.com/last [15:52:46] Sam: clairement mieux qu'avant [15:54:34] Max: y a des chances que je raplique un peu en aout au fait, pour faire un fkk, si ça te dit :) [15:54:41] Max: ou ptet avant meme... [16:04:19] Sam: pourquoi pas [16:04:25] Sam: d'ici aout ça fait loin cela dit [16:04:39] Max: ouais mais je previens quand meme [16:05:06] Max: en general on se previent avant avec picsou ça permet de so'rganiser, d'avoir des bons tarifs, mouiiii c pas cher [16:05:14] Sam: ^^ [16:11:48] Max: hooo putain [16:11:52] Max: 16000 vues ! [16:11:57] Max: et le serveur tiens le coup :) [16:12:03] Sam: merci varnish [16:12:05] Sam: merci Max [16:12:38] Max: faut que j'aille voir dans quel etat il est :) [16:12:43] Max: ptet c en train de mourir [16:13:29] Max: ça tiens le coup [16:13:36] Max: 2.5 de load average [16:13:44] Max: ça a l'air stable [16:13:55] Max: bon on sait qu'on peut faire 15K / jour [16:14:00] Max: pour 4€ ça va [16:16:24] Sam: ^^ [16:18:47] Sam: AH oui là c'est clairement facebook [16:23:36] Max: tin faut mettre une pop under avec du carpédiem ^^ [16:24:33] Sam: en fait, faut arrêter de faire des articles python tous les jours [16:24:36] Sam: juste un article de cul par mois [16:24:38] Sam: ça suffit [16:25:08] Max: :) [16:25:16] Max: par contre le partage c pas top on m'a dit [16:25:27] Max: un truc mainstream comme ça faut un gros boutons "partager sur fb" [16:25:32] Max: on vient de me faire la remarque [16:25:46] Max: les gens savent pas copier coller une url, savent pas ce que c une url deja... [16:25:55] Sam: Ouai, déjà que j'ai horreur de ces boutons, on va pas non plus leur faire une place spéciale avec un podium [16:25:57] Max: c con on perd du potentiel là [16:26:04] Max: le trafic ! [16:26:06] Max: le trafic ! [16:26:06] Sam: c'est très bien si ça filtre les abrutis [16:26:08] Sam: m'en fout [16:26:12] Max: le trafic ! [16:26:14] Sam: je veux pas du traffic d'abrutis sur ce blog [16:26:18] Max: c'est l'or noir du webmaster [16:26:22] Max: le saint graal ! [16:26:25] Sam: ranafout [16:26:28] Max: le fromage de tête ! [16:26:35] Sam: on a un super publique [16:26:41] Sam: c'est entre autre parcequ'on écrème les cons [16:26:41] Max: t'es pas commercial pour un sou :) [16:26:48] Sam: au contraire [16:26:51] Sam: je suis comme cartman [16:26:58] Sam: et son park d'atttraction [16:27:02] Max: oui c vrai, mais bon là ça va attirer les cons [16:27:02] Sam: c'est une stratégie commerciale [16:27:06] Sam: "vous pouvez pas rentrer" [16:27:08] Max: j'attends caroline fourest... [16:27:20] Max: stan et kule en particulier [16:27:33] Sam: plus je regarde cette série [16:27:38] Sam: plus je trouve qu'elle a du génie [16:28:26] Max: depuis le temps que je le dis... [16:28:33] Max: ça et al bamby... [16:28:39] Max: tout le reste c'est à jetter [16:28:40] Sam: Ouai mais je te fais jamais confiance [16:28:46] Sam: avec toi y a un ratio de 1 sur 100 [16:28:51] Sam: le 1 est de l'or [16:28:53] Sam: j'avoues [16:28:56] Sam: mais ça fait beaucoup à filtrer [16:28:57] Max: nan c'est l'instinc :) [16:33:08] Max: sur les 16000 vis, pas un connard c'est inscrit à la newsletter, c vrai que c du traf de merde quand meme [17:07:16] Max: 20k ! [17:07:18] Max: ça va péter ! [17:07:24] Max: le serveur chie des bulles là [17:07:27] Max: alerte rouge ! [17:07:32] Sam: lol [17:07:51] Sam: ça va il répond toujours vite [17:07:56] Max: je vais tout rediriger sur sitedeculdemax.com ^^ [17:07:59] Sam: lol [17:08:35] Max: pinaise c mortel ces buzzs quand même [17:10:16] Max: une fois j'avais fait un buzz comme ça avec un truc de téléphone geoloc, ct monté à 100k, si ça fait pareil ça va chier pour le serv [17:10:27] Max: par contre de nos jours le hic c que le buzz retombe et paf [17:10:41] Max: avant ça faisait du backlink dans les forums, les blogs etc [17:11:01] Max: depuis FB et twitters les buzzs ne font plus gagner de PR, ça s'etouffe dans le gouffre de l'internet [17:13:02] Sam: ça s'étouffe dans le gouffre de facebook [17:13:06] Sam: car facebook est une boite noire [17:13:12] Sam: non indexée par les moteurs de recherche [17:13:15] Sam: avec de l'info jetable [17:13:20] Max: ben vouais [17:13:21] Sam: que personne n'ira relire, comme sur un forum [17:13:22] Max: c triste [17:16:02] Sam: je crois que je vais poster cette conversation sur le blog [17:16:16] Sam: ça fait longtemps qu'on a pas eu un post "en couliss" [17:20:19] Max: tin g tjrs pas trouvé la bonne cochonne pour l'aricle du gode en pyrex... [17:21:46] Sam: je l'attend de pied ferme [17:30:56] Max: mouiii [17:31:08] Max: ça existe un snippet pour avoir un manager pour foutre du code en cache ? [17:31:20] Max: genre là dans le model de Video on a pas mal de trucs en caches [17:31:46] Max: mais à chaque on appelle REDIS.set ... ou on utilise le cache django mais un manager ça serait ptet pas mal [17:32:00] Sam: oui, y a django-cache-machine et johny-cache font ça via des managers [17:32:09] Max: ha, mais ct l'usine non ? [17:32:16] Sam: ah oui faut pas rêver [17:32:22] Max: on els avaient pas utilisé pour leur côté compliqué il me semble ? [17:32:29] Max: mouais [17:32:33] Sam: forcément, un modèle c'est compliqué [17:32:38] Sam: y a des relations dans tous les sens [17:32:41] Max: vouais [17:32:43] Sam: pour avoir un cas généralisable à tout [17:32:48] Sam: faut un truc costaus [17:32:59] Max: d :) [17:33:12] Max: ouais faut couvrir toutes les possibilités [...] [17:43:16] Sam: ah, ça y est, S&m est en 500 [17:44:26] Max: il a pas tenu le coup [17:44:30] Max: paix à son âme [17:48:12] Max: je stoppe varnish et php qui continue à être à 100% de cpu... [17:48:21] Sam: forcément [17:48:23] Max: tin faut un blog en python homemade [17:48:36] Sam: c'est mort, on code pas un blog [17:50:01] Max: bon g redem php [17:50:04] Max: c lent maisça marche [17:50:09] Max: faudrait augmenter le timeout de nginx [17:50:25] Max: mais ça veut dire aussi plus de cnx // qui attendent et embourbe le tout [18:46:48] Max: y a rien à faire Mr Sam [18:46:52] Max: on a tout essayé [18:47:06] Max: il est mort sur le coup suite à une longue intoxication [18:47:20] Sam: c'est la vie [18:47:30] Sam: on saura jamais combien de personnes sont vraiment venues [18:47:39] Sam: et le blog sera inaccessible jusqu'à demain [18:48:03] Max: au moins ouais [18:48:09] Max: en meme temps c pas fait pour ça [18:48:13] Max: ptit serveur [18:48:18] Max: il chie sa mere là [19:20:42] Max: y a vraiment quelqu'un qui tape à mort dans le feed [19:20:43] Max: 46.44 RxURL /beaucoup-de-femmes-sont-de-mauvais-coups/ 35.45 RxURL /feed/ [19:20:57] Max: à gauche c le nb de requete sur l'url ces 60 dernieres seconds [19:21:10] Max: presque une req / s sur l'url du feed [19:46:30] Max: bon le site est reup car j'ai forbidden les /feed/ et /beaucoup de femme..../ , ces pages sont doncs réduites au silence pour éviter que tout le site en patisse [20:16:57] Max: bon g fait une modif asser marrante, je force le serveur à servir les pages en foutant un timeout infin, du coup le site est up, la page se charge au bout de très lgtps mais au moins elle se charge, j'ai le serveur sous les yeux pour voir quand il va brûler, là 7 de load average, il peut mieux faire :) [20:17:32] Max: ce qui ne vous tue pas vous rend plus fort.. courage français, ça va zouker !! [20:36:06] Max: cawpitain cawpitain ça tiens lew coup !! samedi 25 janvier 2014 [13:30:58] Sam: ça a tenu [13:31:03] Sam: tu as remis le feed up ? [13:31:54] Max: si senior [13:31:59] Sam: good [13:32:10] Max: g augmenté le timeout fastcgi sur nginx [13:32:30] Max: du coup ça a tenu, sauf que le serv est monté à 7/8 de load [13:32:37] Max: mais bon, pas grave [13:32:39] Sam: c'est poétique [13:32:52] Max: c à 10k là [13:33:10] Max: on dirait qu'il y a une rémanence :) [13:33:14] Sam: il est que midi [13:33:39] Sam: J'aurais pas du posté l'article sur beautiful soup hier [13:33:44] Sam: j'aurais du en mettre un moins important [13:33:50] Sam: car je pense que du coup il a perdu en visibilité [13:34:07] Sam: alors que y a un sacré boulot dessus [13:34:34] Max: clair [13:34:45] Sam: sinon j'ai vu "sunshine" en film hier [13:34:48] Sam: très très bon [13:34:50] Sam: je te le recommande [13:34:58] Max: bé il est vieux [13:34:59] Sam: ouai [13:35:02] Max: ouais pas trop mal [13:35:43] Sam: ah, au fait, tu sais on se demandait quoi offrir pour le prochain concourt [13:35:48] Sam: ben on m'a donné une super idée [13:35:52] Sam: un GROS fromage [13:36:02] Max: bé on avait dit xxxx [13:36:09] Max: un gros fromage ? [13:36:11] Sam: un gros fromage [13:36:18] Sam: genre une roue de parmesan [13:36:22] Sam: un truc bien gros [13:36:28] Sam: parceque le fromage, c'est bon [13:36:39] Sam: et y en a marre des trucs electronique [13:37:09] Sam: et je pense qu'il faut sensibiliser le lectorat à la beauté du fromage [13:37:31] Sam: d'ailleurs, ça te manque pas le fromage chez les thai ? [13:38:21] Max: nan y a tout ce qui faut en fromage ici, des ryaons entiers, de l'import [13:38:39] Max: une roue de parmesan ça doit couter dans les 500 / 1000 € [13:38:53] Max: deja le petit bout de merde 100 grs c'est 4€ au leclerc [13:39:01] Sam: ouai c'était pour l'exemple [13:39:06] Sam: le parmesan c'est hors de nos moyen [13:39:09] Max: sans compter le poids du merdie en terme de tarif de livraison [13:39:16] Sam: c'est clair [13:39:20] Max: enfin bon ça se périme déjà [13:39:29] Max: donc c mort, si le mec est celib ça partira à la poubelle [13:39:40] Max: mais la bouffe bof [13:39:44] Sam: ça se garde super bien le fromage, t'abuse [13:39:49] Max: bcp de gens sont "spéciaux" [13:39:52] Sam: moi j'étais super enthousiaste [13:39:53] Max: pas une fois ouvert [13:40:07] Sam: ou alors on laisse le choix : fromage ou xxxxx ? [13:40:08] Sam: lol [13:40:13] Max: si tu ouvre un sachet de parmesan ou meme un morceaud e parmesans sous vide faut le consommer rapidos [13:40:17] Max: :) [13:40:18] Sam: une proposition unique dans une vie [13:40:25] Max: bcp de gens aiment pas ça [13:40:36] Sam: et ben c'est tous des nuls [13:40:39] Sam: ils méritent pas un cadeau [13:40:46] Max: certains ont meme des lubbies, genre regime dukan, vegetariens, vegetaliens, etc... :) [...] [13:41:21] Max: et pis bon la bouffe c'est plus la france, maintenant c macdo et cie [13:41:23] Sam: c'est pas possible [13:41:29] Sam: c'est pas faux [13:41:55] Max: les epicuriens sont tres rares [13:42:02] Max: c un coup de poker je trouve [13:42:09] Max: pis c con d'annoncer le cadeau à l'avance [13:42:13] Max: la suprise c chouette aussi [13:42:24] Max: sinon un resto atype mais bon faut l'adresse [13:42:32] Sam: je passe [13:42:42] Sam: def sam(): pass [13:42:49] Max: d'ailleurs ptit resto francais hier [13:42:53] Max: endives au jambon avec bechamel [13:43:05] Max: jamais j'ai eu droit à de la vrai béchamel en france, tjrs de la conserve [13:43:06] Sam: bon en tout cas, xxxxx je sens que ça va foirer, [13:43:09] Sam: faut trouver autre chose [13:43:12] Max: ha [13:43:49] Sam: ben oui : genre on tombe pas sur un codeur mais un policier, un biologiste ou, dieu nous en garde, un hipster [13:44:55] Sam: y a pas tant de codeurs que ça sur le blog finalement [13:45:05] Max: mouiii [13:45:07] Sam: et dans le lot, plein ont des ide qu'ils préfèrent [13:45:11] Max: je réfléchis [13:45:22] Sam: va faire caca, ça te réussi [13:45:46] Sam: j'aurais bien offert un stage de survie chez david manise, mais c'est un peu cher [13:45:52] Sam: par contre c'est super cool [13:46:23] Sam: le casque que j'ai acheter dernièrement est fantastique cela dit [13:46:27] Sam: et tout le monde aime les casques [13:46:39] Sam: tout le monde en a besoin pour un truc ou un autre [13:46:44] Max: ouais y avait ça ou la jambox [13:46:44] Sam: et il est à 60 euros [13:46:49] Sam: la jambox c'est hors budget [13:46:53] Max: mais bon c classique [13:46:55] Sam: clair [13:46:59] Sam: ça maque d'attrait [13:47:43] Max: un gode en pyrex fait main [13:48:15] Sam: j'aime l'idée, mais je pense qu'on a pas non plus une majorité de chauds lapins qui vont jouer [13:48:36] Sam: généralement la capacité à résoudre un casse tête est inversement proportionnel à la capacité à chopper [13:48:39] Max: bon je vais chier [13:48:41] Max: ça s'impose [13:48:47] Sam: je vais me faire à bouffer [13:48:49] Sam: moi je rentre [13:48:50] Sam: toi tu sors [13:48:53] Sam: on est syncro [...] [16:18:47] Max: j'ai trouvé ces cookies: http://www.pepperidgefarm.com/ProductDetail.aspx?catID=723&prdID=112059 [16:18:52] Max: c horrible chui accroc [16:18:56] Max: pourtant j'aime pas le sucre [16:19:07] Sam: c'est vrai qu'ils sont bons [16:19:14] Max: 10 grs de sucre pour un cookie de 26 grs...
Ceci est un post invité de k3c posté sous licence creative common 3.0 unported.
Un exemple de parsing HTML avec BeautifulSoup.
Cet article ne traitera pas l’écriture ou la modification de HTML, et pompera allègrement la doc BeautifulSoup (traduite).
De manière générale, pour télécharger une vidéo sur un site de replay, il faut
Prenons un exemple sur les replays de d8.tv, par exemple
(attention cet exemple sera rapidement obsolète, mais c’est le principe qui nous intéresse)
L’installation de BeautifulSoup 4 se fait avec, au choix
$ apt-get install python-bs4 $ easy_install beautifulsoup4 $ pip install beautifulsoup4
La documentation de BeautifulSoup 4 est à
http://www.crummy.com/software/BeautifulSoup/bs4/doc/
Si on regarde le code source de la page (CTRL U sous Firefox, sinon voyez avec votre navigateur préféré), on voit que la partie qui nous intéresse et qui contient videoId est courte
Ici l’identifiant recherché est 943696
En Python, on va donc faire quelque chose comme
from urllib2 import urlopen import bs4 as BeautifulSoup html = urlopen('http://www.d8.tv/d8-series/pid6654-d8-longmire.html').read() soup = BeautifulSoup.BeautifulSoup(html)
Comme le dit la doc BeautifulSoup
début du pompage de la doc BeautifulSoup
Beautiful Soup transforme un document HTML complexe en un arbre complexe d’objets Python. Mais vous aurez à manipuler seulement quatre types d’objets : Tag, NavigableString, BeautifulSoup, et Comment.
Pour l’instant, les caractéristiques les plus importantes d’un tag sont
son nom
>>> tag.name u'b'
ses attributs
Un tag peut avoir n’importe quel nombre d’attributs. Le tag
<b class="boldest">
possède un attribut “class” dont la valeur est “boldest”. On peut accéder les attributs d’un tag en le traitant comme un dictionaire :
>>> tag['class'] u'boldest'
On peut accéder directement ce dictionaire avec .attrs:
>>>tag.attrs {u'class': u'boldest'}
>>> tag.string u'Extremely bold' >>> type(tag.string) class 'bs4.element.NavigableString'
Un NavigableString est comme une chaîne de caractères Python Unicode, sauf que elle supporte aussi quelques unes des caractéristiques décrites dans Navigating the tree et Searching the tree. Vous pouvez convertir une chaîne de caractères NavigableString en Unicode avec unicode():
>>> unicode_string = unicode(tag.string) >>> unicode_string u'Extremely bold' >>> type(unicode_string) type 'unicode'
Comme l’objet BeautifulSoup ne correspond pas à un tag final HTML ou XML, il n’a pas de nom et pas d’attributs. Mais il est parfois utile de rechercher son .name, donc on lui a donné le .name “[document]”:
>>> soup.name u'[document]'
Tag, NavigableString, et BeautifulSoup couvrent presque tout ce que vous verrez dans un fichier HTML ou XML, mais il y a quelques cas à part. Le seul qui doit vous inquiéter (un peu) est le commentaire :
markup = "<b><!--Hey, buddy. Want to buy a used parser?--></b>"
>>> soup = BeautifulSoup(markup) >>> comment = soup.b.string >>> type(comment) class 'bs4.element.Comment'
L’objet Comment est simplement un type spécial de NavigableString:
>>> comment u'Hey, buddy. Want to buy a used parser'
Mais quand il apparaît en tant que morceau de document HTML, un Comment est affiché avec un formattage spécial :
# <b> # <!--Hey, buddy. Want to buy a used parser?--> # </b>
Beautiful Soup définit des classes pour n’importe quoi d’autre qui apparaîtrait dans un document XML : CData, ProcessingInstruction, Declaration, et Doctype. De la meme manière que Comment, ces classes sont des sous-classes de NavigableString qui ajoutent quelque chose à la chaîne de caractères. Voici un exemple qui remplace le commentaire par un block CDATA :
from bs4 import CData cdata = CData("A CDATA block") comment.replace_with(cdata) print(soup.b.prettify())
# <b> # <![CDATA[A CDATA block]]> # </b>
fin du pompage de la doc BeautifulSoup
On peut faire un
print soup.prettify()
pour voir à quoi ressemble le code HTML de la page
Il faut d’abord analyser la page et rechercher ce qui suit videoId
Pour commencer nous allons naviguer dans le document.
BeautifulSoup permet de multiples syntaxes, par exemple, on n’est pas obligé de donner le chemin complet
soup.head.meta
ou
soup.meta
affichent le meme résultat, vu que la première balise meta est sous la balise head
<meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
Si on regarde les méthodes disponibles
dir(soup.meta) ['FORMATTERS', '__call__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dict__', '__doc__', '__eq__', '__format__', '__getattr__', '__getattribute__', '__getitem__', '__hash__', '__init__', '__iter__', '__len__', '__module__', '__ne__', '__new__', '__nonzero__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', '__unicode__', '__weakref__', '_all_strings', '_attr_value_as_string', '_attribute_checker', '_find_all', '_find_one', '_lastRecursiveChild', '_last_descendant', 'append', 'attribselect_re', 'attrs', 'can_be_empty_element', 'childGenerator', 'children', 'clear', 'contents', 'decode', 'decode_contents', 'decompose', 'descendants', 'encode', 'encode_contents', 'extract', 'fetchNextSiblings', 'fetchParents', 'fetchPrevious', 'fetchPreviousSiblings', 'find', 'findAll', 'findAllNext', 'findAllPrevious', 'findChild', 'findChildren', 'findNext', 'findNextSibling', 'findNextSiblings', 'findParent', 'findParents', 'findPrevious', 'findPreviousSibling', 'findPreviousSiblings', 'find_all', 'find_all_next', 'find_all_previous', 'find_next', 'find_next_sibling', 'find_next_siblings', 'find_parent', 'find_parents', 'find_previous', 'find_previous_sibling', 'find_previous_siblings', 'format_string', 'get', 'getText', 'get_text', 'has_attr', 'has_key', 'hidden', 'index', 'insert', 'insert_after', 'insert_before', 'isSelfClosing', 'is_empty_element', 'name', 'namespace', 'next', 'nextGenerator', 'nextSibling', 'nextSiblingGenerator', 'next_element', 'next_elements', 'next_sibling', 'next_siblings', 'parent', 'parentGenerator', 'parents', 'parserClass', 'parser_class', 'prefix', 'prettify', 'previous', 'previousGenerator', 'previousSibling', 'previousSiblingGenerator', 'previous_element', 'previous_elements', 'previous_sibling', 'previous_siblings', 'recursiveChildGenerator', 'renderContents', 'replaceWith', 'replaceWithChildren', 'replace_with', 'replace_with_children', 'select', 'setup', 'string', 'strings', 'stripped_strings', 'tag_name_re', 'text', 'unwrap', 'wrap']
on voit que de nombreuses méthodes sont disponibles, et suivant la doc, on peut donc le traiter comme un dictionnaire
>>> soup.meta['http-equiv'] 'Content-Type'
et tester quelques méthodes
>>> soup.meta.name 'meta' >>> soup.meta.find_next_sibling() '<meta content="D8" name="author"/>'
soup.meta.find_previous_sibling()
Nous voyons que soup.meta a un sibling (frère ou soeur) suivant, mais pas de précédent, c’est le premier de l’arborescence.
Bon, la balise meta a pour nom meta, pas un scoop, on continue avec les clés de dictionnaire, sans surprise
>>> soup.meta.find_next_sibling() '<meta content="D8" name="author"/>' >>> soup.meta.find_next_sibling()['content'] 'D8' >>> soup.meta.find_next_sibling()['name'] 'author'
Pour le fun, regardez ce que renvoie
soup.meta.find_next_sibling().parent
et
soup.meta.find_next_sibling().parent.parent
et je vous laisse deviner la prochaine commande que vous allez passer…
Revenons à une recherche qui va trouver de nombreuses occurences
soup.find('div')
va trouver la première balise div, et
soup.findall('div')
va renvoyer une liste contenant tous les div de la page, mais cela ne permet pas de trouver facilement la portion contenant videoId, par contre, la documentation de BeautifulSoup montre comment trouver spécifiquement une CSS class, voir
searching by css class dans la doc BeautifulSoup
dans la documentation BeautifulSoup
Voici la syntaxe à utiliser
soup.find('div',attrs={"class":u"block-common block-player-programme"})
va renvoyer la partie qui nous intéresse, par exemple
<div class="block-common block-player-programme"> <div class="bpp-player"> <div class="playerVideo player_16_9"> <div class="itemprop" itemprop="video" itemscope itemtype="http://schema.org/VideoObject"> <h1>Vidéo : <span itemprop="name">Longmire - Samedi 30 novembre à 20h50</span></h1> <meta itemprop="duration" content="" /> <meta itemprop="thumbnailUrl" content="http://media.canal-plus.com/wwwplus/image/53/1/1/LONGMIRE___BANDE_ANNONCE__131120_UGC_3279_image_L.jpg" /> <meta itemprop="embedURL" content="http://player.canalplus.fr/embed/flash/CanalPlayerEmbarque.swf?vid=975153" /> <meta itemprop="uploadDate" content="2013-11-29T00:00:00+01:00" /> <meta itemprop="expires" content="2014-02-18T00:00:00+01:00" /> <canal:player videoId="975153" width="640" height="360" id="CanalPlayerEmbarque"></canal:player>
Le type de cette donnée est bs4 élément tag
Comme l’a dit un homme célèbre
si vous ne savez pas ce que contient une variable, vous ne comprenez pas le programme
on peut donc faire un type, dir, help, doc, repr, par exemple
>>> type(soup.find('div',attrs={"class":u"block-common block-player-programme"})) class 'bs4.element.Tag'
donc nous pouvons rechercher un tag, comme
canal:player
>>> soup.find('div',attrs={"class":u"block-common block-player-programme"}).find('canal:player') '<canal:player height="360" id="CanalPlayerEmbarque" videoid="786679" width="640"></canal:player>' >>> soup.findAll('div', attrs={"class":u"tlog-inner"})
renvoie une liste
[<div class="tlog-inner"> <div class="tlog-account"> <span class="tlog-avatar"><img height="30" src="http://media.canal-plus.com/design/front_office_d8/images/xtrans.gif" width="30"/></span> <a class="tlog-logout le_btn" href="#">x</a> </div> <form action="#" method="post"> <label class="switch-fb"> <span class="cursor traa"> </span> <input checked="" id="check-switch-fb" name="switch-fb" type="checkbox" value="1"/> </label> </form> <div id="headerFbLastActivity"> <input id="name_facebook_user" type="hidden"/> <div class="top-arrow"></div> <div class="top"> <div class="top-bg"></div> <div class="top-title">Activité récente</div> </div> <div class="middle"> <div class="wrap-last-activity"> <div class="entry">Aucune</div> </div> <div class="wrap-notification"></div> </div> <div class="bottom"> <a class="logout" href="#logout">Déconnexion</a> </div> </div> </div>]
On peut prendre le premier élément de cette liste
soup.findAll('div', attrs={"class":u"tlog-inner"})[0]
et ne vouloir que la ligne commençant par “span class”
soup.findAll('div', attrs={"class":u"tlog-inner"})[0].span
ce qui affiche
<span class="tlog-avatar"><img height="30" src="http://media.canal-plus.com/design/front_office_d8/images/xtrans.gif" width="30"/></span>
Voyons le type de donnée
>>> type(soup.findAll('div', attrs={"class":u"tlog-inner"})[0].span) <class 'bs4.element.Tag'>
et voyons les méthodes disponibles
>>> dir(soup.findAll('div', attrs={"class":u"tlog-inner"})[0].span) ['FORMATTERS', '__call__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dict__', '__doc__', '__eq__', '__format__', '__getattr__', '__getattribute__', '__getitem__', '__hash__', '__init__', '__iter__', '__len__', '__module__', '__ne__', '__new__', '__nonzero__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__' , '__setitem__', '__sizeof__', '__str__', '__subclasshook__', '__unicode__', '__weakref__', '_all_strings', '_attr_value_as_string', '_attribute_checker', '_find_all', '_find_one', '_lastRecursiveChild', '_last_descendant', 'append', 'attribselect_re', 'attrs', 'can_be_empty_element', 'childGenerator', 'children', 'clear', 'contents', 'decode', 'decode_contents', 'decompose', 'descendants', 'encode', 'encode_contents', 'extract', 'fetchNextSiblings', 'fetchParents', 'fetchPrevious', 'fetchPreviousSi blings', 'find', 'findAll', 'findAllNext', 'findAllPrevious', 'findChild', 'findChildren', 'findNext', 'findNextSibling', 'findNextSiblings', 'findParent', 'findParents ', 'findPrevious', 'findPreviousSibling', 'findPreviousSiblings', 'find_all', 'find_all_next', 'find_all_previous', 'find_next', 'find_next_sibling', 'find_next_sibling s', 'find_parent', 'find_parents', 'find_previous', 'find_previous_sibling', 'find_previous_siblings', 'format_string', 'get', 'getText', 'get_text', 'has_attr', 'has_k ey', 'hidden', 'index', 'insert', 'insert_after', 'insert_before', 'isSelfClosing', 'is_empty_element', 'name', 'namespace', 'next', 'nextGenerator', 'nextSibling', 'ne xtSiblingGenerator', 'next_element', 'next_elements', 'next_sibling', 'next_siblings', 'parent', 'parentGenerator', 'parents', 'parserClass', 'parser_class', 'prefix', 'prettify', 'previous', 'previousGenerator', 'previousSibling', 'previousSiblingGenerator', 'previous_element', 'previous_elements', 'previous_sibling', 'previous_sibli ngs', 'recursiveChildGenerator', 'renderContents', 'replaceWith', 'replaceWithChildren', 'replace_with', 'replace_with_children', 'select', 'setup', 'string', 'strings' , 'stripped_strings', 'tag_name_re', 'text', 'unwrap', 'wrap']
Nous voulons maintenant juste ce qui suit videoId.
dir(soup.find('div',attrs={"class":u"block-common block-player-programme"}).find('canal:player'))
montre, entre autres choses, que la méthode get est disponible.
Pour récupérer l’identifiant qui nous intéresse, on peut donc faire
>>> soup.find('div',attrs={"class":u"block-common block-player-programme"}).find('canal:player').get('videoid') '975153'
ou utiliser une autre syntaxe
>>> soup.find('div',attrs={"class":u"block-common block-player-programme"}).find('canal:player')['videoid'] '975153'
De la même manière, on peut récupérer le titre de la vidéo
>>> soup.find('h3',attrs={"class":u"bpp-title"}) '<h3 class="bpp-title">Longmire - Samedi 30 novembre à 20h50</h3>'
mais on veut juste le titre, donc
>>> soup.find('h3',attrs={"class":u"bpp-title"}).text uu'Longmire - Samedi 30 novembre \xe0 20h50'
Maintenant que l’on a le numéro de la vidéo, on peut le passer au site qui contient l’adresse, et avec un peu de scripting XML, récupérer l’adresse de la vidéo (un autre article sera consacré au scripting XML)
Selon que la vidéo vient de D8 ou de canal, elle sera sur
vidéo de d8
ou
vidéo de Canal Plus
et avec un peu de code
from lxml import objectify def get_HD(d8_cplus,vid): root = objectify.fromstring(urlopen('http://service.canal-plus.com/video/rest/getVideosLiees/'+d8_cplus+'/'+vid).read()) for x in root.iter(): if x.tag == 'VIDEO' and x.ID.text == vid: for vres in vidattr: if hasattr(x.MEDIA.VIDEOS, vres): print 'Resolution :', vres videoUrl = getattr(x.MEDIA.VIDEOS, vres).text break break print videoUrl for x in ['d8','cplus']: get_HD(x,vid)
on peut trouver l’adresse de la vidéo.
Il reste juste à envoyer la commande rtmpdump, dans ce cas
rtmpdump -r rtmp://ugc-vod-fms.canalplus.fr/ondemand/videos/1311/LONGMIRE___BANDE_ANNONCE__131120_UGC_3279_video_HD.mp4 -c 1935 -m 10 -B 1 -o mavideo.mp4
Voilà, il reste à noter que BeautifulSoup peut restreindre sa recherche à une partie du document, utiliser une regex (même si c’est le mal), on peut limiter la taille de la liste renvoyée par findAll
Quelles sont les méthodes les plus utiles, si vous avez la flemme de lire toute la doc ?
['value'] != u''
soup.findAll('p', {'class': None})
>>>soup.head.link '<link href="http://media.canal-plus.com/design_pack/front_office_d8/css/d8.d25540b7a93dba7baf89e5ca53ef00e5.min.css" rel="stylesheet" type="text/css"/>' >>> soup.head.link.attrs {'href': 'http://media.canal-plus.com/design_pack/front_office_d8/css/d8.d25540b7a93dba7baf89e5ca53ef00e5.min.css', 'type': 'text/css', 'rel': ['stylesheet']}
<div class="category_link"> Category: <a href="/category/personal">Personal</a> </div>
on peut récupérer la chaîne Category : de plusieurs manières, par exemple l’évident
>>> soup.findAll('div')[0].contents[0] u'\n Category:\n '
mais aussi en remontant depuis la balise a
>>> soup.find('a').previousSibling u'\n Category:\n '
Sinon cela est aussi utile avec un HTML mal foutu comme
<p>z1</p>tagada <p>z2</p>tsointsoin
Dans ce cas, pour récupérer tagada tsointsoin
on fera par exemple
soup.findAll('p')[0].next_sibling soup.findAll('p')[1].next_sibling
ou, pour faire plaisir à Sam/Max
>>> [p.next_sibling for p in soup.findAll('p')] [u'tagada', u'tsoitsoin']
>>> soup.findAll('div', {"class":"tmlog-wdrw wdrw"})[0].a '<a class="tmlogin-btn" href="#">' '<span>Se connecter</span>' '</a>' >>> soup.findAll('div', {"class":"tmlog-wdrw wdrw"})[0].a.contents [u'\n', <span>Se connecter</span>, u'\n'] >>> soup.findAll('div', {"class":"tmlog-wdrw wdrw"})[0].a.text u'\nSe connecter\n' >>> soup.findAll('div', {"class":"tmlog-wdrw wdrw"})[0].a.findAll(text=True) [u'\n', u'Se connecter', u'\n']