一、ItemLoaderItem的区别

    • ItemLoader是负责数据的收集、处理、填充,item仅仅是承载了数据本身
    • 数据的收集、处理、填充归功于item loader中两个重要组件:
      • 输入处理input processors
      • 输出处理output processors

二、ItemLoader的使用

  • 1、创建一个项目并创建一个爬虫
  • 2、在item.py中使用

import redis
import scrapy
from scrapy.loader import ItemLoader
from scrapy.loader.processors import MapCompose, TakeFirst, Join
from w3lib.html import remove_tags from utils.common import extract_num def add_jobbole(value):
return value + 'zhangyafei' def date_convert(value):
try:
value = value.strip().replace('·', '').strip()
create_date = datetime.datetime.strptime(value, "%Y/%m/%d").date()
except Exception as e:
create_date = datetime.datetime.now().date
return create_date def get_nums(value):
try:
if re.match('.*?(\d+).*', value).group(1):
nums = int(re.match('.*?(\d+).*', value).group(1))
else:
nums = 0
except:
nums = 0
return nums def remove_comment_tags(value):
if "评论" in value:
return ''
return value def return_value(value):
return value def gen_suggests(index, info_tuple):
# 根据字符串生成搜索建议数组
used_words = set()
suggests = []
for text, weight in info_tuple:
if text:
# 调用es的analyze接口分析字符串
words = es.indices.analyze(index=index, analyzer="ik_max_word", params={'filter': ["lowercase"]}, body=text)
anylyzed_words = set([r["token"] for r in words["tokens"] if len(r["token"]) > 1])
new_words = anylyzed_words - used_words
else:
new_words = set() if new_words:
suggests.append({"input": list(new_words), "weight": weight}) return suggests class ArticleItemLoader(ItemLoader):
# 自定义itemloader
default_output_processor = TakeFirst() class JobboleArticleItem(scrapy.Item):
title = scrapy.Field()
create_date = scrapy.Field(
input_processor=MapCompose(date_convert),
)
url = scrapy.Field()
url_object_id = scrapy.Field()
front_image_url = scrapy.Field(
output_processor=MapCompose(return_value)
)
front_image_path = scrapy.Field()
praise_nums = scrapy.Field(
input_processor=MapCompose(get_nums)
)
comment_nums = scrapy.Field(
input_processor=MapCompose(get_nums)
)
fav_nums = scrapy.Field(
input_processor=MapCompose(get_nums)
)
tags = scrapy.Field(
input_processor=MapCompose(remove_comment_tags),
output_processor=Join(",")
)
content = scrapy.Field() def get_insert_sql(self):
insert_sql = """
insert into jobbole_article(title, url, create_date, fav_nums)
VALUES (%s, %s, %s, %s) ON DUPLICATE KEY UPDATE content=VALUES(fav_nums)
"""
params = (self["title"], self["url"], self["create_date"], self["fav_nums"]) return insert_sql, params def save_to_es(self):
article = ArticleType()
article.title = self['title']
article.create_date = self["create_date"]
article.content = remove_tags(self["content"])
article.front_image_url = self["front_image_url"]
if "front_image_path" in self:
article.front_image_path = self["front_image_path"]
article.praise_nums = self["praise_nums"]
article.fav_nums = self["fav_nums"]
article.comment_nums = self["comment_nums"]
article.url = self["url"]
article.tags = self["tags"]
article.meta.id = self["url_object_id"] article.suggest = gen_suggests(ArticleType._doc_type.index, ((article.title, 10), (article.tags, 7))) article.save() redis_cli.incr("jobbole_count") return

 spider中的使用

 def parse(self, response):
