2013-03-13 16 views
12

Als ich anfing, scrapy zu lernen, bin ich über die Anforderung gekommen, die Item-Attribute dynamisch zu erstellen. Ich kratze nur eine Webseite, die eine Tabellenstruktur hat und ich wollte die Artikel- und Feldattribute beim Crawlen bilden. Ich habe dieses Beispiel durchgespielt Scraping data without having to explicitly define each field to be scraped aber konnte nicht viel davon machen.Scrapy: Objekte dynamisch definieren

Sollte ich ein Element Pipeline schreiben, um die Informationen dynamisch zu erfassen. Ich habe auch die Loader-Funktion von Item betrachtet, aber wenn jemand im Detail erklären kann, wird es sehr hilfreich sein.

+0

Wenn Sie ein Beispiel für die Struktur der Tabelle geben könnten und wie sie variieren könnte, würde diese Frage leichter zu beantworten sein. – Talvalin

+0

Talvalin, es ist unabhängig von der Tabellenstruktur, ich würde Elemente hinzufügen und es dynamisch an die Pipeline senden, d. H. Ohne sie explizit in der items.py-Klasse definieren zu müssen. schätze deine Antwort. – Srikanth

+2

Ich hatte mehr Erfolg mit einer Item-Unterklasse überschreiben 'self.fields' mit einem defaultdict [wie in dieser Antwort gezeigt] (http://stackoverflow.com/a/24357273/149872). – elias

Antwort

9

Verwenden Sie einfach ein einzelnes Feld als beliebigen Datenplatzhalter. Und dann, wenn Sie die Daten erhalten möchten, anstatt for field in item zu sagen, sagen Sie for field in item['row']. Sie benötigen keine pipelines oder loaders, um diese Aufgabe zu erfüllen, aber sie werden beide aus gutem Grund ausgiebig verwendet: Sie sind es wert, gelernt zu werden.

Spinne:

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:

[email protected]:/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) <GET http://scrapy.org?finger> (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 <GET http://www.iana.org/domains/example/> from <GET http://example.com/toe> 
2013-03-14 06:55:53-0600 [tabletest] DEBUG: Redirecting (302) to <GET http://www.iana.org/domains/example> from <GET http://www.iana.org/domains/example/> 
2013-03-14 06:55:53-0600 [tabletest] DEBUG: Crawled (200) <GET http://www.iana.org/domains/example> (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) 
+0

obwohl .. Ich war mehr über Erklärung im Umgang mit den Daten mit Artikel Lader und Pipelines ... Dies ist ein Workaorund, um Dinge in Bewegung zu bringen ... trotzdem danke .. – Srikanth

7

Verwenden Sie diese Klasse:

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

ich mehr wurde über Erklärung xpecting die Daten mit Artikel Lader in der Handhabung und Pipelines

Unter der Annahme:

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

Es ist (in neueren Versionen) sehr einfach ...

item = Item() 
l = ItemLoader(item=item, response=response) 

item.fields[fieldname] = Field() 
l.add_xpath(fieldname, fieldxpath) 

return l.load_item() 
2

Ich weiß, dass meine Antwort spät, aber für diejenigen, die brauchen immer noch ein dynamischen Elemente mit Scrapy habe ich ein Repository auf Github erstellt, mit einem Beispiel.

Hier gehen Sie

https://github.com/WilliamKinaan/ScrapyDynamicItems

+0

Sie mir helfen, eine Menge Zeit zu sparen. Hinweis: Wenn Sie in items.py 'import scrapy' verwenden, dann müssen Sie 'scrapy.Field()' in __setitem__ function verwenden –

3

Die benutzerdefinierte __setitem__ Lösung nicht für mich arbeiten, wenn sie in Scrapy 1.0.3 Artikel Lader verwenden, da die Artikel loader accesses the fields attribute directly:

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

Der Brauch __setitem__ wird nur für Zugriffe auf Elementebene wie item['new field'] aufgerufen. Da fieldsjust a dict ist, erkannte ich, dass ich einfach eine Item-Unterklasse erstellen konnte, die defaultdict verwendet, um diese Situationen elegant zu behandeln.

Am Ende nur zwei zusätzliche Zeilen Code:

from collections import defaultdict 


class FlexItem(scrapy.Item): 
    """An Item that creates fields dynamically""" 
    fields = defaultdict(scrapy.Field) 
+1

Danke, ich bin auch auf dieses Problem gestoßen. Nur um klar zu sein (ich weiß, dass du * extra * gesagt hast), musst du zusätzlich zum Hinzufügen dieses Standarddict auch die Überschreibung "__setitem__" beibehalten. – fpghost

2

In Scrapy 1.0+ der besseren Weg sein könnte Python dicts anstelle von Item-Instanzen zu erhalten, wenn Sie ein gut definiertes Schema nicht haben . Überprüfen z.B. ein Beispiel auf http://scrapy.org/ Titelseite - es ist kein Artikel definiert.

Verwandte Themen