初试scrapy + redis 爬虫
2018/5/17大约 2 分钟约 521 字
前言
我的思路是这样的,master跑redis,mysql,spider1。slave就只跑spider2。这样子能做到分布式处理。 spider1负责维护在redis上增量更新的待爬取队列。spider2则分布式的处理队列,爬取并处理每一个网页,处理完毕后上传数据到mysql。 master服务器性能羸弱,这样做其实是无奈之举,若学校能提供更强大的服务器就好了。
代码及需求
需求1:分布式处理spider1的item
scrapy-redis 默认是读取url 队列,而我要处理的是json队列。几番Google未果,故直接看源码。发现作者早就想到了这个问题。RedisMixin这个类里面就可以看到。
def make_request_from_data(self, data):
"""Returns a Request instance from data coming from Redis.
By default, ``data`` is an encoded URL. You can override this method to
provide your own message decoding.
Parameters
data : bytes
Message from redis.
"""
url = bytes_to_str(data, self.redis_encoding)
return self.make_requests_from_url(url)我现在只需要override这个方法就行了。 对了,还有一个窍门,如果想给请求的回调函数传参的话呢,网上很多人用lambda,我Google到可以从meta传入。在回调函数的response.request.meta就可以取到参数了。
def make_request_from_data(self, data):
data = eval(data) # FIXME:有安全问题
return Request(
method='GET',
url=data['url'],
meta={
'title': data['title'],
'resource_id': data['resource_id'],
'type_id': data['type_id']
},
callback=self.parse_text,
dont_filter=True
)需求2:url去重
这个可以利用redis的哈希表,写一个PIPELINE来过滤掉重复的item。
# -*- coding: utf-8 -*-
import redis
from .settings import REDIS_PORT, REDIS_HOST
from scrapy.exceptions import DropItem
pool = redis.ConnectionPool(
host=REDIS_HOST,
port=REDIS_PORT,
decode_responses=True
)
r = redis.Redis(connection_pool=pool)
class GdprojectPipeline(object):
def process_item(self, item, spider):
# item的hash已存在表中,dropitem
if r.hexists("gdggzy_project_hash", str(item['resource_id'])):
raise DropItem("Duplicate item found:%s" % item['resource_id'])
# item的hash不存在,添加hash到表中
r.hset("gdggzy_project_hash", str(item['resource_id']), 0)
return item然后在设置里面添加自己的pipeline
ITEM_PIPELINES = {
'gdproject.pipelines.GdprojectPipeline': 300,
。。。。。。。
}