"""
1.获取文章列表页的文章url交给scrapy下载后并进行解析
2.获取下一页的url交给scrapy进行下载,下载完成后交给parse解析
"""
"""
解析文章列表页中的所有文章url交给scrapy下载并进行解析
"""
if response.status == 404:
self.fail_urls.append(response.url)
self.crawler.stats.inc_value("failed_urls") post_nodes = response.css('#archive .post-thumb a')
for post_node in post_nodes:
img_url = post_node.css('img::attr(src)').extract_first()
# img_url = [img_url if 'http:' in img_url else ('http:' + img_url)]
post_url = post_node.css('::attr(href)').extract_first()
yield scrapy.Request(url=parse.urljoin(response.url, post_url), meta={'img_url': img_url},
callback=self.parse_detail)
next_url = response.css('.next.page-numbers::attr(href)').extract_first()
# 获取下一页的url交给scrapy下载并进行解析
if next_url:
yield scrapy.Request(url=next_url, callback=self.parse) def parse_detail(self, response):
# 通过item loader加载item
front_image_url = response.meta.get("front_image_url", "") # 文章封面图
item_loader = ArticleItemLoader(item=JobboleArticleItem(), response=response)
item_loader.add_css("title", ".entry-header h1::text")
item_loader.add_value("url", response.url)
item_loader.add_value("url_object_id", get_md5(response.url))
item_loader.add_css("create_date", "p.entry-meta-hide-on-mobile::text")
item_loader.add_value("front_image_url", [front_image_url])
item_loader.add_css("praise_nums", ".vote-post-up h10::text")
item_loader.add_css("comment_nums", "a[href='#article-comment'] span::text")
item_loader.add_css("fav_nums", ".bookmark-btn::text")
item_loader.add_css("tags", "p.entry-meta-hide-on-mobile a::text")
item_loader.add_css("content", "div.entry") article_item = item_loader.load_item() yield article_item

三、常见的内置处理器

  • 1、Identity

    不对数据进行处理,直接返回原来的数据

  • 2、TakeFirst

    返回第一个非空值,常用于单值字段的输出处理

  • 3、Join

    相当于把列表中的元素拼接起来

  • 4、MapCompose把几个方法组合起来

四、数据清洗方法详解

processor

scrapy提供了一个processors类,里面有下列几种方法:Join,TakeFirst,MapCompose,Compose,Identity,SelectJmes

对这几种方法的用法简单介绍一下:

from scrapy.loader.processors import Join,TakeFirst,MapCompose,Compose,Identity,SelectJmes

#以特定字符连接,示例以空连接,对字符串也能操作
c = Join('')
c(['a','b'])
>>>'ab'
#******************** #传入函数的列表的每一个元素都会经过第一个函数,
#得到值在经过第二个函数,如果有返回值为None的,则抛弃,
#最后返回一个列表
c=MapCompose(str.strip,str.upper)
c([' a ','b'])
>>>['A', 'B']
#******************** #如果传入一个列表时则会报下面这个错误
#descriptor 'strip' requires a 'str' object but received a 'list'
#但如果Compose的第一个函数是取列表的第一个元素,不会报错
#即Compose是处理单一数据,MapCompose是批量处理
c=Compose(str.strip,str.upper)
c(' ac ')
>>>'AC'
#******************** #拿到JSON格式数据时会有作用
proc = SelectJmes('a')
proc({'a':'b','c':'d'})
>>>'b'

input--output

Item Loader 为每个 Item Field 单独提供了一个 Input processor 和一个 Output processor;

Input processor 一旦它通过 add_xpath()add_css()add_value() 方法收到提取到的数据便会执行,执行以后所得到的数据将仍然保存在 ItemLoader 实例中;当数据收集完成以后,ItemLoader 通过 load_item() 方法来进行填充并返回已填充的 Item 实例。

即input_processor是在收集数据的过程中所做的处理,output_processor是数据yield之后进行的处理,通过下面这个例子会更加理解:

#type字段取出来时是'type': ['2室2厅', '中楼层/共6层']

#定义一个在第一个元素后面加a的函数
def adda(value):
return value[0]+'a' type = scrapy.Field(output_processor = Compose(adda))
>>>'type': '2室2厅a' type = scrapy.Field(input_processor = Compose(adda))
>>>'type': ['2室2厅a', '中楼层/共6层a']
#如果使用MapCompose的话,两个结果会一样,这也是Compose和MapCompose的区别

当指定了取列表的第一个元素后,有些信息想保留整个列表便可以使用name_out,Identity()是取自身的函数。

class TeItem(ItemLoader):
default_out_processor = TakeFirst()
name_out = Identity()

也可以在基于scrapy.Item的item中定义一些规则:

class Scrapy1Item(scrapy.Item):
name = scrapy.Field(output_processor=Identity())

优先级

scrapy提供了很多种方式去自定义输入输出的内容,具有一定的优先级,优先级最高的是name_out这种,其次是在scrapy.Field()中定义的output_processor和input_processor,最后是default_out_processor = TakeFirst()这种。

