当前位置:   article > 正文

【Python从入门到进阶】49、当当网Scrapy项目实战(二)_python itemadapter.itemadapter

python itemadapter.itemadapter

接上篇《48、当当网Scrapy项目实战(一)
上一篇我们正式开启了一个Scrapy爬虫项目的实战,对当当网进行剖析和抓取。本篇我们继续编写该当当网的项目,讲解刚刚编写的Spider与item之间的关系,以及如何使用item,以及使用pipelines管道进行数据下载的操作。

一、使用item封装数据

在上一篇我们通过编写的爬虫文件,获取到当当网“一般管理类”书籍的第一页的明细列表信息。但是我们仅仅是将爬取到的目标信息print打印到控制台了,没有保存下来,这里我们就需要item先进行数据的封装。在“dang.py”爬虫文件里,我们获取到了目标数据,这些数据是我们之前通过item定义过这些数据的数据结构,但是没有使用过:

  1. import scrapy
  2. class ScrapyDangdang01Item(scrapy.Item):
  3.     # 书籍图片
  4.     src = scrapy.Field()
  5.     # 书籍名称
  6.     title = scrapy.Field()
  7.     # 书籍作者
  8.     search_book_author = scrapy.Field()
  9.     # 书籍价格
  10.     price = scrapy.Field()
  11.     # 书籍简介
  12.     detail = scrapy.Field()

那么,我们如何使用item定义好的数据结构呢?我们在爬虫文件中,首先通过from引用上面的class的名称:

from scrapy_dangdang_01.items import ScrapyDangdang01Item

注:可能编译器会报错,这是编译器版本的问题,不影响后面的执行,可以忽略。
导入完毕之后,我们创建一个book对象,这个对象就是把上面那些零散的信息全部都组装起来的集合体,然后在构造函数中,将所有抓取到的属性,挨个赋值到item文件中的各个属性中去:

book = ScrapyDangdang01Item(src=src, title=title, search_book_author=search_book_author, price=price, detail=detail)

然后这个book对象,就要交给pipelines进行下载。

二、设置yield返回目标对象

这里我们需要使用到Python中的yield指令,它的作用如下:

        yield是Python中的一个关键字,主要用于定义生成器(generator)。生成器是一种特殊的迭代器,可以逐个地生成并返回一系列的值,而不是一次性地生成所有的值。这可以节省大量的内存,尤其是在处理大量数据时。

        yield的工作原理类似于return,但它不仅仅返回一个值,还可以保存生成器的状态,使得函数在下次调用时可以从上次离开的地方继续执行。
下面是一个简单的生成器函数的例子:

  1. def simple_generator():  
  2.     n = 1  
  3.     while n <= 5:  
  4.         yield n  
  5.         n += 1  
  6.   
  7. for i in simple_generator():  
  8.     print(i)

        在这个例子中,simple_generator 是一个生成器函数,它使用 yield 来生成一系列的数字。当我们对这个生成器进行迭代(例如,在 for 循环中)时,它会逐个生成数字 1 到 5,并打印出来。

所以我们这里使用yield是用来将上面for循环中的每一个book交给pipelines处理,循环一个处理一个。编写代码如下:

  1. # 将数据封装到item对象中
  2. book = ScrapyDangdang01Item(src=src, title=title, search_book_author=search_book_author, price=price, detail=detail)
  3. # 获取一个book对象,就将该对象交给pipelines
  4. yield book

此时for循环每执行一次,爬虫函数就会返回一个封装好的book对象。完整的爬虫文件代码如下(scrapy_dangdang_01/scrapy_dangdang_01/spiders/dang.py):

  1. import scrapy
  2. from scrapy_dangdang_01.items import ScrapyDangdang01Item
  3. class DangSpider(scrapy.Spider):
  4.     name = "dang"
  5.     allowed_domains = ["category.dangdang.com"]
  6.     start_urls = ["http://category.dangdang.com/cp01.22.01.00.00.00.html"]
  7.     def parse(self, response):
  8.         # 获取所有的图书列表对象
  9.         li_list = response.xpath('//ul[@id="component_59"]/li')
  10.         # 遍历li列表,获取每一个li元素的几个值
  11.         for li in li_list:
  12.             # 书籍图片
  13.             src = li.xpath('.//img/@data-original').extract_first()
  14.             # 第一张图片没有@data-original属性,所以会获取到控制,此时需要获取src属性值
  15.             if src:
  16.                 src = src
  17.             else:
  18.                 src = li.xpath('.//img/@src').extract_first()
  19.             # 书籍名称
  20.             title = li.xpath('.//img/@alt').extract_first()
  21.             # 书籍作者
  22.             search_book_author = li.xpath('./p[@class="search_book_author"]//span[1]//a[1]/@title').extract_first()
  23.             # 书籍价格
  24.             price = li.xpath('./p[@class="price"]//span[@class="search_now_price"]/text()').extract_first()
  25.             # 书籍简介
  26.             detail = li.xpath('./p[@class="detail"]/text()').extract_first()
  27.             # print("======================")
  28.             # print("【图片地址】", src)
  29.             # print("【书籍标题】", title)
  30.             # print("【书籍作者】", search_book_author)
  31.             # print("【书籍价格】", price)
  32.             # print("【书籍简介】", detail)
  33.             # 将数据封装到item对象中
  34.             book = ScrapyDangdang01Item(src=src, title=title, search_book_author=search_book_author, price=price, detail=detail)
  35.             # 获取一个book对象,就将该对象交给pipelines
  36.             yield book

