scrapy笔记六 scrapy运行架构的实例配合解析

2019-11-27 15:08:02 浏览数 (1)

在之前的项目中已经可以正常运行出scrapy框架下的爬虫程序,但是如果换一个项目换一个爬取任务,要活学活用还需要进行针对scrapy是如何运行的进行深入的学习.

目录:

基本概念

具体从代码中解析

item

pipelines

spiders

可以从这里学习详细的结构说明:

http://www.tuicool.com/articles/fiyIbq

基本概念

Scrapy框架主要由五大组件组成,调试器(Scheduler)、下载器(Downloader)、爬虫(Spider)和实体管道(Item Pipeline)、Scrapy引擎(Scrapy Engine)。

如下图.

Scrapy运行流程

  1. 首先,引擎从调度器中取出一个链接(URL)用于接下来的抓取
  2. 引擎把URL封装成一个请求(Request)传给下载器,下载器把资源下载下来,并封装成应答包(Response)
  3. 然后,爬虫解析Response
  4. 若是解析出实体(Item),则交给实体管道进行进一步的处理。
  5. 若是解析出的是链接(URL),则把URL交给Scheduler等待抓取

具体解析:

参照项目 meizitu 源代码在:

https://github.com/luyishisi/WebCrawlers/tree/master/scrapy_code/meizitu

item.py : 定义提取的数据结构:

在定义这部分时,要考虑项目目标是为了爬取妹子图网的图片,因此需要的结构有:

  • url : 页面路径 ;
  • name:页面名字 ;
  • tags : 页面标签(用于图片分类)
  • image_urls = 图片的地址 ;
  • images = 图片的名字

item代码如下:

代码语言:javascript复制
import scrapy
class MeizituItem(scrapy.Item):
    url = scrapy.Field()
    name = scrapy.Field()
    tags = scrapy.Field()
    image_urls = scrapy.Field()
    images = scrapy.Field()
关于field

Field对象指明了每个字段的元数据(metadata)。

您可以为每个字段指明任何类型的元数据。Field 对象对接受的值没有任何限制。也正是因为这个原因,文档也无法提供所有可用的元数据的键(key)参考列表。Field 对象中保存的每个键可以由多个组件使用,并且只有这些组件知道这个键的存在

关于items.的实例化

可从抓取进程中得到这些信息, 比如预先解析提取到的原生数据,items 提供了盛装抓取到的数据的*容器* , 而Item Loaders提供了构件*装载populating*该容器。

在用于下面例子的管道功能时.在spiders中使用了item的实例化:代码如下:

代码语言:javascript复制
def parse_item(self, response):
        #l=用ItemLoader载入MeizituItem()
        l = ItemLoader(item=MeizituItem(), response=response)
        #名字
        l.add_xpath('name', '//h2/a/text()')
        #标签
        l.add_xpath('tags', "//div[@id='maincontent']/div[@class='postmeta  clearfix']/div[@class='metaRight']/p")
        #图片连接
        l.add_xpath('image_urls', "//div[@id='picture']/p/img/@src", Identity())
        #url
        l.add_value('url', response.url)
 
        return l.load_item()

最终, 当所有数据被收集起来之后, 调用 ItemLoader.load_item() 方法, 实际上填充并且返回了之前通过调用 add_xpath(), add_css(), and add_value() 所提取和收集到的数据的Item.

pipeline.py : (管道.用于保存数据)

需要导入settings配置文件,根据你保存的内容需要不同的包,例如,保存文本数据往往需要json包,本项目保存的是图片,则导入os包用于设定保存路径等.最重要的是要导入requests包.用于发送请求给图片的url,将返回的应答包进行保存.

本部分至少需要重写图片下载类,返回的是item对象.

