真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Python自動(dòng)化開發(fā)學(xué)習(xí)-分布式爬蟲(scrapy-redis)

scrapy-redis

講師的博客:https://www.cnblogs.com/wupeiqi/p/6912807.html
scrapy-redis是一個(gè)基于redis的scrapy組件,通過它可以快速實(shí)現(xiàn)簡單分布式爬蟲程序,該組件本質(zhì)上提供了三大功能:

創(chuàng)新互聯(lián)專注于桂東企業(yè)網(wǎng)站建設(shè),響應(yīng)式網(wǎng)站開發(fā),商城網(wǎng)站建設(shè)。桂東網(wǎng)站建設(shè)公司,為桂東等地區(qū)提供建站服務(wù)。全流程定制網(wǎng)站建設(shè),專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務(wù)

  • scheduler : 調(diào)度器
  • dupefilter : URL去重規(guī)則(被調(diào)度器使用)
  • pipeline : 數(shù)據(jù)持久化

準(zhǔn)備工作

安裝模塊

pip install scrapy-redis

創(chuàng)建爬蟲應(yīng)用
項(xiàng)目就不重新創(chuàng)建了,直接在之前Scrapy課程的項(xiàng)目里,再創(chuàng)建一個(gè)新的應(yīng)用:

> cd PeppaScrapy
> scrapy genspider [項(xiàng)目名稱] [起始url]

通過環(huán)境變量指定配置文件
之前的課程上,已經(jīng)對配置文件做了一些設(shè)置了。這里既不想把之前的內(nèi)容覆蓋掉,也不想受到之前配置的影響。
可以通過命令行的-s參數(shù)或者是在類里寫一個(gè)名稱為custom_settings的字典,一個(gè)一個(gè)參數(shù)的進(jìn)行設(shè)置。這兩個(gè)的優(yōu)先級(jí)都很高。但是都不是配置文件的形式,這里可以單獨(dú)再寫一個(gè)配置文件,然后通過設(shè)置系統(tǒng)環(huán)境變量的方式來指定爬蟲應(yīng)用加載的配置文件。
在原來的settings.py的同級(jí)目錄里創(chuàng)建一個(gè)mysettings.py的配置文件,文件可以在任意位置,只要能配python導(dǎo)入。然后設(shè)置一個(gè)系統(tǒng)環(huán)境變量即可:

import os
os.environ.setdefault('SCRAPY_SETTINGS_MODULE', 'PeppaScrapy.mysettings')

連接redis設(shè)置

把設(shè)置都寫在配置文件中即可:

# redis 配置文件
REDIS_HOST = 'localhost'  # 主機(jī)名
REDIS_PORT = 6379  # 端口
# REDIS_URL = 'redis://user:pass@hostname:9001'  # 連接URL,和上面2條一樣,也是連接redis的設(shè)置,優(yōu)先使用這個(gè)
# REDIS_PARAMS  = {}  # Redis連接參數(shù),這里有一些默認(rèn)值,可以去源碼里看
# REDIS_PARAMS['redis_cls'] = 'myproject.RedisClient'  # 指定連接Redis的Python模塊,默認(rèn):redis.StrictRedis
# REDIS_ENCODING = "utf-8"  # redis編碼類型,默認(rèn):'utf-8'

連接參數(shù)
這里提供兩種方式連接,一種是前2個(gè)設(shè)置,指定HOST和PORT。如果還需要用戶名和密碼就沒辦法了。
另一種就是用一個(gè)REDIS_URL參數(shù),按照上面的格式把所有的信息填好。另外這里REDIS_URL參數(shù)的優(yōu)先級(jí)高,就是說如果設(shè)置了REDIS_URL參數(shù),那么上面的2個(gè)參數(shù)就沒有效果了。這個(gè)邏輯可以在scrapy_redis.connection.py里的get_redis函數(shù)里找到。

其他連接參數(shù)
REDIS_PARAMS,是連接redis時(shí)使用的其他參數(shù)。所以其實(shí)用戶名,密碼也是可以寫在這里面的。即使什么都不寫,scrapy-redis模塊本身也設(shè)置了一些默認(rèn)值,可以在scrapy_redis.defaults.py里找到:

REDIS_PARAMS = {
    'socket_timeout': 30,
    'socket_connect_timeout': 30,
    'retry_on_timeout': True,
    'encoding': REDIS_ENCODING,  // 這個(gè)值默認(rèn)是'utf-8',也在這個(gè)文件里
}

具體還可以設(shè)置哪些參數(shù),就是看連接Redis的那個(gè)類的構(gòu)造函數(shù)了,源碼里就是用這個(gè)字典直接**kwargs創(chuàng)建對象了。默認(rèn)的用來連接Redis的模塊是redis.StrictRedis,下面是這個(gè)類的構(gòu)造函數(shù)的參數(shù)列表:

    def __init__(self, host='localhost', port=6379,
                 db=0, password=None, socket_timeout=None,
                 socket_connect_timeout=None,
                 socket_keepalive=None, socket_keepalive_options=None,
                 connection_pool=None, unix_socket_path=None,
                 encoding='utf-8', encoding_errors='strict',
                 charset=None, errors=None,
                 decode_responses=False, retry_on_timeout=False,
                 ssl=False, ssl_keyfile=None, ssl_certfile=None,
                 ssl_cert_reqs='required', ssl_ca_certs=None,
                 max_connections=None):

REDIS_ENCODING,是指定編碼類型,默認(rèn)utf-8沒太多要說的。
用于連接redis的所有參數(shù),除了以下4個(gè)是單獨(dú)寫的,其他的都寫在REDIS_PARAMS這個(gè)字典里。按照上面構(gòu)造函數(shù)里的變量名稱寫。這4個(gè)可以單獨(dú)寫的,其實(shí)也就是做了一步映射而已:

# 這個(gè)也是 scrapy_redis.connection.py 里的源碼
SETTINGS_PARAMS_MAP = {
    'REDIS_URL': 'url',
    'REDIS_HOST': 'host',
    'REDIS_PORT': 'port',
    'REDIS_ENCODING': 'encoding',
}

REDIS_PARAMS['redis_cls'],指定連接Redis的Python模塊。按上面說的,處理那4個(gè)參數(shù),其他的都只能寫在REDIS_PARAMS這個(gè)字典里。
源碼文件
上面這些主要是翻了3個(gè)文件里的源碼:

  • scrapy_redis.defaults.py
  • scrapy_redis.connection.py
  • redis.client.py

URL去重

模塊提供了一個(gè)使用redis做去重的規(guī)則,只需要按之前在Scrapy課程里學(xué)的,設(shè)置自定的去重規(guī)則即可。具體就是加上一條配置:

DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"

url去重算法
這里的去重規(guī)則,會(huì)先把url轉(zhuǎn)換成唯一標(biāo)識(shí),然后再存到集合里。
源碼里會(huì)把url通過hashlib進(jìn)行哈希運(yùn)算,用的是SHA1·。把原本的字符串轉(zhuǎn)成數(shù)字簽名。這么做的好處就是,即使url會(huì)很長,但是生成的數(shù)字簽名的長度是固定的。這里可以注意下這個(gè)技巧,把url存儲(chǔ)在redis里的時(shí)候,占用的長度是固定的,關(guān)鍵是不會(huì)太長。
另外這個(gè)轉(zhuǎn)換過程還有個(gè)特點(diǎn)。如果url里是帶get參數(shù)的,如果參數(shù)的值是一樣的,但是出現(xiàn)的順序不同,轉(zhuǎn)換后生成的唯一標(biāo)識(shí)也是一樣的。比如像下面這樣:

url1 = 'https://www.baidu.com/s?wd=sha1&ie=utf-8'
url2 = 'https://www.baidu.com/s?ie=utf-8&wd=sha1'

像這種,只是參數(shù)的先后順序不同,兩個(gè)請求應(yīng)該還是同一個(gè)請求,應(yīng)該要去重。這里在這種情況下生成的唯一標(biāo)識(shí)是一樣的,所以可以做到去重。

