2013-03-13 2 views
12

Когда я начал изучать scrapy, я столкнулся с требованием динамически строить атрибуты Item. Я просто очищаю веб-страницу, которая имеет структуру таблицы, и я хотел создать атрибуты элемента и поля во время обхода. Я прошел через этот пример Scraping data without having to explicitly define each field to be scraped, но не смог многое сделать.Scrapy: динамически определять элементы

Должен ли я писать элемент pipleline для записи информации динамически. Я также посмотрел на функцию Loader Item, но если кто-нибудь может объяснить подробно, это будет действительно полезно.

+0

Если вы могли бы привести пример структуры таблицы и как она может меняться, это облегчит ответ на этот вопрос. – Talvalin

+0

Talvalin, он не зависит от структуры таблицы, я хотел бы добавлять элементы и динамически отправлять их в конвейер, т. Е. Не указывая их явно в классе items.py. оцените ваш ответ. – Srikanth

+2

У меня было больше успеха с использованием подкласса Item, переопределяющего 'self.fields' с defaultdict [как показано в этом ответе] (http://stackoverflow.com/a/24357273/149872). – elias

ответ

9

Просто используйте одно поле в качестве произвольного заполнителя данных. И затем, когда вы хотите получить данные, вместо того, чтобы говорить for field in item, вы говорите for field in item['row']. Для выполнения этой задачи вам не нужны pipelines или loaders, но они оба широко используются по уважительной причине: они заслуживают изучения.

паук:

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

хотя .. Я больше разбирался в пояснениях при обработке данных с помощью погрузчиков и конвейеров ... Это - workaorund, чтобы заставить вещи двигаться ... спасибо в любом случае .. – Srikanth

7

Используйте этот класс:

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

Я был более xpecting об объяснении в обработке данных с п погрузчиков и трубопроводов

Предполагая:

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

Это (в последних версиях) очень просто ...

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

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

return l.load_item() 
2

Я знаю, что мой ответ запаздывает, но и для тех, кто все еще нужны динамические элементы с помощью Scrapy, я создал репозиторий на Github, включая пример.

Держи

https://github.com/WilliamKinaan/ScrapyDynamicItems

+0

вы поможете мне сэкономить массу времени. Обратите внимание: если в items.py вы используете «import scrapy», тогда вы должны использовать «scrapy.Field()» в функции __setitem__ –

3

Решение пользовательских __setitem__ не работает для меня, когда с помощью элемента погрузчики в Scrapy 1.0.3 потому что деталь погрузчик accesses the fields attribute directly:

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

Обычай __setitem__ предназначен только для доступа на уровне позиции, например, item['new field']. Поскольку fields - just a dict, я понял, что могу просто создать подкласс Item, который использует defaultdict, чтобы изящно обрабатывать эти ситуации.

В конце концов, только две дополнительные строки кода:

from collections import defaultdict 


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

Спасибо, я тоже столкнулся с этой проблемой. Чтобы быть ясным (я знаю, что вы сказали * дополнительно *), вам нужно также сохранить переопределение '__setitem__' в дополнение к добавлению этого defaultdict. – fpghost

2

В Scrapy 1.0+ лучший способ может быть, чтобы получить Python dicts вместо экземпляров элемента, если у вас нет четко определенной схемы , Проверьте, например. пример на главной странице http://scrapy.org/ - нет определенного предмета.

Смежные вопросы