Scrapy的核心

基于管道的持久化存储

1. scrapy框架中已经为我们专门集成好了高效、便捷的持久化操作功能,我们直接使用即可。
1) items.py -> 数据结构模板文件。定义数据属性。
2) pipelines.py -> 管道文件。接收数据(items),进行持久化操作。

2. 持久化存储的实现流程 (基于管道):
	1) 爬虫文件发送请求, 爬取到数据后, 进行数据解析
	2) 在items.py文件中封装items类
	3) 实例化item类型的对象
	4) 将解析的数据依次存储封装到items类型的对象中
	5) 使用yield关键字将items对象提交给pipelines管道
	6) 在管道文件中的process_item方法中接受items对象, 编写代码将items对象进行持久化存储
	7) settings.py 配置文件中开启管道

爬取多页面数据(全站爬取)

1.将每一个页码对应的url存放到爬虫文件的起始url列表(start_urls)中。(不推荐)

2.使用Request方法手动发起请求
第一步: 设定一个通用的url模板
pageNum = 1   # 起始页码
url = 'https://www.qiushibaike.com/text/page/%d/' # 每页的url

第二步: 在parse方法中
# 爬取所有页码数据
if self.pageNum <= 13: #一共爬取13页(共13页)
	self.pageNum += 1
	url = format(self.url % self.pageNum)

# 递归爬取数据:callback参数的值为回调函数(将url请求后,得到的相应数据继续进行parse解析),递归调用parse函数
	yield scrapy.Request(url=url,callback=self.parse)

post请求发送

1. 爬虫文件中的爬虫类继承到了Spider父类中的start_requests(self)方法, 该方法就可以对start_urls列表中的url发起请求

2. 实现post请求
该方法默认的实现,是对起始的url发起get请求,如果想发起post请求,则需要子类重写该方法。
	-方法: 重写start_requests方法,让其发起post请求
    
示例:
def start_requests(self):
    # post请求参数
	data = {
		'kw': 'dog'
		}
	for url in self.start_urls:
        # 发起post请求
		yield scrapy.FormRequest(url=url, callback=self.parse, formdata=data)

请求传参

请求传参的应用场景:
	- 爬取且解析的数据没有在同一张页面
	- 在请求方法中使用meta(字典)参数, 该字典会传递给回调函数
	- 回调函数接收meta: response.meta['key']

五大核心组件工作流程

引擎(Scrapy)
用来处理整个系统的数据流处理, 触发事务(框架核心), 根据不同的数据流进行判断.

调度器(Scheduler)
用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址

下载器(Downloader)
用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)

爬虫(Spiders)
爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面

项目管道(Pipeline)
负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。

五大核心组件工作流程

scrapy执行顺序

具体流程

1. spider中的url被封装成请求对象交给引擎(每一个url对应一个请求对象);

2. 引擎拿到请求对象之后, 将其全部交给调度器;

3. 调度器拿到所有请求对象后, 通过内部的过滤器过滤掉重复的url, 最后将去重后的所有url对应的请求对象压入到队列中, 随后调度器调度出其中一个请求对象, 并将其交给引擎;

4. 引擎将调度器调度出的请求对象交给下载器;

5. 下载器拿到该请求对象去互联网中下载数据;

6. 数据下载成功后会被封装到response中, 随后response会被交给下载器;

7. 下载器将response交给引擎;

8. 引擎将response交给spiders;

9. spiders拿到response后调用回调方法进行数据解析, 解析成功后产生item, 随后spiders将item交给引擎;

10. 引擎将item交给管道, 管道拿到item后进行数据的持久化存储.

示例

持久化存储流程(校花网)

爬虫文件

import scrapy
from XiaoHuaPro.items import XiaohuaproItem