這個(gè)去重規(guī)則實(shí)現(xiàn)了去重的算法,是要被調(diào)度器使用的。在去重的這個(gè)類里,通過request_seen這個(gè)方法來判斷是否有重復(fù)。下面的調(diào)度器里就會(huì)調(diào)用這個(gè)方法來判斷避免重復(fù)爬取。

調(diào)度器

在配置文件里,加上下面的配置來指定調(diào)度器,這個(gè)是scrapy讀取的配置:

SCHEDULER = "scrapy_redis.scheduler.Scheduler"

接下來出一些在scrapy-redis模塊里要讀取的配置。其中部分在scrapy_redis.defaults.py里有默認(rèn)設(shè)置,還有的在代碼里也有默認(rèn)的邏輯,所以配置文件里可以什么都不寫,需要改變設(shè)置的話也可以自己在配置文件里指定。

使用的隊(duì)列

SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.PriorityQueue'

指定調(diào)度器存取請求使用的隊(duì)列類型,有3個(gè)可選的:

  • PriorityQueue,默認(rèn)。通過redis的有序集合來實(shí)現(xiàn)
  • FifoQueue,隊(duì)列。通過redis的List實(shí)現(xiàn)的,使用lpush和rpop進(jìn)行存取
  • LifoQueue,棧。也是通過redis的List實(shí)現(xiàn)的,使用lpush和lpop進(jìn)行存取

請求存放在redis中的key

SCHEDULER_QUEUE_KEY = '%(spider)s:requests'

這樣,不同的爬蟲應(yīng)用在redis中可以使用不同的key存取請求。

對保存到redis中的數(shù)據(jù)進(jìn)行序列化

SCHEDULER_SERIALIZER = "scrapy_redis.picklecompat"  

這個(gè)設(shè)置在defaults里沒有,如果沒有設(shè)置,就用 scrapy_redis.picklecompat 這個(gè)。而底層用的還是pickle模塊。這樣可以把對象序列化之后在redis中保存起來。

清空調(diào)度器和去重記錄
下面的2個(gè)參數(shù)都是布爾型的,如果不設(shè)置就是False,如果需要開啟,就設(shè)置為True:

SCHEDULER_PERSIST = True  # 是否在關(guān)閉時(shí)候保留,調(diào)度器和去重記錄
SCHEDULER_FLUSH_ON_START = True  # 是否在開始之前清空,調(diào)度器和去重記錄

上面兩個(gè)參數(shù)的效果都是判斷后決定是否要執(zhí)行調(diào)度器的一個(gè)flush方法。而這個(gè)flush方法里執(zhí)行的則是清空調(diào)度器和去重記錄。
注意這兩個(gè)參數(shù)的效果。默認(rèn)都是False,就是在開始之前不清空調(diào)度器和去重記錄,在結(jié)束的時(shí)候也不保留調(diào)度器和去重記錄。測試的話一般不需要保留。如果是上線使用,一般需要保留調(diào)度器和去重記錄,那么就按下面來設(shè)置:

# 保留調(diào)度器和去重記錄的設(shè)置
SCHEDULER_PERSIST = True
SCHEDULER_FLUSH_ON_START = False

獲取請求時(shí)等待的時(shí)間
調(diào)度器獲取請求時(shí),如果數(shù)據(jù)為空,進(jìn)行等待的時(shí)間,默認(rèn)為0,單位秒:

SCHEDULER_IDLE_BEFORE_CLOSE = 0 

這個(gè)實(shí)際就是redis的blpop和brpop操作時(shí)的timeout參數(shù),默認(rèn)為0。如果為0,就使用lpop和rpop這2個(gè)不阻塞的pop方法。如果大于0,就使用阻塞的pop方法。這里不會(huì)用到timeout為0的阻塞pop,所以不會(huì)一直阻塞,總會(huì)返回的。無論阻塞還是不阻塞,取不到值就返回None。
另外,調(diào)度器默認(rèn)用的是有序集合,redis的有序集合取值沒有阻塞也沒有timeout,所以這個(gè)值是無效的。

