#!/usr/bin/env python
__author__="Riccardo Attilio Galli (riccardo<at>sideralis<dot>net)"
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 </tAbLE>
#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('<!--%s-->' % data)
def handle_decl(self,data):
if self.fetching:
self.currentNode.data.append('<!%s>' % data)
def handle_pi(self,data):
if self.fetching:
self.currentNode.data.append('<?%s>' % 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',de
fault=False,
help="controlla solo i tag che non contengono se'
stessi")
parser.add_option('-r','--reg-ignorecase',action='store_true',dest='reg_ig
norecase',default=False,
help='la regexp ignora maiuscole/minuscole')
parser.add_option('-h','--help',action='store_true',dest='help',default=Fa
lse,
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