Scrapy的Item_loader机制详解的更多相关文章

  1. 从mixin到new和prototype:Javascript原型机制详解

    从mixin到new和prototype:Javascript原型机制详解   这是一篇markdown格式的文章,更好的阅读体验请访问我的github,移动端请访问我的博客 继承是为了实现方法的复用 ...

  2. 浏览器 HTTP 协议缓存机制详解

    最近在准备优化日志请求时遇到了一些令人疑惑的问题,比如为什么响应头里出现了两个 cache control.为什么明明设置了 no cache 却还是发请求,为什么多次访问时有时请求里带了 etag, ...

  3. JVM的垃圾回收机制详解和调优

    JVM的垃圾回收机制详解和调优 gc即垃圾收集机制是指jvm用于释放那些不再使用的对象所占用的内存.java语言并不要求jvm有gc,也没有规定gc如何工作.不过常用的jvm都有gc,而且大多数gc都 ...

  4. ThreadPoolExecutor运转机制详解

    ThreadPoolExecutor运转机制详解 - 走向架构师之路 - 博客频道 - CSDN.NET 最近发现几起对ThreadPoolExecutor的误用,其中包括自己,发现都是因为没有仔细看 ...

  5. Linux 内存机制详解宝典

    Linux 内存机制详解宝典 在linux的内存分配机制中,优先使用物理内存,当物理内存还有空闲时(还够用),不会释放其占用内存,就算占用内存的程序已经被关闭了,该程序所占用的内存用来做缓存使用,对于 ...

  6. PHP的垃圾回收机制详解

    原文:PHP的垃圾回收机制详解 最近由于使用php编写了一个脚本,模拟实现了一个守护进程,因此需要深入理解php中的垃圾回收机制.本文参考了PHP手册. 在理解PHP垃圾回收机制(GC)之前,先了解一 ...

  7. Java 反射 设计模式 动态代理机制详解 [ 转载 ]

    Java 反射 设计模式 动态代理机制详解 [ 转载 ] @author 亦山 原文链接:http://blog.csdn.net/luanlouis/article/details/24589193 ...

  8. Android事件分发机制详解

    事件分发机制详解 一.基础知识介绍 1.经常用的事件有:MotionEvent.ACTION_DOWN,MotionEvent.ACTION_MOVE,MotionEvent.ACTION_UP等 2 ...

  9. Android Binder机制详解:手写IPC通信

    想要掌握一样东西,最好的方式就是阅读理解它的源码.想要掌握Android Binder,最好的方式就是写一个AIDL文件,然后查看其生成的代码.本文的思路也是来自于此. 简介 Binder是Andro ...

随机推荐

  1. RocketMQ架构原理解析(三):消息索引

    一.概述 "索引"一种数据结构,帮助我们快速定位.查询数据 前文我们梳理了消息在Commit Log文件的存储过程,讨论了消息的落盘策略,然而仅仅通过Commit Log存储消息是 ...

  2. 代码仓库gogs的基本配置使用

    目录 一.基本功能介绍 主板说明 页面说明 用户设置 二.仓库 新建仓库 迁移仓库 仓库介绍 三.组织和团队 创建新组织 创建团队 一.基本功能介绍 主板说明 图中1表示自己个人账户下的仓库(所有权属 ...

  3. 【划重点】Python matplotlib绘图建立画布和坐标系

    一.建立画布 import matplotlib.pyplot as plt import numpy as np x=np.arange(8) y=np.arange(8) print(x,y) # ...

  4. 转:ios delegate

    首先,大家应该都明白的是委托是协议的一种,顾名思义,就是委托他人帮自己去做什么事.也就是当自己做什么事情不方便的时候,就可以建立一个委托,这样就可以委托他人帮自己去实现什么方法. 其次,我简单的总结了 ...

  5. [BUUCTF]REVERSE——[ACTF新生赛2020]easyre

    [ACTF新生赛2020]easyre 附件 步骤 查壳,32位程序,upx壳儿 脱完壳儿,扔进ida 分析 一开始给我们定义了一个数组, v4=[42,70,39,34,78,44,34,40,73 ...

  6. velocity使用foreach进行遍历时$velocityCount不起作用

    把$velocityCount替换成$foreach.count 例如 #if($foreach.count != $columns.size()),#end

  7. JAVA实现QQ第三方登录

    首先在QQ互联: https://connect.qq.com/manage.html 申请账号,并且进行资料审核,同时创建应用(设置回调地址) 申请应用完后,会有app_ID.app_KEY等参数 ...

  8. js中的jQuery Validate增加手机号码验证

    $.validator.addMethod("isPhone", function(value,element) { var length = value.length; var ...

  9. JAVA获取某年(当年)的第一天的开始时刻和某年(当年)的最后一天的最后时刻

    package com.date; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Dat ...

  10. JAVA获取当前日期所在的周一到周日的所有日期集合

    /** * 获取当前日期所在的周一到周日的所有日期集合 * @return */ public static List<Date> getWeekDateList() { Calendar ...