De la web al model
L'scrapping
Ahir ja vàrem veure quin era el model de dades i el bé que va sorl per a manipular imatges, així com la utilització de la llibreria requests ens quedava veure una altra part important: com agafar el contingut de la web, parsejar-lo i obtenir-ne la informació que necessitam.
Per fer això hi ha diverses utilitats, algunes molt especialitzades com scrappy, i amb més solera és BeautifulSoup. Aquesta llibreria té la qualitat de ser molt permisiva amb l'HTML i hi ha poques planes que no pugui tractar d'una manera o altra.
La plana de Meneame té las notícies de portada dins un div anomenat news-summari
, així que el primer que farem serà carregar la plana dins una instància de BeautifulSoup i cercar aquestes notícies.
page = requests.get('http://www.meneame.net')
if page.status_code != 200:
print "Ups! pareix que hi ha un petit problema"
return page.status_code
soup = BeautifulSoup(page.content)
noticies = soup.findAll('div', 'news-summary')
amb això BS ens haurà donat tots els divs que tenen la classes 'news-summary' amb la qual cosa ja és sols cosa d'aplicar un tractament semblant per a obtenir la informació de cada notícia
for noticia in noticies:
titular = noticia.find('h1').text
texto = noticia.find('p').text
img = noticia.find('img', 'thumbnail')
if img:
src = img['src']
match_obj = compile_obj.search(src)
id = match_obj.group('id')
try:
NoticiasPortada.objects.get(identificador=id)
except NoticiasPortada.DoesNotExist:
u"Tractant la noticia: %s" % id
noticia = NoticiasPortada(identificador=id, texto=texto, titular=titular,
thumbnail=download_image(src, 'thumb-%s.jpg' % id))
noticia.save()
El mètode find
ens permet a accedir al primer tag que compleix la condició i text
ens en dona el contingut. Si com a segon paràmetre hi possam una classe ens retornarà el primer element d'aquell tipus que tengui la classe que li hem donat.
El problema ve quan volem identificar d'alguna manera les notícies. Volem guardar sols les que tenen una imatge. Podem veure que Meneame genera el thumbnail de la notícia amb l'identificador de la mateixa, així que podem fer us d'una expressió regular:
rawstr = r"""(?P\d+).jpg$"""
compile_obj = re.compile(rawstr)
així
src = img['src']
match_obj = compile_obj.search(src)
id = match_obj.group('id')
ens donarà l'identificar de la notícia a partir de la informació de la url de la imatge. Hi ha una utilitat fantàstica per a la depuració i testeig d'expressions regulars anomenada Kodos, no us la podeu perdre.
El toc final
En una aplicació com aquesta la CPU està molt de temps sense fer res, esperant que li arribi la informació. És un bon candidat per a que l'aplicació faci ús dels Threads o del multiprocés. Una de le maneres més senzilles de fer-ho és fent servir la llibreria Queue
, d'aquesta manera sols hem de definir quants Threads farem servir en el processament i que aquests consumeixin el contingut (cada notícia) de la cua.
Així el codi final quedaría si fa no fa:
import re
import cStringIO
import requests
from Queue import Queue
from threading import Thread
from BeautifulSoup import BeautifulSoup
from django.core.management.base import BaseCommand
from django.core.files.uploadedfile import SimpleUploadedFile
from t1.models import NoticiasPortada
rawstr = r"""(?P\d+).jpg$"""
compile_obj = re.compile(rawstr)
class Command(BaseCommand):
"""Divertimento. Permet posar les notícies amb foto de meneame dins una BD. """
def handle(self, *args, **options):
page = requests.get('http://www.meneame.net')
if page.status_code != 200:
"Ups! Pareix que tenim un problema"
return page.status_code
self.q = Queue()
for i in range(5):
t = Thread(target=self._importar_portada)
t.daemon = True
t.start()
soup = BeautifulSoup(page.content)
noticies = soup.findAll('div', 'news-summary')
for noticia in noticies:
self.q.put(noticia)
self.q.join()
def download_image(self, img_url, filename):
r = requests.get(img_url)
if r.status_code == 200:
return SimpleUploadedFile(content=r.content, name=filename, content_type=r.headers.get('content-type'))
else:
return None
def _importar_portada(self):
while True:
noticia = self.q.get()
titular = noticia.find('h1').text
texto = noticia.find('p').text
img = noticia.find('img', 'thumbnail')
if img:
src = img['src']
match_obj = compile_obj.search(src)
id = match_obj.group('id')
try:
NoticiasPortada.objects.get(identificador=id)
except NoticiasPortada.DoesNotExist:
u"Processant la notícia: %s" % id
noticia = NoticiasPortada(identificador=id, texto=texto, titular=titular,
thumbnail=self.download_image(src, 'thumb-%s.jpg' % id))
noticia.save()
self.q.task_done()
I això és tot, com sempre amb Python l'explicació sol ser molt més llarga que el codi a executar, fins i tot amb els fils.
Esperant que us hagi agradat el divertimento.