这部分的特性是:
  • 避免重新下载最近已经下载过的数据
  • 指定存储数据位置
  • 将所有下载的图片转换成通用的格式(JPG)和模式(RGB)
  • 缩略图生成
  • 检测图像的宽/高,确保它们满足最小限制
 典型的工作流程如下:
  1. 在一个爬虫(在spiders.py中),你抓取一个项目,把其中图片的URL放入 file_urls 组内。 l.add_xpath('image_urls', "//div[@id='picture']/p/img/@src", Identity()
  2. 项目从爬虫(在spiders.py中)内返回,进入项目管道(到pipeline.py中)。 return l.load_item()
  3. 当项目进入 Pipelinefile_urls || image_urls 组内的URLs将被Scrapy的调度器和下载器(这意味着调度器和下载器的中间件可以复用)安排下载(将url请求发送到下载器)。项目会在这个特定的管道阶段保持“locker”的状态,直到完成文件的下载(或者由于某些原因未完成下载)。
  4. 当文件下载完后,另一个字段(files)将被更新到结构中。这个组将包含一个字典列表,其中包括下载文件的信息,比如下载路径、源抓取地址(从 file_urls 组获得)和图片的校验码(checksum)。 files 列表中的文件顺序将和源 file_urls 组保持一致。如果某个图片下载失败,将会记录下错误信息,图片也不会出现在 files 组中。
附录:

为了启用 media pipeline,你首先需要在项目中添加它setting.

对于 Images Pipeline, 使用:

代码语言:javascript复制
<span class="n">ITEM_PIPELINES</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'scrapy.pipeline.<span style="color: #ff0000;">images</span>.<span style="color: #ff0000;">Images</span>Pipeline'</span><span class="p">:</span> <span class="mi">1</span><span class="p">}</span>

对于 Files Pipeline, 使用:

代码语言:javascript复制
<span class="n">ITEM_PIPELINES</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'scrapy.pipeline.<span style="color: #ff0000;">files</span>.<span style="color: #ff0000;">Files</span>Pipeline'</span><span class="p">:</span> <span class="mi">1</span><span class="p">}</span>

因此配置文件如下:

代码语言:javascript复制
BOT_NAME = 'meizitu'
 
SPIDER_MODULES = ['meizitu.spiders']
NEWSPIDER_MODULE = 'meizitu.spiders'
#载入ImageDownLoadPipeline类
#为了启用一个Item Pipeline组件,你必须将它的类添加到 ITEM_PIPELINES 配置
#分配给每个类的整型值,确定了他们运行的顺序,item按数字从低到高的顺序,通过pipeline,
TEM_PIPELINES = {'meizitu.pipelines.ImageDownloadPipeline': 1}
#图片储存,第一个meizitu是项目名称,imagedown部分则是类的名字.
IMAGES_STORE = '.'
pipeline代码如下:
代码语言:javascript复制
# -*- coding: utf-8 -*-
 
#图片下载部分(自动增量)
import requests
from meizitu import settings
import os
 
#导入重写图片下载类
class ImageDownloadPipeline(object):
    def process_item(self, item, spider):
        #每个item pipeline组件都需要调用该方法,这个方法必须返回一个 Item (或任何继承类)对象,
        # 或是抛出 DropItem 异常,被丢弃的item将不会被之后的pipeline组件所处理
        if 'image_urls' in item:#如果‘图片地址’在项目中
            images = []#定义图片空集
            
            dir_path = '%s/%s' % (settings.IMAGES_STORE, spider.name)
 
            #建立目录名字和项目名称一致
            if not os.path.exists(dir_path):
                os.makedirs(dir_path)
    
            #根据item字典进行查询   
            for image_url in item['image_urls']:
                us = image_url.split('/')[3:]
                image_file_name = '_'.join(us)
                file_path = '%s/%s' % (dir_path, image_file_name)
                images.append(file_path)
 
            #如果这个文件存在则跳过
                if os.path.exists(file_path):
                    continue
            #进行图片文件写入,wb模式打开文件,然后requests.get获取图片流,
                with open(file_path, 'wb') as handle:
                    response = requests.get(image_url, stream=True)
                    for block in response.iter_content(1024):
                        #获取的流如果有不存在的,则使用break结束,如果没有一次结束则进行写入
                        if not block:
                            break
 
                        handle.write(block)
 
            item['images'] = images
           
        return item

