官术网_书友最值得收藏!

4.3 Field元數據

在第2章中曾講到,一項數據由Spider提交給Scrapy引擎后,可能會被遞送給其他組件(ItemPipeline、Exporter)處理。假設想傳遞額外信息給處理數據的某個組件(例如,告訴該組件應以怎樣的方式處理數據),此時可以使用Field的元數據。請看下面的例子:

        class ExampleItem(Item):
          x=Field(a='hello', b=[1, 2, 3])      #x有兩個元數據,a是個字符串,b是個列表
          y=Field(a=lambda x: x**2)      #y有一個元數據,a是個函數

訪問一個ExampleItem對象的fields屬性,將得到一個包含所有Field對象的字典:

        >>> e = ExampleItem(x=100, y=200)
        >>> e.fields
        {'x': {'a': 'hello', 'b': [1, 2, 3]},
        'y': {'a': <function __main__.ExampleItem.<lambda>>}}
        >>> type(e.fields['x'])
        scrapy.item.Field
        >>> type(e.fields['y'])
        scrapy.item.Field

實際上,Field是Python字典的子類,可以通過鍵獲取Field對象中的元數據:

        >>> issubclass(Field, dict)
        True
        >>>field_x=e.fields['x']        # 注意,不要混淆e.fields['x']和e['x']
        >>> field_x
        {'a': 'hello', 'b': [1, 2, 3]}
        >>> field_x['a']
        'hello'
        >>> field_y = e.fields['y']
        >>> field_y
        {'a': <function __main__.ExampleItem.<lambda>>}
        >>> field_y.get('a', lambda x: x)
        <function __main__.ExampleItem.<lambda>>

接下來,看一個應用Field元數據的實際例子。假設我們要把爬取到的書籍信息寫入csv文件,那每一項數據最終由Scrapy提供的CsvItemExporter寫入文件(數據導出在第7章詳細講解),在爬取過程中提取到的信息并不總是一個字符串,有時可能是一個字符串列表,例如:

        >>> book['authors'] = [’李雷’, ’韓梅梅’, ’吉姆’]

但在寫入csv文件時,需要將列表內所有字符串串行化成一個字符串,串行化的方式有很多種,例如:

        1.’李雷|韓梅梅|吉姆’                 #'|'.join(book['authors'])
        2.’李雷;韓梅梅;吉姆’                 #'; '.join(book['authors'])
        3. "[’李雷’,’韓梅梅’,’吉姆’]"    #str(book['authors'])

我們可以通過authors字段的元數據告訴CsvItemExporter如何對authors字段串行化:

        class BookItem(Item):
          ...
          authors = Field(serializer=lambda x: '|'.join(x))
          ...

其中,元數據的鍵serializer是CsvItemExporter規定好的,它會用該鍵獲取元數據,即一個串行化函數對象,并使用這個串行化函數將authors字段串行化成一個字符串。以下是Scrapy源碼中的相關實現:

        # exports.py

        class BaseItemExporter(object):
          ...

          def _get_serialized_fields(self, item, default_value=None, include_empty=None):
              ...
              for field_name in field_iter:
                if field_name in item:
                    field = {} if isinstance(item, dict) else item.fields[field_name]
                    value = self.serialize_field(field, field_name, item[field_name])
                else:
                    value = default_value
                yield field_name, value
          ...

        class CsvItemExporter(BaseItemExporter):

          ...

          def export_item(self, item):
              ...
              fields = self._get_serialized_fields(item, default_value='',
                                        include_empty=True)
              values = list(self._build_row(x for _, x in fields))
              self.csv_writer.writerow(values)

          ...

          def serialize_field(self, field, name, value):
              serializer = field.get('serializer', self._join_if_needed)
              return serializer(value)

          ...

解釋上述代碼如下:

● 爬取到的每一項數據由export_item方法導出到文件,寫入文件之前,先調用_get_serialized_fields方法(在基類中實現)獲得數據中每個字段串行化的結果。

● 在_get_serialized_fields方法中調用serialize_field方法,獲取其中一個字段串行化的結果。

● 在serialize_field方法中獲取字段的元數據serializer,得到串行化函數(如果不存在,就使用默認的_join_if_needed函數),最終調用該函數對字段串行化,并將結果返回。

在實際應用中,我們可以仿照上面的例子靈活使用Field元數據。

主站蜘蛛池模板: 宕昌县| 临高县| 元阳县| 古蔺县| 简阳市| 石棉县| 日喀则市| 巴中市| 四会市| 合山市| 株洲县| 江西省| 泰宁县| 宁晋县| 华池县| 诸暨市| 临安市| 抚宁县| 福贡县| 三台县| 宝清县| 商洛市| 聊城市| 临武县| 乌拉特后旗| 白河县| 太原市| 凭祥市| 佛坪县| 宜宾市| 陇南市| 麦盖提县| 昌图县| 商水县| 平昌县| 宁远县| 云浮市| 黄龙县| 邵东县| 定日县| 隆化县|