2012-12-09 2 views
5

Sto analizzando le pagine Web su un sito che visualizza i dati degli articoli. Questi articoli hanno circa 20 campi che possono o non possono verificarsi - ad esempio: prezzo, quantità, ultimo acquisto, alto, basso, ecc.Modo elegante per provare/tranne una serie di comandi BeautifulSoup?

Attualmente sto usando una serie di comandi; circa 20 righe di soup.find('div',{'class':SOME_FIELD_OF_INTEREST}) per cercare ciascun campo di interesse separato. (Alcuni sono in div, span, dd, e così via, quindi è difficile fare solo un comando soup.find_all('div').)

La mia domanda: C'è un modo elegante per try e except tutto ciò in modo tale che la visione di detto codice può essere più compatto o conciso? In questo momento una riga di esempio sarebbe:

try: 
    soup.find('div', {'id':'item-pic'}).img["src"] 
except: 
    "" 

Speravo di combinare tutto in una riga. Non credo di poter eseguire in modo sintattico la prova: <line of code> except: <code> e non sono sicuro di scrivere una funzione che vada try_command(soup.find('div',{'id':'item-pic'}).img["src"]) senza eseguire effettivamente il comando.

Mi piacerebbe sapere se qualcuno ha qualche consiglio (tra cui: "questo non è possibile/pratico, andare avanti"). :)

EDIT: Dopo aver parlato un po ', penso di voler vedere cosa è buona pratica per la gestione delle eccezioni inline e se questa è la strada giusta da intraprendere.

+0

mi si sta cercando porzioni in un testo, questo è un lavoro per regex. – eyquem

+0

Vuoi eseguire un equivalente di espressione XPATH: '// div [@ id =" elemento-pic "]/img/@ src' usando BeautifulSoup? Per favore, fornisci altri esempi. Hai letto [i documenti] (http://www.crummy.com/software/BeautifulSoup/bs4/doc/#searching-the-tree)? 'try/except' sembra una cattiva idea qui. – jfs

+0

In cima alla mia testa ricordo vagamente le incompatibilità tra XPATH e BeautifulSoup ... lemme provalo adesso ... – binarysolo

risposta

1

forse qualcosa di simile:

def try_these(start_obj, *args) : 
     obj = start_obj 
     for trythat in args : 
      if obj is None : 
       return None 
      try : 
       if isinstance(trythat, str) : 
        obj = getattr(obj, trythat) 
       else : 
        method, opts = trythat 
        obj = getattr(obj, method)(*opts) 
      except : 
       return None 
     return obj  
src = try_these(soup, ('find', ({'id':'item-pic'},),), 
         'img', 
         ('get', ('src',),)) 

dove si può passare str per ottenere attributo di oggetto o tuple (metodo str, tuple params), infine, si otterrà None o risultato. Non ho familiarità con la zuppa quindi non sono sicuro che se lo get('src') fosse un buon approccio (come probabilmente non è un ditt), in ogni caso è possibile modificare facilmente questo snippet per accettare qualcosa di più che solo "call o attr".


Ispirato dalla tua domanda che ho scritto semplice modulo python che aiuta ad affrontare tale situazione, si può trovare here

import silentcrawler  

wrapped = silentcrawler.wrap(soup) 
# just return None on failure 
print wrapped.find('div', {'id':'item-pic'}).img["src"].value_ 

# or 
def on_success(value) : 
    print 'found value:', value 
wrapped = silentcrawler.wrap(soup, success=on_success) 
# call on_success if everything will be ok 
wrapped.find('div', {'id':'item-pic'}).img["src"].value_ 

v'è più possibilità

+0

+1 per silentwrapper, anche se è un peccato utilizzare un callback invece di restituire un oggetto wrapper. La funzione 'try_these' sembra più complessa e meno elegante di una semplice serie di try/excepts .. – dbr

+0

Non capisco perché è un peccato, comunque hai un paio di modi per usarlo. Il callback è stato utile per me (c'è callback di successo e fallimento), ma puoi ottenere l'oggetto wrapper, l'oggetto wrapper, puoi impostare il valore di default e qualunque cosa tu voglia, vedere una breve documentazione allegata al modulo: https://github.com/lupatus/silentcrawler – lupatus

+0

Oh, stupido. Non ho letto correttamente il codice di esempio, e ho perso il fatto che il primo metodo restituisce l'oggetto (o 'None'). Il callback è sembrato strano in questo caso specifico, ma restituire il valore sembra perfetto \ o/ – dbr

1

Se ho capito bene, si desidera trovare alcuni campi basati su un nome di classe interessante, ma non sono necessari lo stesso elemento (non tutti <div>)

In tal caso, con BeautifulSoup è possibile passare una regex compilata (da re.compile al posto di una stringa in molti casi.Per esempio:

print soup.findAll(re.compile(".*"), {'class': 'blah'}) 
# [<div class="blah"></div>, <span class="blah"></span>] 

possiamo usare questo ciclo per ordinatamente su tutti i relativi elementi DOM alla ricerca, che potrebbe contenere l'immagine:

import re 
import urllib 

from BeautifulSoup import BeautifulSoup as BS 


html = """ 
<html> 
<body> 
<div class="blah"></div> 
<span class="blah"><img src="yay.jpg"></span> 
<span class="other"></div> 

</body> 
</html> 
""" 

def get_img_src(soup, cssclass): 
    for item in soup.findAll(re.compile(".*"), {'class': cssclass}): 
     if item.img is not None and 'src' in dict(item.img.attrs): 
      return item.img['src'] 


soup = BS(html) 
img = get_img_src(soup, cssclass = "blah") 
print img # outputs yay.jpg, or would return None if nothing was found 

discutibile, ma penso che con i if controlli è più appropriato in questo caso, perché item.img['src']

Si potrebbe ugualmente essere scritto in questo modo:

def get_img_src(soup, cssclass): 
    for item in soup.findAll(re.compile(".*"), {'class': cssclass}): 
     try: 
      return item.img['src'] 
     except TypeError: 
      pass 

..ma è strano per la cattura TypeError qui (come 'NoneType' object has no attribute '__getitem__' in realtà non è l'eccezione che si sta cercando di recuperare, è un sottoprodotto della sintassi utilizzata da BeautifulSoup per accedere agli attributi)

+0

Questo è stato elegante - Continuo a dimenticare di regexare le cose quindi I DR (M). Grazie per il consiglio! Anche se il mio problema principale è la gestione delle eccezioni e se riesco a renderlo stilisticamente più pulito ... – binarysolo

+0

@binarysolo Oh, stavo pensando che la regex potrebbe essere usata per ottenere elegantemente il risultato che stai cercando .. ma ho completamente dimenticato di scrivere quel bit della risposta! Modificato – dbr