class XiaohuaSpider(scrapy.Spider):
    name = 'xiaohua'
    # allowed_domains = ['www.xxx.com']
    start_urls = ['http://www.521609.com/daxuemeinv/']
    # 生成一个通用url模板
    url = 'http://www.521609.com/daxuemeinv/list8%d.html'
    page_num = 2

    def parse(self, response):
        li_list = response.xpath('//div[@class="index_img list_center"]/ul/li')
        for li in li_list:
            name = li.xpath("./a[2]/text() | ./a[2]/b/text()").extract_first()
            img_url="http://www.521609.com"+li.xpath('./a[1]/img/@src').extract_first()
			
            # 实例化item类, 将解析到的数据封装至items对象中
            item = XiaohuaproItem()
            item['name'] = name
            item['img_url'] = img_url
            yield item   # #提交item到管道文件
		
        # 对其他页码的url进行手动i请求的发送
        if self.page_num <= 5:
            new_url = format(self.url%self.page_num)
            self.page_num += 1
            yield scrapy.Request(url=new_url, callback=self.parse)

items文件

import scrapy

class XiaohuaproItem(scrapy.Item):
    # define the fields for your item here like: 存储字段
    name = scrapy.Field()
    img_url = scrapy.Field()

管道文件

import pymysql

# 作用:将解析到的数据存储到某一个平台中。
class XiaohuaproPipeline(object):
    fp = None    # 定义一个文件描述符属性
    def open_spider(self, spider):
        print('开始爬虫')
        self.fp = open('./xiaohua.txt', 'w', encoding='utf-8')
	
    # 作用:实现持久化存储的操作
    # 该方法的item参数就可以接收爬虫文件提交过来的item对象
    # 该方法每接收一个item就会被调用一次(调用多次)
    def process_item(self, item, spider):
        name = item['name']
        img_url = item['img_url']
        self.fp.write(name + ":" + img_url + '\n')
        # 返回值的作用:就是将item传递给下一个即将被执行的管道类
        return item

    def close_spider(self, spider):
        print('结束爬虫')
        self.fp.close()

class MysqlPipeline(object):
    conn = None
    cursor = None
    def open_spider(self, spider):
        # 解决数据库字段无法存储中文处理:alter table tableName convert to charset utf8;
        self.conn = pymysql.Connect(host='127.0.0.1',port=3306,user='root',password='123',db='test')
        print(self.conn)
    def process_item(self, item, spider):
        self.cursor = self.conn.cursor()
        try:
            self.cursor.execute('insert into xiahua values ("%s","%s")'%(item['name'],item['img_url']))
            self.conn.commit()
        except Exception as e:
            print(e)
            self.conn.rollback()
        return item
    def close_spider(self, spider):
        self.cursor.close()
        self.conn.close()

settings文件配置

# 字典中的键值表示的是即将被启用执行的管道文件和其执行的优先级。(值越小优先级越高)
ITEM_PIPELINES = {
   'XiaoHuaPro.pipelines.XiaohuaproPipeline': 300,
   'XiaoHuaPro.pipelines.PymysqlPipeline': 301,
}

发处理请求传参(meta参数)

# -*- coding: utf-8 -*-
import scrapy
from moviePro.items import MovieproItem

class MovieSpider(scrapy.Spider):
    name = 'movie'
    # allowed_domains = ['www.xxx.com']
    start_urls = ['https://www.4567tv.tv/index.php/vod/show/id/9.html']
    
    # 接收一个请求传递过来的数据
    def detail_parse(self,response):
        item = response.meta['item']
        desc = response.xpath('/html/body/div[1]//span[2]/text()').extract_first()
        item['desc'] = desc
        yield item
        
    def parse(self, response):
        li_list = response.xpath('//div[@class="stui-pannel_bd"]/ul/li')
        for li in li_list:
            name = li.xpath('.//h4[@class="title"]/a/text()').extract_first()
            detail_url = 'https://www.4567tv.tv'+li.xpath('.//h4[@class="title"]/a/@href').extract_first()
            
            item = MovieproItem()
            item['name'] = name
            # meta是一个字典,字典中所有的键值对都可以传递给指定好的回调函数
            yield scrapy.Request(url=detail_url,callback=self.detail_parse,meta={'item':item})
posted @ 2019-06-27 10:16  言值  阅读(243)  评论(0编辑  收藏  举报