三、编写pipelines保存数据至本地

首先我们进入setting.py中,设置“ITEM_PIPELINES”参数,在其中添加我们设置的pipelines管道文件的路径地址:

  1. # 管道可以有很多个,前面是管道名后面是管道优先级,优先级的范围是1到1000,值越小优先级越高
  2. ITEM_PIPELINES = {
  3.     "scrapy_dangdang_01.pipelines.ScrapyDangdang01Pipeline": 300,
  4. }

此时我们进入pipelines.py中编写管道逻辑:

  1. from itemadapter import ItemAdapter
  2. # 如果需要使用管道,要在setting.py中打开ITEM_PIPELINES参数
  3. class ScrapyDangdang01Pipeline:
  4.     # process_item函数中的item,就是爬虫文件yield的book对象
  5.     def process_item(self, item, spider):
  6.         # 这里写入文件需要用'a'追加模式,而不是'w'写入模式,因为写入模式会覆盖之前写的
  7.         with open('book.json', 'a', encoding='utf-8') as fp:
  8.             # write方法必须写一个字符串,而不能是其他的对象
  9.             fp.write(str(item))
  10.         return item

此时我们执行爬虫函数,可以看到执行成功:

然后我们打开生成的book.json文件,“Ctrl+Alt+l”排版之后,可以看到我们爬取的数据已经生成了:

上面就是管道+爬虫+item的综合使用模式。

四、进行必要的优化

在上面的pipelines管道函数中,我们每一次获取到爬虫for循环yield的book对象时,都需要打开一次文件进行写入,比较耗费读写资源,对文件的操作过于频繁。

优化方案:在爬虫执行开始的时候就打开文件,爬虫执行结束之后再关闭文件。此时我们就需要了解pipelines的生命周期函数。分别为以下几个方法:

(1)open_spider(self, spider): 当爬虫开始时,这个方法会被调用。你可以在这里进行一些初始化的操作,比如打开文件、建立数据库连接等。
(2)close_spider(self, spider): 当爬虫结束时,这个方法会被调用。你可以在这里进行清理操作,比如关闭文件、断开数据库连接等。
(3)process_item(self, item, spider): 这是pipelines中最核心的方法。每个被抓取并返回的项目都会经过这个方法。你可以在这里对数据进行清洗、验证、转换等操作。这个方法必须返回一个项目(可以是原项目,也可以是经过处理的新项目),或者抛出一个DropItem异常,表示该项目不应被进一步处理。

此时我们就可以使用open_spider定义爬虫开始时打开文件,close_spider定义爬虫结束时关闭文件,而在爬虫运行期间的process_item方法中,只进行写的操作,完整代码如下:

  1. from itemadapter import ItemAdapter
  2. import json
  3. # 如果需要使用管道,要在setting.py中打开ITEM_PIPELINES参数
  4. class ScrapyDangdang01Pipeline:
  5.     # 1、在爬虫文件开始执行前执行的方法
  6.     def open_spider(self,spider):
  7.         print('++++++++爬虫开始++++++++')
  8.         # 这里写入文件需要用'a'追加模式,而不是'w'写入模式,因为写入模式会覆盖之前写的
  9.         self.fp = open('book.json', 'a', encoding='utf-8') # 打开文件写入
  10.     # 2、爬虫文件执行时,返回数据时执行的方法
  11.     # process_item函数中的item,就是爬虫文件yield的book对象
  12.     def process_item(self, item, spider):
  13.         # write方法必须写一个字符串,而不能是其他的对象
  14.         self.fp.write(str(item)) # 将爬取信息写入文件
  15.         return item
  16.     # 在爬虫文件开始执行后执行的方法
  17.     def close_spider(self, spider):
  18.         print('++++++++爬虫结束++++++++')
  19.         self.fp.close() # 关闭文件写入

这样就能解决对文件操作频繁,耗费读写资源的问题了。

五、多管道的支持

pipelines支持设置多个管道,例如我们在原来的pipelines.py中再定义一个管道class类,用来下载每一个图书的图片:

  1. # 下载爬取到的book对象中的图片文件
  2. class ScrapyDangdangImagesPipeline:
  3.     def process_item(self, item, spider):
  4.         # 获取book的src属性,并按照地址下载图片,保存值books文件夹下
  5.         url = 'http:' + item.get('src')
  6.         filename = './books/' + item.get('title') + '.jpg'
  7.         # 检查并创建目录
  8.         if not os.path.exists('./books/'):
  9.             os.makedirs('./books/')
  10.         urllib.request.urlretrieve(url=url, filename=filename)
  11.         return item

然后我们在setting.py中的ITEM_PIPELINES参数中追加这个管道:

  1. # 管道可以有很多个,前面是管道名后面是管道优先级,优先级的范围是1到1000,值越小优先级越高
  2. ITEM_PIPELINES = {
  3.     "scrapy_dangdang_01.pipelines.ScrapyDangdang01Pipeline": 300,
  4.     "scrapy_dangdang_01.pipelines.ScrapyDangdangImagesPipeline": 301
  5. }

运行爬虫文件,可以看到相关的图片已经全部下载下来:

并且都是可以打开的图片:

至此管道+爬虫+item的综合使用模式讲解完毕。下一篇我们来讲解Scrapy的多页面下载如何实现。

参考:尚硅谷Python爬虫教程小白零基础速通
转载请注明出处:https://guangzai.blog.csdn.net/article/details/136283532

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号