Scrapy 基础

Scrapy

异步处理框架,可配置和可扩展程度非1 常高,Python中使用最广泛的爬虫框架

安装:

Ubuntu安装
1、安装依赖包

  • sudo apt-get install libffi-dev libssl- dev libxml2-dev python3-dev libxslt1-dev zlib1g-dev
  • sudo pip3 install -I -U service_identity

2、安装scrapy框架

  • sudo pip3 install Scrapy

windows安装

python -m pip install Scrapy

一、概述

1. 五大组件

  • 引擎(Engine) :整个框架核心
  • 调度器(Scheduler) :维护请求队列
  • 下载器(Downloader):获取响应对象
  • 爬虫文件(Spider) :数据解析提取
  • 项目管道(Pipeline):数据入库处理

下载器中间件(Downloader Middlewares) : 引擎 ->下载器,包装请求(随机代理等)

蜘蛛中间件(Spider Middlewares) : 引擎 -> 爬虫文件,可修改响应对象属性

2. 项目启动

  • 由引擎向爬虫程序索要第一个要爬取的URL,交给调度器去入队列
  • 调度器处理请求后出队列,通过下载器中间件交给下载器去下载
  • 下载器得到响应对象后,通过蜘蛛中间件交给爬虫程序
  • 爬虫程序进行数据提取:
  • 数据交给管道文件去入库处理
  • 对于需要继续跟进的URL,再次交给调度器入队列,依次循环

3. 常用命令

1、创建爬虫项目

scrapy startproject 项目名

2、创建爬虫文件

scrapy genspider 爬虫名 域名

3、运行爬虫

scrapy crawl 爬虫名

4. 项目目录

Baidu # 项目文件夹
├── Baidu # 项目目录
│ ├── items.py # 定义数据结构
│ ├── middlewares.py # 中间件
│ ├── pipelines.py # 数据处理
│ ├── settings.py # 全局配置文件
│ └── spiders
│ ├── baidu.py # 爬虫文件
└── scrapy.cfg # 项目基本配置文件

5. 全局配置文件

1、定义User-Agent

USER_AGENT = 'Mozilla/5.0'

2、是否遵循robots协议,一般设置为False

ROBOTSTXT_OBEY = False

3、最大并发量,默认为16

CONCURRENT_REQUESTS = 32

4、下载延迟时间

DOWNLOAD_DELAY = 1

5、请求头,此处也可以添加User-Agent

DEFAULT_REQUEST_HEADERS={}

6、项目管道

ITEM_PIPELINES={
	'项目目录名.pipelines.类名':300
}

6.创建爬虫项目步骤

1、新建项目 :scrapy startproject 项目名
2、cd 项目文件夹
3、新建爬虫文件 :scrapy genspider 文件名 域名
4、明确目标(items.py)
5、写爬虫程序(文件名.py)
6、管道文件(pipelines.py)
7、全局配置(settings.py)
8、运行爬虫 :scrapy crawl 爬虫名

7.运行爬虫

1、创建begin.py(和scrapy.cfg文件同目录)
2、begin.py中内容:

from scrapy import cmdline
cmdline.execute('scrapy crawl maoyan'.split())

Pycharm运行爬虫

  • 创建run.py(和scrapy.cfg文件同目录)
  • run.py中内容:
from scrapy import cmdline
cmdline.execute('scrapy crawl maoyan'.split())

8. 示例

爬取百度首页的标签--百度一下,你就知道

步骤:

  • 创建项目Baidu 和 爬虫文件baidu

    scrapy startproject Baidu # 创建项目
    cd Baidu
    scrapy genspider baidu www.baidu.com # 创建爬虫文件
    
  • 编写爬虫文件baidu.py,xpath提取数据

    # -*- coding: utf-8 -*-
    import scrapy
    class BaiduSpider(scrapy.Spider):
    name = 'baidu'
    allowed_domains = ['www.baidu.com']
    start_urls = ['http://www.baidu.com/']
    def parse(self, response):
    result = response.xpath('/html/head/title/text()').extract_first()
    print('*'*50)
    print(result)
    print('*'*50)
    
  • 全局配置文件settings.py

    USER_AGENT = 'Mozilla/5.0'
    ROBOTSTXT_OBEY = False
    DEFAULT_REQUEST_HEADERS = {
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'en',
    }
    
  • 创建run.py

    from scrapy import cmdline
    cmdline.execute('scrapy crawl baidu'.split())
    
  • 启动爬虫

    直接运行run.py即可

