Scrapy: Definir elementos dinámicamente

Cuando comencé a aprender sobre chatarra, me he encontrado con un requisito para construir dinámicamente los atributos del elemento. Solo estoy raspando una página web que tiene una estructura de tabla y quería formar los atributos del elemento y del campo mientras rastreaba. He analizado este ejemplo. Datos de raspado sin tener que definir explícitamente cada campo para ser raspado, pero no pude aprovechar mucho.

¿Debería estar escribiendo una línea de elementos para capturar la información dinámicamente? También he analizado la función del cargador de elementos, pero si alguien puede explicarlo en detalle, será de gran ayuda.

Simplemente use un solo campo como marcador de posición de datos arbitrarios. Y luego, cuando desea obtener los datos, en lugar de decir for field in item , diga for field in item['row'] . No necesita tuberías ni cargadores para realizar esta tarea, pero ambos se utilizan ampliamente por una buena razón: vale la pena aprenderlos.

araña:

 from scrapy.item import Item, Field from scrapy.spider import BaseSpider class TableItem(Item): row = Field() class TestSider(BaseSpider): name = "tabletest" start_urls = ('http://scrapy.org?finger', 'http://example.com/toe') def parse(self, response): item = TableItem() row = dict( foo='bar', baz=[123, 'test'], ) row['url'] = response.url if 'finger' in response.url: row['digit'] = 'my finger' row['appendage'] = 'hand' else: row['foot'] = 'might be my toe' item['row'] = row return item 

outptut

 stav@maia:/srv/stav/scrapie/oneoff$ scrapy crawl tabletest 2013-03-14 06:55:52-0600 [scrapy] INFO: Scrapy 0.17.0 started (bot: oneoff) 2013-03-14 06:55:52-0600 [scrapy] DEBUG: Overridden settings: {'NEWSPIDER_MODULE': 'oneoff.spiders', 'SPIDER_MODULES': ['oneoff.spiders'], 'USER_AGENT': 'Chromium OneOff 24.0.1312.56 Ubuntu 12.04 (24.0.1312.56-0ubuntu0.12.04.1)', 'BOT_NAME': 'oneoff'} 2013-03-14 06:55:53-0600 [scrapy] DEBUG: Enabled extensions: LogStats, TelnetConsole, CloseSpider, WebService, CoreStats, SpiderState 2013-03-14 06:55:53-0600 [scrapy] DEBUG: Enabled downloader middlewares: HttpAuthMiddleware, DownloadTimeoutMiddleware, UserAgentMiddleware, RetryMiddleware, DefaultHeadersMiddleware, MetaRefreshMiddleware, HttpCompressionMiddleware, RedirectMiddleware, CookiesMiddleware, ChunkedTransferMiddleware, DownloaderStats 2013-03-14 06:55:53-0600 [scrapy] DEBUG: Enabled spider middlewares: HttpErrorMiddleware, OffsiteMiddleware, RefererMiddleware, UrlLengthMiddleware, DepthMiddleware 2013-03-14 06:55:53-0600 [scrapy] DEBUG: Enabled item pipelines: 2013-03-14 06:55:53-0600 [tabletest] INFO: Spider opened 2013-03-14 06:55:53-0600 [tabletest] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min) 2013-03-14 06:55:53-0600 [scrapy] DEBUG: Telnet console listening on 0.0.0.0:6023 2013-03-14 06:55:53-0600 [scrapy] DEBUG: Web service listening on 0.0.0.0:6080 2013-03-14 06:55:53-0600 [tabletest] DEBUG: Crawled (200)  (referer: None) 2013-03-14 06:55:53-0600 [tabletest] DEBUG: Scraped from <200 http://scrapy.org?finger> {'row': {'appendage': 'hand', 'baz': [123, 'test'], 'digit': 'my finger', 'foo': 'bar', 'url': 'http://scrapy.org?finger'}} 2013-03-14 06:55:53-0600 [tabletest] DEBUG: Redirecting (302) to  from  2013-03-14 06:55:53-0600 [tabletest] DEBUG: Redirecting (302) to  from  2013-03-14 06:55:53-0600 [tabletest] DEBUG: Crawled (200)  (referer: None) 2013-03-14 06:55:53-0600 [tabletest] DEBUG: Scraped from <200 http://www.iana.org/domains/example> {'row': {'baz': [123, 'test'], 'foo': 'bar', 'foot': 'might be my toe', 'url': 'http://www.iana.org/domains/example'}} 2013-03-14 06:55:53-0600 [tabletest] INFO: Closing spider (finished) 2013-03-14 06:55:53-0600 [tabletest] INFO: Dumping Scrapy stats: {'downloader/request_bytes': 1066, 'downloader/request_count': 4, 'downloader/request_method_count/GET': 4, 'downloader/response_bytes': 3833, 'downloader/response_count': 4, 'downloader/response_status_count/200': 2, 'downloader/response_status_count/302': 2, 'finish_reason': 'finished', 'finish_time': datetime.datetime(2013, 3, 14, 12, 55, 53, 848735), 'item_scraped_count': 2, 'log_count/DEBUG': 13, 'log_count/INFO': 4, 'response_received_count': 2, 'scheduler/dequeued': 4, 'scheduler/dequeued/memory': 4, 'scheduler/enqueued': 4, 'scheduler/enqueued/memory': 4, 'start_time': datetime.datetime(2013, 3, 14, 12, 55, 53, 99635)} 2013-03-14 06:55:53-0600 [tabletest] INFO: Spider closed (finished) 

Utilice esta clase:

 class Arbitrary(Item): def __setitem__(self, key, value): self._values[key] = value self.fields[key] = {} 

La solución __setitem__ personalizada no me funcionó al usar los cargadores de elementos en Scrapy 1.0.3 porque el cargador de elementos accede directamente al atributo de los campos :

 value = self.item.fields[field_name].get(key, default) 

El __setitem__ personalizado solo se llama para accesos de nivel de item['new field'] como item['new field'] . Como los fields son solo un dictado , me di cuenta de que simplemente podría crear una subclase de ítem que usa un defaultdict para manejar con gracia estas situaciones.

Al final, solo dos líneas extra de código:

 from collections import defaultdict class FlexItem(scrapy.Item): """An Item that creates fields dynamically""" fields = defaultdict(scrapy.Field) 

Sé que mi respuesta es tardía, pero para aquellos que todavía necesitan elementos dynamics usando Scrapy, creé un repository en Github que incluye un ejemplo.

Aqui tienes

https://github.com/WilliamKinaan/ScrapyDynamicItems

En Scrapy 1.0+, la mejor manera podría ser producir dicts de Python en lugar de instancias de Item si no tiene un esquema bien definido. Verifique, por ejemplo, un ejemplo en http://scrapy.org/ front page – no hay un artículo definido.

Esperaba más la explicación en el manejo de los datos con los cargadores de elementos y las tuberías.

Asumiendo:

 fieldname = 'test' fieldxpath = '//h1' 

Es (en versiones recientes) muy simple …

 item = Item() l = ItemLoader(item=item, response=response) item.fields[fieldname] = Field() l.add_xpath(fieldname, fieldxpath) return l.load_item()