scrapy里面item传递数据后数据不正确的问题

在上篇文章《python3 + scrapy 爬取妹子图 (meizitu.com)》中,我爬取了妹子图网站的图片,爬取是按照如下思路的:

  1. 通过首页(http://www.meizitu.com/),爬取标签名称tag_name和标签链接tag_href

  2. 通过标签链接,爬取当前标签下全部页面page_list

  3. 通过页面,爬取当前页面的图片专辑名称album_name和图片专辑链接album_href

  4. 通过专辑链接,爬取该专辑里面所有图片名称img_title、图片链接img_src及图片链接列表img_urls

5 通过图片链接列表,使用scrapy自带的图片下载器ImagesPipeline下载图片到设定的文件夹

然后我们的爬虫代码里面有4层,层与层之间通过meta参数传递数据,例如parse到parse_page时,数据传递是yield scrapy.Request(url=item[‘tag_href’], meta={‘item’: item}, callback=self.parse_page),其他各层传递与这个一样,到最后一层的时候就yield item,供pipeline使用。
爬虫代码整个代码如下:

-- coding: utf-8 --

import scrapy

from meizitu.items import MeizituItem

class MztSpider(scrapy.Spider):
name = ‘mzt’
allowed_domains = [‘meizitu.com’]
start_urls = [‘http://www.meizitu.com/’]

def parse(self, response):
    """
    提取标签名称和链接
    :param response:
    :return:
    """
    
    tags = response.xpath(".//*[@class='tags']/span/a")
    for i in tags:
        item = MeizituItem()
        tag_href = i.xpath(".//@href").extract()[0]
        tag_name = i.xpath(".//@title").extract()[0]
        item['tag_name'] = tag_name
        item['tag_href'] = tag_href
        yield scrapy.Request(url=item['tag_href'], meta={'item': item}, callback=self.parse_page)

def parse_page(self, response):
    """
    提取标签下链接
    :param response:
    :return:
    """
    
    # 进入某个标签后,爬取底部分页按钮
    page_lists = response.xpath(".//*[@id='wp_page_numbers']/ul/li")
    # 获取底部分页按钮上的文字,根据文字来判断当前标签页下总共有多少分页
    page_list = page_lists.xpath('.//text()')
    # 如果当前标签页下有多个页面,则再根据第一个按钮是否为“首页”来进行再次提取,因为这里有的页面第一个按钮是“首页”,有的第一个按钮是“1”
    if len(page_lists) > 0:
        if page_list[0].extract() == '首页':
            page_num = len(page_lists) - 3
        
        else:
            page_num = len(page_lists) - 2
    else:
        page_num = 1
    
    # 根据当前标签页的链接,来拼成带页码的链接
    if '_' in response.url:
        index = response.url[::-1].index('_')
        href_pre = response.url[:-index]
    else:
        if page_num == 1:
            href_pre = response.url.split('.html')[0]
        else:
            href_pre = response.url.split('.html')[0] + '_'
    for i in range(1, page_num + 1):
        if page_num == 1:
            href = href_pre + '.html'
        else:
            href = href_pre + str(i) + '.html'
        item = response.meta['item']
        item['page_list'] = href
        
        yield scrapy.Request(url=item['page_list'], meta={'item': item}, callback=self.parse_album)

def parse_album(self, response):
    """
    提取专辑名称和专辑链接
    :param response:
    :return:
    """
    
    albums = response.xpath(".//*[@class='pic']")
    for album in albums:
        album_href = album.xpath(".//a/@href").extract()[0]
        album_name = album.xpath(".//a/img/@alt").extract()[0]
        item = response.meta['item']
        item['album_name'] = album_name
        item['album_href'] = album_href
        
        yield scrapy.Request(url=item['album_href'], meta={'item': item}, callback=self.parse_img)

def parse_img(self, response):
    """
    提取图片名称和链接
    :param response:
    :return:
    """
    
    img_list = response.xpath(".//p/img")
    
    for img in img_list:
        item = response.meta['item']
        img_title = img.xpath(".//@alt").extract()[0]
        if img_title == '':
            for i in range(1, len(img_list) + 1):
                img_title = item['album_name'] + '_' + str(i)
        else:
            img_title = img_title
        img_urls = img.xpath(".//@src").extract()
        img_src = img.xpath(".//@src").extract()[0]
        item['img_title'] = img_title
        item['img_src'] = img_src
        item['img_urls'] = img_urls
        
        yield item

然后坑的地方就出现了,按照理想状态,最后的item数据每个都是唯一的,但是实际情况是最后的item很多数据都是重复和错乱的,要么只取了最后一张图片,要么图片和专辑对不上,完全不对。

后来在网上查了很久,终于找到这篇文章,作者的文章中说:
查找原因后,发现是因为使用 Request 函数传递 item 时,使用的是浅复制(对象的字段值被复制时,字段引用的对象不会被复制)

然后作者给出方法是,使用深复制,导入copy模块,将meta={‘item’: item} 更改为 meta={‘item’: copy.deepcopy(item)
果然,按照这个方法,把几个Request里面的都改了,数据是正常了。
但是,还没完。
此时,如果我打开ImagePipeline,再跑一遍查看数据,发现还是错乱的。
因为,最后yield item后,如果需要下载图片,ImagePipeline的get_media_requests方法需要参数item,通过item获取图片URL再去下载,如果是浅拷贝就有可能跟上面的情况一样
于是我把爬虫里面的最后一行代码yield item 修改成 yield copy.deepcopy(item)就完全ok了

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值