scrapy框架

1. scrapy框架简介

  Scrapy是一个为爬取网站数据、提取结构性数据而设计的应用程序框架,它可以应用在广泛领域:Scrapy 常应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。通常我们可以很简单的通过 Scrapy 框架实现一个爬虫,抓取指定网站的内容或图片。 
  尽管Scrapy原本是设计用来屏幕抓取(更精确的说,是网络抓取),但它也可以用来访问API来提取数据。

2.scrapy框架

1. 整体架构

Scrapy 使用了 Twisted异步网络库来处理网络通讯。整体架构大致如下

scrapy框架

2. Scrapy主要包括了以下组件:

Scrapy Engine(引擎)

负责Spider、ItemPipeline、Downloader、Scheduler中间的通讯,信号、数据传递等。

用来处理整个系统的数据流处理,触发事务(加载核心)。

Scheduler(调度器)

它负责接受引擎发送过来的Request请求,并按照一定的方式进行整理排列,入队,当引擎需要时,交还给引擎。

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

Downloader(下载器): 

负责下载Scrapy Engine(引擎)发送的所有Requests请求,并将其获取到的Responses交还给Scrapy Engine(引擎),由引擎交给Spider来处理。

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

Spider(爬虫)

它负责处理所有Responses,从中分析提取数据,获取Item字段需要的数据,并将需要跟进的URL提交给引擎,再次进入Scheduler(调度器)。

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

Item Pipeline(管道)

它负责处理Spider中获取到的Item,并进行进行后期处理(详细分析、过滤、存储等)的地方。

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

Downloader Middlewares(下载中间件)

一个可以自定义扩展下载功能的组件。

Spider Middlewares(Spider中间件)

一个可以自定扩展和操作引擎和Spider中间通信的功能组件。

3. Scrapy运行流程大概如下:

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

3. scrapy框架集成的功能

  1. 高性能的数据解析操作(xpath)
  2. 高性能的数据下载
  3. 高性能的持久化存储
  4. 中间件
  5. 全栈数据爬取操作
  6. 分布式:redis
  7. 请求传参的机制(深度爬取)
  8. scarpy中合理的应用selenium

4. 环境安装

1. linux和max上的安装

pip3 install scrapy

2. window上的安装

# 1.安装wheel
1.pip3 install wheel

# 2.下载twisted
http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
    
# 3.安装twisted
进入下载目录,执行pip3 install Twisted?17.1.0?cp35?cp35m?win_amd64.whl

# 4.安装pywin32
pip3 install pywin32

# 5.安装scrapy
pip3 install scrapy

5.创建工程

# 在pyCharm的终端执行

# 切换到当前目录下
cd scrapy_practice

# 创建一个项目
scrapy startproject proname 

# 切换到当前项目文件目录下
cd proname  

# 创建爬虫文件 
scrapy genspider spidername www.xxx.com  

# 执行命令爬虫的文件
scrapy crawl spidername    
 
   
# settings配置文件
1.默认是遵从robots协议的,ROBOTSTXT_OBEY = True改为False,就不遵从robots协议
2.UA伪装
3.LOG_LEVEL = "ERROR" # 修改日志的输出等级,让日志值输出错误信息

爬虫文件的初始

import scrapy


