Stanisław P. Software designer
Temat: Page scraping
Hej! Skoro są tutaj pustki (podobno?), więc rzucam nowym tematem ogólno-dyskusyjnym-kodo-analizatorskim ;)W kilku projektach musiałem szybko wyciągnąć coś ze stron i doszedłem do prostego, ogólnego modelu na parsowanie z xpath. Wygląda to tak:
class PageParsingException(Exception): pass
class ModelParser(object):
prefix_multi = False
def __init__(self, page):
if not hasattr(self, 'model'):
raise PageParsingException("model not defined")
if type(page) == str:
p = html5lib.HTMLParser(tree = html5lib.treebuilders.getTreeBuilder("lxml"))
page = p.parse(page)
if hasattr(self, 'prefix'):
nodes = page.xpath(self.prefix)
if len(nodes) > 1 and not self.prefix_multi:
raise PageParsingException("prefix ambiguous")
elif len(nodes) == 0:
raise PageParsingException("prefix not found")
page = nodes[0]
for name, val in self.model.items():
path, klass = val
xpath_result = page.xpath(path)
if type(klass) == type and issubclass(klass, ModelParser):
setattr(self, name, [klass(x) for x in xpath_result])
elif klass == str:
setattr(self, name, ''.join(str(x) for x in xpath_result))
elif hasattr(klass, "__call__"):
setattr(self, name, klass(xpath_result))
else:
raise PageParsingException("unknown result class <%s>" % (klass,))
Potem można tego prosto używać. Np. modele do wyciągania rzeczy ze sklepu dell'a:
class DealItem(ModelParser):
model = {
'name': ('''.//td[@class="para"]/a[@class="para"]/text()''', str),
'config': ('''.//td[@class="para"]/a[@class="para"]/@onclick''', cleanup_config_redir),
'price': ('''.//td/span[@class="para"]/span[@class="pricing_sale_price"]/text()''', cleanup_price),
}
def __repr__(self):
return "[Item <%s> <%s> <%s>]" % (self.name, self.config, self.price)
class DealsList(ModelParser):
prefix = '''//table//td[@style="width: 100%; height: inherit"]'''
model = {
'items': ('''./table''', DealItem),
}
def __repr__(self):
return str(self.items)
I wyciągamy itemy przez: DealsList(contents_strony).items
Czy macie jakieś ciekawsze rozwiązania, własne podobne konstrukcje? Może jest jakiś podobny projekt który przeoczyłem?