scrapy框架简介

## 【1】 scrapy框架简介

### (1)基本介绍

Scrapy一个开源和协作的框架,其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的,使用它可以以快速、简单、可扩展的方式从网站中提取所需的数据。但目前Scrapy的用途十分广泛,可用于如数据挖掘、监测和自动化测试等领域,也可以应用在获取API所返回的数据(例如 Amazon Associates Web Services ) 或者通用的网络爬虫。Scrapy 是基于twisted框架开发而来,twisted是一个流行的事件驱动的python网络框架。因此Scrapy使用了一种非阻塞(又名异步)的代码来实现并发。

```text

Components:

1、引擎(EGINE)
引擎负责控制系统所有组件之间的数据流,并在某些动作发生时触发事件。

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

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

4、爬虫(SPIDERS)
SPIDERS是开发人员自定义的类,用来解析responses,并且提取items,或者发送新的请求

5、项目管道(ITEM PIPLINES)
在items被提取后负责处理它们,主要包括清理、验证、持久化(比如存到数据库)等操作
下载器中间件(Downloader Middlewares)位于Scrapy引擎和下载器之间,主要用来处理从EGINE传到DOWLOADER的请求request,已经从DOWNLOADER传到EGINE的响应response,

6、爬虫中间件(Spider Middlewares)
位于EGINE和SPIDERS之间,主要工作是处理SPIDERS的输入(即responses)和输出(即requests)

```