去重規(guī)則的參數(shù)
下面2個(gè)是去重規(guī)則使用的參數(shù),其中一個(gè)在去重的時(shí)候講過了。另一個(gè)就是在redis里使用的key:

SCHEDULER_DUPEFILTER_KEY = '%(spider)s:dupefilter'  # 去重規(guī)則在redis中保存時(shí)對應(yīng)的key
SCHEDULER_DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter'  # 去重規(guī)則對應(yīng)處理的類

數(shù)據(jù)持久化

模塊還提供了數(shù)據(jù)持久化,在scrapy的配置里指定好數(shù)據(jù)持久化使用的類:

ITEM_PIPELINES = {
   'scrapy_redis.pipelines.RedisPipeline': 300,
}

# 下面是兩個(gè)持久化時(shí)可以定制的配置
# PIPELINE_KEY = '%(spider)s:items'
# REDIS_ITEMS_SERIALIZER = "scrapy.utils.serialize.ScrapyJSONEncoder"

先去看看這個(gè)類里具體做了些什么:

from twisted.internet.threads import deferToThread

class RedisPipeline(object):

    def process_item(self, item, spider):
        return deferToThread(self._process_item, item, spider)

    def _process_item(self, item, spider):
        key = self.item_key(item, spider)
        data = self.serialize(item)
        self.server.rpush(key, data)
        return item

先看process_item方法,返回一個(gè)deferToThread,在twisted里的相當(dāng)于拿一個(gè)線程來做一個(gè)事。具體twisted干了啥就忽略把,主要是處理_process_item這個(gè)方法。所以真正做的處理是在_process_item方法里。
這里先把item的數(shù)據(jù)通過self.serialize函數(shù)做序列化,然后就是下面的rpush存到redis里去了。這里有2個(gè)可以定制的參數(shù),一個(gè)是序列化的方法,一個(gè)是存儲(chǔ)的redis里使用的Key。
默認(rèn)使用下面的Key,當(dāng)然可以在配置文件里指定:

PIPELINE_KEY = '%(spider)s:items'

默認(rèn)使用的序列化的方法是一個(gè)做了一些定制的json方法,在下面這里:

from scrapy.utils.serialize import ScrapyJSONEncoder

也是可以通過參數(shù)來指定自己的序列化方法的:

REDIS_ITEMS_SERIALIZER = "scrapy.utils.serialize.ScrapyJSONEncoder"

爬蟲和起始url

之前在spiders文件夾下寫爬蟲的時(shí)候,都是繼承scrapy.Spider然后寫自己的類。
模塊也提供了一個(gè)它自己的類,可以直接繼承模塊提供的這個(gè)類來寫爬蟲:

from scrapy_redis.spiders import RedisSpider

class TestSpider(RedisSpider):
    name = 'jandan'
    allowed_domains = ['jandan.net']

用了模塊的爬蟲后,起始url也可以直接從redis里獲取了,相關(guān)的配置有下面2個(gè):

START_URLS_KEY = '%(name)s:start_urls'
START_URLS_AS_SET = False

第一個(gè)設(shè)置,是在redis里用來存儲(chǔ)起始url的key,只有通過這個(gè)key才能從redis里獲取到起始的url。
第二個(gè)設(shè)置,是指定在redis里使用是否使用集合來存儲(chǔ)起始url,默認(rèn)不用集合,用的就是列表。
使用模塊提供的這個(gè)爬蟲,會(huì)一直運(yùn)行,永遠(yuǎn)不會(huì)停止。沒有任務(wù)的話應(yīng)該就是一直等著直到獲取到新的任務(wù)??梢酝鵵edis對應(yīng)的起始url的key里添加數(shù)據(jù),就會(huì)自動(dòng)的開始爬蟲。適合在線上使用。
而scrapy模塊提供的scrapy.Spider這個(gè)爬蟲適合平時(shí)自己用,爬完就結(jié)束了。


網(wǎng)頁名稱:Python自動(dòng)化開發(fā)學(xué)習(xí)-分布式爬蟲(scrapy-redis)
分享路徑:http://weahome.cn/article/gesscd.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部