Spiders

Spider类定义了如何爬取某个(或某些)网站。包括了爬取的动作(例如:是否跟进链接)以及如何从网页的内容中提取结构化数据(爬取item)。 换句话说,Spider就是定义爬取的动作及分析某个网页(或者是有些网页)的地方。

对spider来说,爬取的循环类似下文:

  1. 以初始的URL初始化Request,并设置回调函数。 当该request下载完毕并返回时,将生成response,并作为参数传给该回调函数。 spider中初始的request是通过调用 start_requests() 来获取的。 start_requests() 读取 start_urls 中的URL, 并以 parse 为回调函数生成 Request
  2. 在回调函数内分析返回的(网页)内容,返回 Item 对象、dict、 Request 或者一个包括三者的可迭代容器。 返回的Request对象之后会经过Scrapy处理,下载相应的内容,并调用设置的callback函数(函数可相同)。
  3. 在回调函数内,您可以使用 选择器(Selectors) (您也可以使用BeautifulSoup, lxml 或者您想用的任何解析器) 来分析网页内容,并根据分析的数据生成item。
  4. 最后,由spider返回的item将被存到数据库(由某些 Item Pipeline 处理)或使用 Feed exports 存入到文件中。

虽然该循环对任何类型的spider都(多少)适用,但Scrapy仍然为了不同的需求提供了多种默认spider。

分析代码:

导入选择器,itemloader等.重写类,从start_urls开始爬取

代码语言:javascript复制
# -*- coding: utf-8 -*-
import scrapy
from scrapy.selector import Selector
from scrapy.contrib.loader import ItemLoader, Identity
from meizitu.items import MeizituItem
 
 
class MeiziSpider(scrapy.Spider):
    name = "meizi"
    allowed_domains = ["meizitu.com"]
    start_urls = (
        'http://www.meizitu.com/',
    )
 
    def parse(self, response):
        #sel是页面源代码,载入scrapy.selector
        sel = Selector(response)
        #每个连接,用@href属性
        for link in sel.xpath('//h2/a/@href').extract():
            #请求=Request(连接,parese_item)
            request = scrapy.Request(link, callback=self.parse_item)
            yield request#返回请求
        #获取页码集合
        pages = sel.xpath('//*[@id="wp_page_numbers"]/ul/li/a/@href').extract()
        print('pages: %s' % pages)#打印页码
        if len(pages) > 2:#如果页码集合>2
            page_link = pages[-2]#图片连接=读取页码集合的倒数第二个页码
            page_link = page_link.replace('/a/', '')#图片连接=page_link(a替换成空)
            request = scrapy.Request('http://www.meizitu.com/a/%s' % page_link, callback=self.parse)
            yield request#返回请求
 
    def parse_item(self, response):
        #l=用ItemLoader载入MeizituItem()
        l = ItemLoader(item=MeizituItem(), response=response)
        #名字
        l.add_xpath('name', '//h2/a/text()')
        #标签
        l.add_xpath('tags', "//div[@id='maincontent']/div[@class='postmeta  clearfix']/div[@class='metaRight']/p")
        #图片连接
        l.add_xpath('image_urls', "//div[@id='picture']/p/img/@src", Identity())
        #url
        l.add_value('url', response.url)
 
        return l.load_item()

阿萨德 阿斯顿

原创文章,转载请注明: 转载自URl-team

本文链接地址: scrapy笔记六 scrapy运行架构的实例配合解析

Related posts:

  1. Scrapy-笔记一 入门项目 爬虫抓取w3c网站
  2. Scrapy笔记四 自动爬取网页之使用CrawlSpider
  3. Scrapy笔记五 爬取妹子图网的图片 详细解析
  4. Scrapy笔记零 环境搭建与五大组件架构
  5. 基于百度IP定位的网站访问来源分析的python实战项目–实践笔记二–调百度地图将经纬信息可视化呈现
  6. scrapy学习笔记十一 scrapy实战效率测评

0 人点赞