[官网链接](https://docs.scrapy.org/en/latest/topics/architecture.htm)

(1)创建项目

创建项目命令:

```python
scrapy startproject NewsPro
cd NewsPro
scrapy genspider wangyi news.163.com
scrapy genspider huanqiu huanqiu.com
```

目录:

```apl
├── NewsPro
│   ├── __init__.py
│   ├── items.py
│   ├── middlewares.py
│   ├── pipelines.py
│   ├── settings.py
│   └── spiders
│       ├── __init__.py
│       ├── huanqiu.py
│       └── wangyi.py
└── scrapy.cfg

```

文件说明:

> - scrapy.cfg  项目的主配置信息,用来部署scrapy时使用,爬虫相关的配置信息在settings.py文件中。
> - items.py   设置数据存储模板,用于结构化数据,如:Django的Model
> - pipelines   数据处理行为,如:一般结构化的数据持久化
> - settings.py 配置文件,如:递归的层数、并发数,延迟下载等。强调:配置文件的选项必须大写否则视为无效,正确写法USER_AGENT='xxxx'
> - spiders    爬虫目录,如:创建文件,编写爬虫规则

`wangyi.py`

```python
import scrapy


class WangyiSpider(scrapy.Spider):
    name = "wangyi"
    allowed_domains = ["news.163.com"]
    start_urls = ["http://news.163.com/"]

    def parse(self, response):
        print("response:::", response.text)
```

启动爬虫程序:

```bash
scrapy crawl wangyi
```

每一次终端启动和麻烦,我们可以在**项目根目录**下新建启动文件,bin.py:

```python
from scrapy.cmdline import execute
execute(['scrapy', 'crawl', 'wangyi', "--nolog"])
```

(2)settings 配置文件

##### 1. 为什么项目中需要配置文件

在配置文件中存放一些公共变量,在后续的项目中方便修改,如:本地测试数据库和部署服务器的数据库不一致

##### 2. 配置文件中的变量使用方法

> 1. 变量名一般全部大写
> 2. 导入即可使用

##### 3. settings.py中的重点字段和含义

```apl
- USER_AGENT 设置ua

- ROBOTSTXT_OBEY 是否遵守robots协议,默认是遵守

- CONCURRENT_REQUESTS 设置并发请求的数量,默认是16个

- DOWNLOAD_DELAY 下载延迟,默认无延迟 (下载器在从同一网站下载连续页面之前应等待的时间(以秒为单位)。这可以用来限制爬行速度,以避免对服务器造成太大影响)

- COOKIES_ENABLED 是否开启cookie,即每次请求带上前一次的cookie,默认是开启的

- DEFAULT_REQUEST_HEADERS 设置默认请求头,这里加入了USER_AGENT将不起作用

- SPIDER_MIDDLEWARES 爬虫中间件,设置过程和管道相同

- DOWNLOADER_MIDDLEWARES 下载中间件

- LOG_LEVEL 控制终端输出信息的log级别,终端默认显示的是debug级别的log信息

  - LOG_LEVEL = "WARNING"
    - CRITICAL  严重
    - ERROR  错误
    - WARNING  警告
    - INFO  消息
    - DEBUG   调试

- LOG_FILE 设置log日志文件的保存路径,如果设置该参数,终端将不再显示信息

  LOG_FILE = "./test.log"
  
- FEED_EXPORT_ENCODING='UTF-8' 保存的是unicode编码字符转为utf8
```

## 【2】 Spider类

Spiders是定义如何抓取某个站点(或一组站点)的类,包括如何执行爬行(即跟随链接)以及如何从其页面中提取结构化数据(即抓取项目)。换句话说,Spiders是您为特定站点(或者在某些情况下,一组站点)爬网和解析页面定义自定义行为的地方。 

```apl
1、 生成初始的Requests来爬取第一个URLS,并且标识一个回调函数
     第一个请求定义在start_requests()方法内默认从start_urls列表中获得url地址来生成Request请求,
     默认的回调函数是parse方法。回调函数在下载完成返回response时自动触发

2、 在回调函数中,解析response并且返回值
     返回值可以4种:
          包含解析数据的字典
          Item对象
          新的Request对象(新的Requests也需要指定一个回调函数)
          或者是可迭代对象(包含Items或Request)

3、在回调函数中解析页面内容
   通常使用Scrapy自带的Selectors,但很明显你也可以使用Beutifulsoup,lxml或其他你爱用啥用啥。

4、最后,针对返回的Items对象将会被持久化到数据库
   通过Item Pipeline组件存到数据库:https://docs.scrapy.org/en/latest/topics/item-pipeline.html)
   或者导出到不同的文件(通过Feed exports:https://docs.scrapy.org/en/latest/topics/feed-exports.html)
  

```

## 【3】 Item

抓取的主要目标是从非结构化源(通常是网页)中提取结构化数据。Scrapy爬虫可以像Python一样返回提取的数据。虽然方便和熟悉,但很容易在字段名称中输入拼写错误或返回不一致的数据,尤其是在具有许多爬虫的较大项目中。

为了定义通用输出数据格式,Scrapy提供了Item类。 Item对象是用于收集抓取数据的简单容器。它们提供类似字典的 API,并具有用于声明其可用字段的方便语法。

### 1、声明项目

使用简单的类定义语法和Field 对象声明项。这是一个例子:

```python
import scrapy
 
class Product(scrapy.Item):
    name = scrapy.Field()
    price = scrapy.Field()
    stock = scrapy.Field()
    last_updated = scrapy.Field(serializer=str)
```

注意那些熟悉Django的人会注意到Scrapy Items被宣告类似于Django Models,除了Scrapy Items更简单,因为没有不同字段类型的概念。

Field对象用于指定每个字段的元数据。例如,last_updated上面示例中说明的字段的序列化函数。

您可以为每个字段指定任何类型的元数据。Field对象接受的值没有限制。出于同样的原因,没有所有可用元数据键的参考列表。

Field对象中定义的每个键可以由不同的组件使用,只有那些组件知道它。您也可以根据Field自己的需要定义和使用项目中的任何其他键。

Field对象的主要目标是提供一种在一个地方定义所有字段元数据的方法。通常,行为取决于每个字段的那些组件使用某些字段键来配置该行为。

### 2、应用(网易新闻)

```python
import scrapy
from items import NewsProItem


class WangyiSpider(scrapy.Spider):
    name = 'wangyi'
    # allowed_domains = ['www.xxx.com']
    start_urls = ['https://news.163.com/']
    cate_list = []  # 每一个板块对应的url
    cate_num_map = {
        "{{__i == 0}}": "新闻",
        "{{__i == 1}}": "北京",
        "{{__i == 2}}": "国内",
        "{{__i == 3}}": "国际",
        "{{__i == 4}}": "独家",
    }

    # 数据解析:每一个板块对应的url
    def parse(self, response):
        with open("news163.html", "w") as f:
            f.write(response.text)

        cate_num_list = response.xpath('//*[contains(@ne-if,"{{")]/@ne-if').extract()
        print("cate_num_list", cate_num_list)

        for cate_num in cate_num_list:
            print("cate_num", cate_num)
            if cate_num in self.cate_num_map:
                cate_title = self.cate_num_map.get(cate_num)
                news_list = response.xpath(f'//*[contains(@ne-if,"{cate_num}")]/div/a')
                print(cate_title)
                print(news_list)

                for news_selector in news_list:
                    news_title = news_selector.xpath('text()').extract_first()
                    news_detail_url = news_selector.xpath('@href').extract_first()
                    cate_title = self.cate_num_map.get(cate_num)
                    print("new_title", news_title)
                    print("new_detail_url", news_detail_url)
                    print("cate_title", cate_title)
                    # 存储数据到item对象
                    item = NewsProItem()
                    item['cate'] = cate_title
                    item['title'] = news_title
                    # 对新闻详情页的url发起请求
                    print("发起新的请求,", news_detail_url)
                    yield scrapy.Request(url=news_detail_url, callback=self.parse_news_detail, meta={'item': item})

    def parse_news_detail(self, response):
        # 解析新闻内容
        # content = response.xpath('//*[@id="content"]/div[@class="post_body"]/p[contains(@id,"1M")]/text()').extract()
        content = response.xpath('//*[@id="content"]/div[@class="post_body"]/p/text()').extract()
        content = ''.join([i.strip() for i in content])
        item = response.meta['item']
        item['content'] = content
        if content:
            print("item", item)
            yield item

    # 爬虫类父类的方法,该方法是在爬虫结束最后一刻执行
    def closed(self, spider):
        # self.bro.quit()
        print("此次爬虫全部结束!")

```

## 【4】 Item PipeLine

在一个项目被蜘蛛抓取之后,它被发送到项目管道,该项目管道通过顺序执行的几个组件处理它。

每个项目管道组件(有时简称为“项目管道”)是一个实现简单方法的Python类。他们收到一个项目并对其执行操作,同时决定该项目是否应该继续通过管道或被丢弃并且不再处理。

项目管道的典型用途是:

> - cleansing HTML data
> - validating scraped data (checking that the items contain certain fields)
> - checking for duplicates (and dropping them)
> - storing the scraped item in a database

### 1 编写自己的项目管道

```python
'''
每个项管道组件都是一个必须实现以下方法的Python类:

process_item(self,项目,蜘蛛)
为每个项目管道组件调用此方法。process_item() 

必须要么:返回带数据的dict,返回一个Item (或任何后代类)对象,返回Twisted Deferred或引发 DropItem异常。丢弃的项目不再由其他管道组件处理。

此外,他们还可以实现以下方法:

open_spider(self,蜘蛛)
打开蜘蛛时会调用此方法。

close_spider(self,蜘蛛)
当蜘蛛关闭时调用此方法。

from_crawler(cls,crawler )
如果存在,则调用此类方法以从a创建管道实例Crawler。它必须返回管道的新实例。Crawler对象提供对所有Scrapy核心组件的访问,
如设置和信号; 它是管道访问它们并将其功能挂钩到Scrapy的一种方式。
'''
```

### 2 项目管道示例

#### (1) 价格验证和丢弃物品没有价格

让我们看看下面的假设管道,它调整 `price`那些不包含增值税(`price_excludes_vat`属性)的项目的属性,并删除那些不包含价格的项目:

```python
from scrapy.exceptions import DropItem
 
class PricePipeline(object):
 
    vat_factor = 1.15
 
    def process_item(self, item, spider):
        if item['price']:
            if item['price_excludes_vat']:
                item['price'] = item['price'] * self.vat_factor
            return item
        else:
            raise DropItem("Missing price in %s" % item)
```

#### (2) 将项目写入JSON文件

以下管道将所有已删除的项目(来自所有蜘蛛)存储到一个`items.jl`文件中,每行包含一个以JSON格式序列化的项目:

注意JsonWriterPipeline的目的只是介绍如何编写项目管道。如果您确实要将所有已删除的项目存储到JSON文件中,则应使用Feed导出。

```python
import json
 
class JsonWriterPipeline(object):
 
    def open_spider(self, spider):
        self.file = open('items.jl', 'w')
 
    def close_spider(self, spider):
        self.file.close()
 
    def process_item(self, item, spider):
        line = json.dumps(dict(item)) + "\n"
        self.file.write(line)
        return item
```

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值