scrapy_redis 相关: 多线程更新 score/request.priority


使用 scrapy_redis 爬虫, 忘记或错误设置 request.priority(Rule 也可以通过参数 process_request 设置 request.priority),导致提取 item 的 request 排在有序集 xxx:requests 的队尾,持续占用内存。



遍历 SortedSet 的所有 item 并根据预定义字典对 data 中的 url 进行正则匹配,更新 score 并复制到临时 newkey,最后执行 rename 

# -*- coding: UTF-8 -*
import sys
import re
from multiprocessing.dummy import Pool as ThreadPool
from functools import partial

   input = raw_input #For py2
except NameError:

import redis

def print_line(string):
    print('\n{symbol}{space}{string}'.format(symbol='#'*10, space=' '*5, string=string))

def check_key_scores(key):
        total = redis_server.zcard(key)
    except redis.exceptions.ResponseError:
        print("The value of '{key}' is not a SortedSet".format(key=key))
    except Exception as err:

    if total == 0:
        print("key '{key}' does not exist or has no items".format(key=key))

    __, min_score = redis_server.zrange(key, 0, 0, withscores=True)[0]
    __, max_score = redis_server.zrange(key, -1, -1, withscores=True)[0]

    print('score  amount')
    total_ = 0
    # Asuming that score/request.priority is an integer, rather than float number like 1.1
    for score in range(int(min_score), int(max_score)+1):
        count = redis_server.zcount(key, score, score)
        print(score, count)
        total_ += count
    print("{total_}/{total} items of key '{key}' have an integer priority".format(
            total_=total_, total=total_, key=key))

def zadd_with_new_score(startstop, total_items):
    data, ori_score = redis_server.zrange(key, startstop, startstop, withscores=True)[0]
    for pattern, score in pattern_score:
        # data eg: b'\\x80\\x02}q\\x00(X\\x03\\x00\\x00\\x00urlq\\x01X\\x13\\x00\\x00\\x00\\x02X\\x08\\x00\\x00\\x00callbackq\\x03X\\x
        # See /site-packages/scrapy_redis/
            # We don't use zadd method as the order of arguments change depending on
            # whether the class is Redis or StrictRedis, and the option of using
            # kwargs only accepts strings, not bytes.
        m ='utf-8', 'replace'))
        if m:
            redis_server.execute_command('ZADD', newkey, score, data)
        redis_server.execute_command('ZADD', newkey, ori_score, data)
    print('{startstop} / {total_items}'.format(
            startstop=startstop+1, total_items=total_items))

if __name__ == '__main__':

    password = 'password'
    host = ''
    port = '6379'
    database_num = 0

    key = 'test:requests'
    newkey = 'temp'
    # Request whose url matching any key of keyword_score would be updated with the corresponding value as its score
    # Smaller value/score means higher request.priority
    keyword_score = {'httpbin': -12, 'apps/details': 1}
    pattern_score = [(re.compile(r'url.*?%s.*?callback'%k), v)for (k, v) in keyword_score.items()]
    threads_amount = 10

    redis_server = redis.StrictRedis.from_url('redis://:{password}@{host}:{port}/{database_num}'.format(
                                                password=password, host=host,
                                                port=port, database_num=database_num))

    print_line('Step 0: pre check')

    print_line('Step 1: copy items and update score')
    # total_items = redis_server.zlexcount(key, '-', '+')
    total_items = redis_server.zcard(key)
    input("Press Enter to copy {total_items} items of '{key}' into '{newkey}' with new score".format(
            total_items=total_items, key=key, newkey=newkey))
    p = ThreadPool(threads_amount), total_items=total_items), range(total_items))
    p.close()   #Prevents any more tasks from being submitted to the pool. Once all the tasks have been completed the worker processes will exit.
    p.join()    #Wait for the worker processes to exit. One must call close() or terminate() before using join().

    # For py3
    # with ThreadPool(threads_amount) as pool:
        #, total_items=total_items), range(total_items))
    # print('zadd_with_new_score done')

    print_line('Step 2: check copy result')

    print_line('Step 3: delete, rename and check key')
    input("Press Enter to DELETE '{key}' and RENAME '{newkey}' to '{key}'".format(
            key=key, newkey=newkey))
    print(redis_server.rename(newkey, key))





posted @ 2018-07-26 18:52  my8100  阅读(327)  评论(0编辑  收藏