最近工作有点愉快, 终于对python scrapy爬虫框架用到吐血,并不是对这个框架功能彻底用透了, 而是对一下普通流水性代码的重复已经腻无力吐槽, 于是慢慢往框架中加入不一样使用方式.
本次博文是在使用scrapy框架爬取腾讯网新闻, 手动加入自定义的队列(from queue import Queue) , 用来保存url请求字符串的字典参数,
from queue import Queue from copy import deepcopy class QQNewsSpider(): name = 'qq_news_spider' allowed_domains = ['pacaio.match.qq.com', 'xw.qq.com'] _queue = Queue() channel_table = { 'pacaio_xw': # https://pacaio.match.qq.com/xw/site uid: 0_ddb8bda1ef6c5 [ ('sports', '__jp1', '体育'), ('finance', '__jp0', '经济'), ('tech', '__jp0', '科技'), ('edu', '__jp2', '教育'), ], 'pacaio_irs': # https://pacaio.match.qq.com/irs/rcd [ ('science', '__jp2', '科技'), ('games', '__jp2', '游戏'), ('house', '__jp0', '房产'), ('health', '__jp3', '健康'), ('history', '__jp2', '历史') ], 'xw': # https://xw.qq.com/service/api/proxy [ ('world', '__jp2', '国际'), ] } @staticmethod def get_query_params(): query_dict = { 'cid': '56', # 不变 'token': 'c786875b8e04da17b24ea5e332745e0f', # 不变 'num': '20', 'page': '4', # 'ext': 'food', # 变 # 'callback': '__jp4', # 变 # 'expIds': '20181028A2IBCF', } for _, channel_container in __class__.channel_table.items(): if _ == 'pacaio_xw': url = 'https://pacaio.match.qq.com/xw/site' query_dict['uid'] = '0_ddb8bda1ef6c5' elif _ == 'pacaio_irs': url = 'https://pacaio.match.qq.com/irs/rcd' query_dict['uid'] = '2222222222222222' else: url = 'https://xw.qq.com/service/api/proxy' query_dict['uid'] = '333333333333' for channel, callback, category in channel_container: query_dict['ext'] = channel query_dict['callback'] = callback query_dict['category'] = category __class__._queue.put([url, query_dict]) for i in range(__class__._queue.qsize()): print(__class__._queue.get() return __class__._queue if __name__ == '__main__': spider = QQNewsSpider() _queue = spider.get_query_params()上述打印结果如下:
留意红框之处: channel_table字典中的每个类别(pacaio_xw, pacaio_irs, xw)下的频道在存入_queue时是一样, 即每个类别下只有最后一个数据才是正确的? why.....
将__class__._queue.put([url, query_dict]) 改成__class__._queue.put([url, i]) 这里i每迭代一次增加1, 打印结果如下:
咦, 为什么队列此次确实正确的结果?
其实, 这是和python的拷贝用法有关!
在python世界中,拷贝分为深(copy)、浅(deepcopy)拷贝两种,都属于复制功能。对于不可变类型(即简单数据类型,如int,str,float等)没有拷贝用法,有复制(赋值,赋值也可视为复制), 但是复制的变量都指向同一片内容地址,示例如下:
对于可变数据类型(如list,dict,tuple,set),复制等同于拷贝中的浅拷贝,现在下面简单说一下拷贝(from copy import deepcopy).
这里将可变类型细分成简单可变数据类型(只有子元素且都是简单类型,没有孙元素)和复杂可变类型(子元素存在可变类型,即有孙元素),示例:
对于简单可变类型:
复杂可变数据类型:
复制和拷贝总结:
复制:值和地址方面保持一致
拷贝:地址一定不同,对于简单数据类型,深浅拷贝在地址和值方面原对象和新对象互不干扰
对于复杂数据类型,如果子元素存在是可变类型的引用,深浅拷贝上:原对象和新对象地址互不干扰,值相同。
若子元素不存在可变类型的引用,深浅拷贝在地址和值方面原对象和新对象互不干扰。
--------------- -----------------------------------------------
现在言归正传, query_dict是可变类型,可变类型默认是浅拷贝,所以故最后一个会覆盖前面值,即使在队列中。
解决方式:改成深拷贝__class__._queue.put([url, deepcopy(query_dict)])