运行截图

二、奇淫技巧

  • 节点对象.xpath('')

    • 列表,元素为选择器 [']
    • 列表.extract() :序列化列表中所有选择器为Unicode字符串 ['A','B','C']
    • 列表.extract_first() 或者 get() :获取列表中第1个序列化的元素(字符串)
  • pipelines.py中必须由1个函数叫process_item

    def process_item(self,item,spider):
        return item ( * 此处必须返回 item )
    
  • 日志变量及日志级别(settings.py)

    • 日志相关变量
      • LOG_LEVEL = ''
      • LOG_FILE = '文件名.log'
    • 日志级别
      • 5 CRITICAL :严重错误
      • 4 ERROR :普通错误
      • 3 WARNING :警告
      • 2 INFO :一般信息
      • 1 DEBUG :调试信息
  • 管道文件使用

    • 在爬虫文件中为items.py中类做实例化,用爬下来的数据给对象赋值

      from ..items import MaoyanItem
      item = MaoyanItem()
      
    • 管道文件(pipelines.py)

    • 开启管道(settings.py)
      ITEM_PIPELINES = { '项目目录名.pipelines.类名':优先级 }

三、猫眼电影

1.创建项目

创建项目文件,爬虫文件

scrapy startproject Maoyan
cd Maoyan
scrapy genspider maoyan https://maoyan.com

创建运行文件

# run.py
from scrapy import cmdline
cmdline.execute('scrapy crawl maoyan'.split())

设置全局设置

# settings.py 只展示需要更改的地方

ROBOTSTXT_OBEY = False

DEFAULT_REQUEST_HEADERS = {
  'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  'Accept-Language': 'en',
  'User-Agent': 'Mozilla/5.0'
}

2. 定义数据结构

./Maoyan/items.py

class MaoyanItem(scrapy.Item):
    # define the fields for your item here like:
    name = scrapy.Field()
    star = scrapy.Field()
    time = scrapy.Field()

3. 定义spider

./Maoyan/Spiders/maoyan.py

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

class MaoyanSpider(scrapy.Spider):
    name = 'maoyan'
    allowed_domains = ['maoyan.com']

    # 起始的URL地址,即第一页地址
    start_urls = ['https://maoyan.com/board/4?offset=0']

    def parse(self, response):
        # 基准的xpath
        dd_list = response.xpath('//dl[@class="board-wrapper"]/dd')
        # 依次遍历
        for dd in dd_list:
            # 创建item对象(./Maoyan/items.py)
            item = MaoyanItem()
            # 电影名称
            item['name'] = dd.xpath('./a/@title').extract_first().strip()
            # 电影主演
            item['star'] = dd.xpath('.//p[@class="star"]/text()').extract_first().strip()
            # 电影上映时间
            item['time'] = dd.xpath('.//p[@class="releasetime"]/text()').extract_first().strip()

            # 把爬取的数据交给管道文件piplines处理
            yield item 
        
        self.offset += 10
        if self.offset <= 90:
            url = 'https://maoyan.com/board/4?offset={}'.format(self.offset) # 拼接下一页的地址
            # 交给调度器入队列
            yield scrapy.Request(url = url, callback=self.parse)

当一个函数中出现了yield关键字时,则python将该函数当做生成器使用,也就是说,函数执行到yield时,将数据传递给piplines,并且函数暂停到此处,等待下一次的调用,当再次调用的时候,则会从上一次的地方继续开始,直到调用结束,scrapy也会捕获这个异常

4. 定义管道文件

./Maoyan/pipelines.py

class MaoyanPipeline(object):
    def process_item(self, item, spider):
        print(item['name'])
        print(item['star'])
        print(item['time'])
        return item

开启管道需要设置settings.py

ITEM_PIPELINES = {
   'Maoyan.pipelines.MaoyanPipeline': 300,
}

300代表优先级,优先级的范围是1-1000,数字越小,优先级越高

5. 小结

以上方法已经足以爬取需要的数据,但是,在运行的过程中,URL在入队列后又出队列,到了yield之后又交给调度器,如此往复循环,并没有发挥scrapy的多线程优势,届时,需要修改代码来充分的使用多线程技术。

5.1 法一

修改maoyan.py

原理:将所有的URL全部交给调度器

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

class MaoyanSpider(scrapy.Spider):
    name = 'maoyan'
    allowed_domains = ['maoyan.com']

    # 起始的URL地址,即第一页地址
    start_urls = ['https://maoyan.com/board/4?offset=0']
    offset = 0

    def parse(self, response):
        for offset in range(0, 91, 10):
            url = 'https://maoyan.com/board/4?offset={}'.format(str(offset))
            # 把地址交给调度器入队列
            yield scrapy.Request(url=url,callback=self.parse_html)

    def parse_html(self, response):
        # 基准xpath,匹配每个电影信息节点对象列表
        dd_list = response.xpath('//dl[@class="board-wrapper"]/dd')
        # dd_list : [<element dd at xxx>,<...>]
        for dd in dd_list:
            # 创建item对象
            item = MaoyanItem()
            # [<selector xpath='' data='霸王别姬'>]
            # dd.xpath('')结果为[选择器1,选择器2]
            # .extract() 把[选择器1,选择器2]所有选择器序列化为unicode字符串
            # .extract_first() : 取第一个字符串
            item['name'] = dd.xpath('./a/@title').extract_first().strip()
            item['star'] = dd.xpath('.//p[@class="star"]/text()').extract()[0].strip()
            item['time'] = dd.xpath('.//p[@class="releasetime"]/text()').extract()[0]

            yield item

这里有个问题,parse函数用来解析,但是在这里却是交付URL,物未尽其用,所以再次修改

5.2 法二

既然离不开start_url,那么就剖其源码可以得到,该变量应用于start_requests函数

以下为start_requests()源码:

def start_requests(self):
    cls = self.__class__
    if method_is_overridden(cls, Spider, 'make_requests_from_url'):
        warnings.warn(
            "Spider.make_requests_from_url method is deprecated; it "
            "won't be called in future Scrapy releases. Please "
            "override Spider.start_requests method instead (see %s.%s)." % (
                cls.__module__, cls.__name__
            ),
        )
        for url in self.start_urls:
            yield self.make_requests_from_url(url)
            else:
                for url in self.start_urls:
                    yield Request(url, dont_filter=True)

那么就可以重写该方法,只需要再次定义start_requests函数即可,根据优先级则先执行本文件的start_requests方法

def start_requests(self):
    for offset in range(0, 91, 10):
        url = 'https://maoyan.com/board/4?offset={}'.format(str(offset))
        # 交给调度器
        yield scrapy.Request(url=url, callback=self.parse_html)

完整maoyan.py代码

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

class MaoyanSpider(scrapy.Spider):
    name = 'maoyan'
    allowed_domains = ['maoyan.com']

    # 重写start_requests方法,提高效率
    def start_requests(self):
        for offset in range(0, 91, 10):
            url = 'https://maoyan.com/board/4?offset={}'.format(str(offset))
            # 交给调度器
            yield scrapy.Request(url=url, callback=self.parse_html)

    def parse_html(self, response):
        # 基准xpath,匹配每个电影信息节点对象列表
        dd_list = response.xpath('//dl[@class="board-wrapper"]/dd')
        # dd_list : [<element dd at xxx>,<...>]
        for dd in dd_list:
            # 创建item对象
            item = MaoyanItem()
            # [<selector xpath='' data='霸王别姬'>]
            # dd.xpath('')结果为[选择器1,选择器2]
            # .extract() 把[选择器1,选择器2]所有选择器序列化为unicode字符串
            # .extract_first() : 取第一个字符串
            item['name'] = dd.xpath('./a/@title').extract_first().strip()
            item['star'] = dd.xpath('.//p[@class="star"]/text()').extract()[0].strip()
            item['time'] = dd.xpath('.//p[@class="releasetime"]/text()').extract()[0]

            yield item
posted @ 2019-09-20 08:47  ChanceySolo  阅读(239)  评论(0编辑  收藏  举报