class FirstSpider(scrapy.Spider):
    # 爬虫文件的名称;爬虫源文件的唯一标识
    name = "first"
    # 允许的域名:就是对起始的url列表进行限制(通常情况下都会注释)
    allowed_domains = [‘www.xxx.com‘]  # 起始的url列表:列表中的列表元素会被scrapy自动的进行请求发送,可以存放多个 
    start_urls = [‘http://www.xxx.com/‘,‘http://www.xxx2.com/‘]# 解析数据
    def parse(self, response):
        print(response)

6. scrapy的数据解析

extract():列表是有多个列表元素
extract_first():列表元素只有单个
import scrapy

class FirstSpider(scrapy.Spider):
    # 爬虫文件的名称:爬虫源文件的唯一标识
    name = ‘first‘
    # 允许的域名:
    allowed_domains = [‘www.baidu.com‘]
    
    #起始的url列表:列表中的列表元素会被scrapy自动的进行请求发送
    start_urls = [‘https://dig.chouti.com/‘]
    
    #解析数据
    def parse(self, response):
      
        div_list = response.xpath(‘/html/body/main/div/div/div[1]/div/div[2]/div[1]‘)
        for div in div_list:
            # 注意:xpath返回的列表中的列表元素是Selector对象,我们要解析获取的字符串的数据是存储在该对象中必须经过一个extract()的操作才可以将改对象中存储的字符串的数据获取
            # content = div.xpath(‘./div/div/div[1]/a/text()‘)[0].extract()
            content = div.xpath(‘./div/div/div[1]/a/text()‘).extract_first()
            # xpath返回的列表中的列表元素有多个(Selector对象),想要将每一个列表元素对应的Selector中的字符串取出改如何操作?response.xpath(‘/div//text()‘).extract()
            print(content) # <Selector xxx=‘dsdsd‘ data="短发hi的说法绝对是">

7.serapy的持久化存储

1.基于终端指令

只可以将parse方法的返回值存储到磁盘文件中

# 返回值的数据统一写入当前file.csv文件中,如果文件不存在就会新建文件
scrapy crawl first -o file.csv    

# 对文件的后缀有限制,支持的文件后缀有:(‘json‘, ‘jsonlines‘, ‘jl‘, ‘csv‘, ‘xml‘, ‘marshal‘, ‘pickle‘)
import scrapy

class HuyaSpider(scrapy.Spider):
    name = ‘huya‘
    # allowed_domains = [‘www.xxx.com‘]
    start_urls = [‘https://www.huya.com/g/xingxiu/‘]

    def parse(self, response):
        print(response)
        li_list = response.xpath(‘//*[@id="js-live-list"]/li‘)
        dic_list = []
        for li in li_list:
            title = li.xpath(‘./a[2]/text()‘).extract_first()
            authod = li.xpath(‘./span/span[1]/i/text()‘).extract_first()
            hot = li.xpath(‘./span/span[2]/i[2]/text()‘).extract_first()
            dic = {
                "title": title,
                "authod": authod,
                "hot": hot,
            }
            dic_list.append(dic)
        return dic_list
# 在终端命令输出 D:\practice\crawler_practice\scrapy框架的应用\huyaproject>scrapy crawl huya -o huya.csv

2.基于管道:pipelines.py

编码流程:

  1. 数据解析
  2. 在item的类中定义相关的属性
  3. 将解析的数据存储封装到item类型的对象中 .item["属性"]
  4. 将item对象提交给管道
  5. 遭管道类中的process_item方法负责接收item对选,然后对item进行任意形式的持久化存储
  6. 在配置文件中开启管道

  scrapy框架

细节补充:

  • 管道文件中的一个管道了表示将数据存储到某一种形式的平台
  • 如果管道文件中定义了多个管道类,爬虫类提交的item会给优先级最高的管道类。(数字越小,优先级越高)
  • process_item方法的实现中的return item的操作表示将item传递给下一个即将被执行的管道类。
# huya.py
import scrapy
from huyaPro.items import HuyaproItem

class HuyaSpider(scrapy.Spider):
    name = ‘huya‘
    # allowed_domains = [‘www.xxx.com‘]
    start_urls = [‘https://www.huya.com/g/xingxiu‘]
   
    def parse(self, response):
        li_list = response.xpath(‘//*[@id="js-live-list"]/li‘)
        for li in li_list:
            title = li.xpath(‘./a[2]/text()‘).extract_first()
            author = li.xpath(‘./span/span[1]/i/text()‘).extract_first()
            hot = li.xpath(‘./span/span[2]/i[2]/text()‘).extract_first()

            # 实例化item类型的对象
            item = HuyaproItem()
            item[‘title‘] = title
            item[‘author‘] = author
            item[‘hot‘] = hot

            yield item # 提交给管道
            
# items.py
import scrapy
class HuyaproItem(scrapy.Item):
    # define the fields for your item here like:
    title = scrapy.Field()  # Field是一个万能的数据类型(可以任意类型的数据)
    author = scrapy.Field()
    hot = scrapy.Field()
    
# pipeline.py   
import pymysql
from redis import Redis

# 写入文件中
class HuyaprojectPipeline(object):
    fp = None
    
    def open_spider(self,spider):
        print("只会在爬虫开始的时候执行一次")
        self.fp = open("huyazhibo.txt","w",encoding="utf-8")
        
    def process_item(self, item, spider): # item就是接收到爬虫类提交过来的item对象
        self.fp.write(item[‘title‘]+‘:‘+item[‘author‘]+‘:‘+item[‘hot‘]+‘\n‘)
        print(item["item"],"写入成功")
        return item
    
    def close_spider(self,spider):
        print("只会在爬虫结束的时候的调用一次")
        self.fp.close()
     


# 写入mysql数据库中
class mysqlPipeLine(object):
    conn = None
    cursor = None
    def open_spider(self,spider):
        self.conn = pymysql.Connect(
            host=‘127.0.0.1‘,
            port=3306,
            user=‘root‘,
            password=‘123‘,
            db=‘Spider‘,
            charset=‘utf8‘)
    def process_item(self,item,spider):
        sql = ‘insert into huya values("%s","%s","%s")‘%(item[‘title‘],item[‘author‘],item[‘hot‘])
        self.cursor = self.conn.cursor()
        try:
            self.cursor.execute(sql)
            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()

# 写radis数据库中
class RedisPipeLine(object):
    conn = None
    def open_spider(self,spider):
        self.conn = Redis(host=‘127.0.0.1‘,port=6379)
    def process_item(self,item,spider):
        self.conn.lpush(‘huyaList‘,item)
        return item

待续