2013-03-13 24 views
12

Kiedy zacząłem uczyć się scrapy, przeszedłem przez wymóg dynamicznego budowania atrybutów przedmiotów. Właśnie skrobię stronę internetową, która ma strukturę tabeli i chciałem utworzyć elementy i atrybuty pól podczas indeksowania. Przeszedłem przez ten przykład Scraping data without having to explicitly define each field to be scraped, ale nie mogłem zrobić wiele.Złomowanie: Zdefiniuj elementy dynamicznie

Czy powinienem pisać pipsy przedmiotów, aby przechwytywać informacje dynamicznie. Przyjrzałem się także funkcji modułu ładującego elementy, ale jeśli ktoś może wyjaśnić szczegółowo, będzie to naprawdę pomocne.

+0

Jeśli mógłbyś podać przykład struktury tabeli i jej różne możliwości, łatwiej byłoby odpowiedzieć na to pytanie. – Talvalin

+0

Talvalin, jest niezależny od struktury tabeli, chciałbym dodać elementy i wysłać je do potoku dynamicznie tj. Bez konieczności jednoznacznego ich zdefiniowania w klasie items.py. doceń swoją odpowiedź. – Srikanth

+2

Miałem więcej sukcesów używając podklasy Item nadpisując 'self.fields' z defaultdict [jak pokazano w tej odpowiedzi] (http://stackoverflow.com/a/24357273/149872). – elias

Odpowiedz

9

Wystarczy użyć pojedynczego pola jako dowolnego symbolu zastępczego danych. A kiedy chcesz uzyskać dane, zamiast powiedzieć for field in item, mówisz for field in item['row']. Aby wykonać to zadanie, nie potrzebujesz pipelines ani loaders, ale oba są szeroko stosowane z nie bez powodu: warto je poznać.

pająk:

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

chociaż ... Byłem bardziej przekonany o wyjaśnieniu w obsłudze danych za pomocą ładowarek i rurociągów ... To jest pracochłonne, aby wszystko się ruszało ... dzięki. – Srikanth

7

Użyj tej klasy:

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

byłem bardziej xpecting o wyjaśnienia w zakresie obsługi danych z ładowarki element i rurociągów

Zakładając:

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

To (w nowszych wersjach) bardzo proste ...

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

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

return l.load_item() 
2

wiem, że moja odpowiedź jest późno, ale dla tych, którzy wciąż potrzebują dynamicznych elementów za pomocą Scrapy, stworzyłem repozytorium na Github, w tym przykład.

Tu idziesz

https://github.com/WilliamKinaan/ScrapyDynamicItems

+0

pomagasz mi zaoszczędzić mnóstwo czasu. Uwaga: jeśli w items.py używasz "import scrapy", musisz użyć "scrapy.Field()" w funkcji __setitem__ –

3

Rozwiązanie zwyczaj __setitem__ nie działa na mnie przy użyciu ładowarki przedmiot w Scrapy 1.0.3 ponieważ ładowarka poz accesses the fields attribute directly:

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

Zwyczaj __setitem__ jest wywoływana tylko dla dostępu na poziomie pozycji, takiego jak item['new field']. Od fields jest just a dict, zdałem sobie sprawę, że mogę po prostu utworzyć podklasy Item, która używa defaultdict, aby zręcznie obsługiwać te sytuacje.

W końcu, zaledwie dwie dodatkowe linie kodu:

from collections import defaultdict 


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

Dzięki, natknąłem się również na ten problem. Żeby było jasne (wiem, że powiedziałeś * ekstra *), oprócz dodania tego defaultdict musisz także zachować nadpisanie '__setitem__''. – fpghost

2

W Scrapy 1.0+ lepszy sposób może być uzyskując dicts Python zamiast instancji element, jeśli nie ma dobrze zdefiniowanego schematu . Sprawdź np. przykład na pierwszej stronie http://scrapy.org/ - nie ma zdefiniowanego elementu.

Powiązane problemy