#!/usr/bin/env python __author__="Riccardo Attilio Galli (riccardosideralisnet)" import HTMLParser,urllib,re class MatchNode(object): """ Nodo che contiene tutti gli elementi interni ad una coppia di tag di cui e' risultato un match. Se contiene dei tag uguali a starttag e endtag, il loro contenuto si trova in un altro nodo, che figura nella lista children """ __slots__=['starttag','endtag','data','children','parent'] def __init__(self): self.starttag='' self.endtag='' self.data=[] self.children=[] self.parent=None def add_child(self,child): self.children.append(child) self.data.append(child) class WebClipper(HTMLParser.HTMLParser): """ La classe WebClipper si occupa del parsing di un documento html ben formattato. Dati in fase di costruzione dell'istanza un tag d'inizio ed uno di fine, ogni qual volta il primo viene trovato viene aggiunto un nodo di tipo MatchNode, che contiene tutti i dati trovati tra i due tag. La radice dell'albero creato e' self.root """ def __init__(self,starttag,endtag): HTMLParser.HTMLParser.__init__(self) self.starttag=starttag[1:-1].lower() self.endtag=endtag[1:-1].lower() self.fetching=0 self.root=MatchNode() self.currentNode=self.root def handle_data(self,data): if self.fetching: self.currentNode.data.append(data) def handle_starttag(self,tag,attrs): orig_tag=self.get_starttag_text() if self.starttag==tag: self.fetching+=1 node=MatchNode() node.parent=self.currentNode node.starttag=orig_tag self.currentNode.add_child(node) self.currentNode=node elif self.fetching: self.currentNode.data.append(orig_tag) def parse_endtag(self,i): #override di un metodo ereditato #utilizzato per avere l'end-tag originale, per esempio #se non si fosse interessati a mantenere il case, sarebbe sufficiente #usare handle_endtag j=HTMLParser.HTMLParser.parse_endtag(self,i) #dato l'indice d'inizio di un tag, trova la fine if j==-1: return -1 #il tag non si chiude correttamente if self.fetching: orig_tag=self.rawdata[i:j] #recupero il tag originale, comprensivo di < > tag=orig_tag[1:-1].lower() if self.endtag==tag: self.currentNode.endtag=orig_tag if self.currentNode!=self.root: self.currentNode=self.currentNode.parent self.fetching-=1 else: self.currentNode.data.append(orig_tag) return j # i metodi seguenti gestiscono dati particolari # che vengono parsati separatemente def handle_comment(self,data): if self.fetching: self.currentNode.data.append('' % data) def handle_decl(self,data): if self.fetching: self.currentNode.data.append('' % data) def handle_pi(self,data): if self.fetching: self.currentNode.data.append('' % data) def hanle_charref(self,ref): if self.fetching: self.currentNode.data.append('&#%s;' % ref) def handle_entityref(self,ref): if self.fetching: self.currentNode.data.append('&%s;' % ref) def traverse(child,leaf_only=False,is_first=True): """ Ricostruisce il contenuto di un node MatchNode. Se leaf_only e' True, considera solo le foglie dell'albero. """ if leaf_only and child.children: for node in child.children: return traverse(node,leaf_only) data=[] if not is_first: data.append(child.starttag) for i in child.data: if isinstance(i,str): data.append(i) else: data.append(traverse(i,is_first=False)) if not is_first: data.append(child.endtag) return ''.join(data) def parseURL(url,starttag,endtag,leaf_only=False,bufsize=8192): """ Parsa una url o un file, restituendo una lista di nodi MatchNode per ogni coppia starttag/endtag trovata nell'url. Se leaf_only e' True considera le sole coppie di tag che non contengono se' stesse al loro interno. """ fd=urllib.urlopen(url) clipper=WebClipper(starttag,endtag) while True: data=fd.read(bufsize) if not data: break clipper.feed(data) fd.close() result=[] for node in clipper.root.children: result.append(traverse(node,leaf_only=leaf_only)) return result if __name__=='__main__': from optparse import OptionParser usage='%prog [options] url starttag endtag regexp' parser=OptionParser(usage=usage) parser.set_conflict_handler('resolve') parser.add_option('-d','--deepest',action='store_true',dest='leaf_only',default=False, help="controlla solo i tag che non contengono se' stessi") parser.add_option('-r','--reg-ignorecase',action='store_true',dest='reg_ignorecase',default=False, help='la regexp ignora maiuscole/minuscole') parser.add_option('-h','--help',action='store_true',dest='help',default=False, help='mostra questo help ed esci') opt,args=parser.parse_args() if len(args)!=4 or opt.help: import sys parser.print_help() sys.exit(-1) url,starttag,endtag,regexp=args try: result=parseURL(url,starttag,endtag,leaf_only=opt.leaf_only) if not opt.reg_ignorecase: PATTERN=re.compile(regexp,re.DOTALL) else: PATTERN=re.compile(regexp,re.DOTALL|re.IGNORECASE) for text in result: for match in PATTERN.findall(text): if match: print match except IOError,e: print e