栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

Python爬虫|Scrapy 基础用法

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Python爬虫|Scrapy 基础用法

scrapy 框架

目录
  • scrapy 框架
    • 1. scrapy 是什么?
      • ① scrapy 架构组成 (基本模块概述)
      • ② scrapy 工作原理
      • ③ scrapy 工作原理图
    • 2. scrapy 框架的基本使用
      • ① 基本使用方法
      • ② spiders 自定义文件内部
      • ③ response 常用的方法
    • 3. scrapy shell
      • ① 什么是 scrapy shell?
      • ② 安装 ipython
      • ③ 进入到 scrapy终端
    • 4. pipelines 管道封装
      • 4.1 yield 关键字
      • 4.2 管道封装(单管道)
        • ① 确定数据结构
        • ② 获取网页内容
        • ③ 管道操作
        • ④ 开启管道(单)
      • 4.3 开启多管道
    • 5. 日志信息和日志等级
    • 6. scrapy 的 post请求
    • 7. 设置代理
    • 8. CrawlSpider
      • ① CrawlSpider 概述
      • ② 运行原理图
      • ③ CrawlSpider案例
    • 9. ★ 写代码注意点(业务逻辑)
    • 10. 案例代码 (使用CrawlSpider)
      • ① football.py (spiders)
      • ② items.py
      • ③ piplines.py
      • ④ settings.py
      • ⑤ start.py (启动文件)
    • 11. scrapy 的一些注意点 (特性)
      • ① scrapy 采用异步下载
      • ② 使用默认方法下载图片注意点
      • ③ meta 为只读属性
      • ④ 导入 settings 失败

1. scrapy 是什么?

Scrapy 是一个为了爬取网站数据,提取结构性数据而编写的应用框架。 可以应用在包括数据挖掘,信息处理
或存储历史数据等一系列的程序中。

① scrapy 架构组成 (基本模块概述)
  1. 调度器(Scheduler)
      调度器,可以把它假设成为一个URL(抓取网页的网址或者说是链接)的优先队列,由它来决定下一个要抓取的网址是什么,同时去除重复的网址(不做无用功)。用户可以自己的需求定制调度器。
  2. 下载器(Downloader)
      下载器,是所有组件中负担最大的,它用于高速地下载网络上的资源。Scrapy 的下载器代码不会太复杂,但效率高,主要的原因是整个 Scrapy框架都在建立在 twisted 这个高效的异步模型上的, Scrapy 下载器也是是建立在 twisted 这个高效的异步模型上的。
  3. 爬虫(Spider)
      爬虫,是用户最关心的部分。用户定制自己的爬虫(通过定制正则表达式等语法),用于从特定的网页中提取自己需要的信息,即所谓的实体(Item)。例如使用 Xpath 提取感兴趣的信息。
    用户也可以从中提取出链接,让Scrapy继续抓取下一个页面。
  4. 实体管道(Item Pipeline)
      实体管道,用于接收网络爬虫传过来的数据,并经过几个特定的次序处理数据,典型的任务包括验证实体的有效性、清除不需要的信息、存入数据库(持久化实体)、存入文本文件等。
  5. Scrapy引擎(Scrapy Engine)
      Scrapy 引擎是整个框架的核心,用来处理整个系统的数据流,触发各种事件。它用来控制调试器、下载器、爬虫。实际上,引擎相当于计算机的CPU,它控制着整个流程。
  6. 中间件(Middlewares)
      整个 Scrapy 框架有很多中间件,如下载器中间件、网络爬虫中间件等,这些中间件相当于过滤器,夹在不同部分之间截获数据流,并进行特殊的加工处理。
② scrapy 工作原理
(1)引擎 ‐‐‐> 自动运行,无需关注,会自动组织所有的请求对象,分发给下载器 
(2)下载器 ‐‐‐> 从引擎处获取到请求对象后,请求数据 
(3)spiders ‐‐‐> Spider类定义了如何爬取某个(或某些)网站。包括了爬取的动作(例 如:是否跟进链接)以及如何从网页的内容中提取结构化数据(爬取item)。 换句话说,Spider就是您定义爬取的动作及 分析某个网页(或者是有些网页)的地方。 
(4)调度器 ‐‐‐> 有自己的调度规则,无需关注 
(5)管道(Item pipeline) ‐‐‐> 最终处理数据的管道,会预留接口供我们处理数据 
    当Item在Spider中被收集之后,它将会被传递到Item Pipeline,一些组件会按照一定的顺序执行对Item的处理。 每个item pipeline组件(有时称之为“Item Pipeline”)是实现了简单方法的Python类。他们接收到Item并通过它执行 一些行为,同时也决定此Item是否继续通过pipeline,或是被丢弃而不再进行处理。 
 以下是item pipeline的一些典型应用: 
     1. 清理HTML数据 
     2. 验证爬取的数据(检查item包含某些字段) 
     3. 查重(并丢弃) 
     4. 将爬取结果保存到数据库中
③ scrapy 工作原理图

2. scrapy 框架的基本使用 ① 基本使用方法
1. 创建 scrapy项目:
    先切换到项目所要放置的位置: cd /d D:Pycharmuntitled2Python-爬虫10. Scrapy 框架
    终端输入: scrapy startproject 项目名称
        注意:
            1. 项目名称不能以数字开头!
            2. 爬虫文件不能以汉字开头,项目名称可以,但不建议

    项目组成:
        spiders
            __init__.py 自定义的爬虫文件.py ‐‐‐> 由我们自己创建,是实现爬虫核心功能的文件
        __init__.py 
		items.py ‐‐‐> 定义数据结构的地方,是一个继承自 scrapy.Item的类
        middlewares.py ‐‐‐> 中间件 代理
        pipelines.py ‐‐‐> 管道文件,里面只有一个类(可以增加),用于处理筛选出来的的实体数据(如将数据存储在数据库中),默认是300优先级,值越小优先级越高(1‐1000)
        settings.py ‐‐‐> 配置文件 比如:是否遵守robots协议,User‐Agent定义等

2. 创建爬虫文件
        (1) 切换到项目中的 spiders目录下
         cd 目录名字/目录名字/spiders
         cd scrapy_baidu_01scrapy_baidu_01spiders

        (2) 创建爬虫文件
         scrapy genspider 爬虫名字 网页的域名
         scrapy genspider baidu http://www.baidu.com

        爬虫文件的基本组成:
            继承scrapy.Spider类
                name = 'baidu' ‐‐‐>  运行爬虫文件时使用的名字
                allowed_domains ‐‐‐>  爬虫允许的域名,在爬取的时候,如果不是此域名之下的 url,会被过滤掉
                start_urls ‐‐‐>  声明了爬虫的起始地址,可以写多个url,一般是一个
                parse(self, response) ‐‐‐> 解析数据的回调函数
                    response.text ‐‐‐> 响应的是字符串
                    response.body ‐‐‐> 响应的是二进制文件
                    response.xpath()‐> xpath方法的返回值类型是selector列表
                    extract() ‐‐‐> 提取的是 selector对象的是 data
                    extract_first() ‐‐‐> 提取的是 selector列表中的第一个数据

3. 运行爬虫代码
    scrapy crawl 爬虫名称
    【注意】:应在 spiders 文件夹内执行
    将 settings文件中 的 ROBOTSTXT_OBEY = True 注释掉,就不用遵守 robots君子协议了

【注意1】:start.py 文件和 scrapy_cfg 文件处于同一级目录

【注意2】:此启动方法会使得代码中的相对路径出错,原本是相对于 piplines.py 或者 settings.py 所在目录那一级,现在是相对于 start.py 文件!

设置请求头

在 settings.py 文件中取消以下代码段,添加请求头

# Override the default request headers:
DEFAULT_REQUEST_HEADERS = {
   'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,**;q=0.8',  
    'Accept-Language': 'en'
}

# 局部设置使用的请求头
# 爬虫程序类中设置
    custom_settings = {
        '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 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'
        }
    }

DOWNLOAD_DELAY

默认值:0

Downloader从同一网站上访问页面应该等待的时间。这个设置常常被用来控制爬取的速度,以免对网站造成太大的压力。这个值的单位为秒,并且可以使用小数。

这个值同样被RANDOMIZE_DOWNLOAD_DELAY所影响。在默认的情况下,Scrapy不会每次都等待一个固定的时间,而是使用一个范围在0.5-1.5之间的值乘以DOWNLOAD_DELAY。

当CONCURRENT_REQUESTS_PER_IP的值不为0时,延迟将按照每个IP判断,而不再是Domain。

这个设置可以在Spider中通过download_delay属性进行设置。

RANDOMIZE_DOWNLOAD_DELAY

默认值:True

如果启用,那么DOWNLOAD_DELAY等待的时间将会随机乘以范围在0.5-1.5之间的一个小数。这个设置的作用是使爬虫的等待时间更有随机性,更难以被检测。
10. 案例代码 (使用CrawlSpider)

普通版本_1 (不使用 CrawlSpider) 下载多层请求、多页数据(图片):Scrapy 下载多层请求、多页图片(下载使用urllib.request.urlretrieve方法)
普通版本_2 (不使用 CrawlSpider) 下载多层请求、多页数据(图片):Scrapy 下载多层请求、多页图片 (重写get_media_requests、file_path方法)

① football.py (spiders)
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from ..items import ScrapyFootballItem

class FootballSpider(CrawlSpider):
    name = 'football'
    allowed_domains = ['mynba.tv']
    start_urls = ['http://www.mynba.tv/video/?page=1']

    rules = (
        Rule(LinkExtractor(allow=r'?page=d+'), follow=True),
        Rule(LinkExtractor(allow=r'/video/.*?.html'), callback='parse_item', follow=False),
    )

    def parse_item(self, response):
        # 匹配数据
        game_title = response.xpath('//*[@id="app"]/div/div[4]/div/div[2]/p[1]/text()').extract_first()
        name_1 = response.xpath('//*[@id="app"]/div/div[4]/div/div[1]/p/a/text()').extract_first()
        logo_src_1 = response.xpath('//*[@id="app"]/div/div[4]/div/div[1]/img/@src').extract_first()
        name_2 = response.xpath('//*[@id="app"]/div/div[4]/div/div[3]/a/text()').extract_first()
        logo_src_2 = response.xpath('//*[@id="app"]/div/div[4]/div/div[3]/img/@src').extract_first()
        info = ScrapyFootballItem(game_title=game_title, name_1=name_1, logo_src_1=logo_src_1, name_2=name_2,logo_src_2=logo_src_2)
        yield info

② items.py
import scrapy

class ScrapyFootballItem(scrapy.Item):
    game_title = scrapy.Field()  # 比赛详情标题
    name_1 = scrapy.Field()  # 主队球队名称
    logo_src_1 = scrapy.Field()  # 主队球队logo
    name_2 = scrapy.Field()  # 客队球队名称
    logo_src_2 = scrapy.Field()  # 客队球队logo
    
③ piplines.py
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html


# useful for handling different item types with a single interface
from itemadapter import ItemAdapter

import xlwt


class ScrapyFootballPipeline:
    def open_spider(self, spider):
        self.workbook = xlwt.Workbook()
        self.worksheet = self.workbook.add_sheet('sheet1')
        self.line_cnt = 0
        self.col_name = ['game_title', 'name_1', 'logo_src_1', 'name_2', 'logo_src_2']
        # 写入表头
        for i in range(4):
            self.worksheet.write(self.line_cnt, i, self.col_name[i])
        self.line_cnt += 1

    def process_item(self, item, spider):
        # 写入数据
        for i in range(4):
            self.worksheet.write(self.line_cnt,i,item[self.col_name[i]])
        self.line_cnt += 1
        self.workbook.save('football_data.xls')

        return item  # 必须加,否则其他管道就无法获得item了!



import urllib.request  # 用于下载图片
class ScrapyFootballPipeline_2:
    def process_item(self, item, spider):
        img_src_1 = item['logo_src_1']
        img_src_2 = item['logo_src_2']
        name_1 = img_src_1.split('/')[-1]
        name_2 = img_src_2.split('/')[-1]
        urllib.request.urlretrieve(img_src_1,filename=f'img/{name_1}')
        urllib.request.urlretrieve(img_src_2,filename=f'img/{name_2}')
        return item
④ settings.py
BOT_NAME = 'scrapy_football'

SPIDER_MODULES = ['scrapy_football.spiders']
NEWSPIDER_MODULE = 'scrapy_football.spiders'

ROBOTSTXT_OBEY = False

DOWNLOAD_DELAY = 3
RANDOMIZE_DOWNLOAD_DELAY = True

ITEM_PIPELINES = {
   'scrapy_football.pipelines.ScrapyFootballPipeline': 300,
   'scrapy_football.pipelines.ScrapyFootballPipeline_2': 301,
}

⑤ start.py (启动文件)
from scrapy import cmdline
cmdline.execute('scrapy crawl football'.split(' '))

11. scrapy 的一些注意点 (特性)

用过几次 scrapy 写爬虫后,对这个框架的一些特性有所感悟

① scrapy 采用异步下载
Scrapy 的下载器代码不会太复杂,但效率高!
主要的原因是 Scrapy 下载器是建立在 twisted 这个高效的异步模型上的(其实整个框架都在建立在这个模型上的)

​ scrapy 采用异步处理请求,即scrapy发送请求之后,不会等待这个请求的响应(也就是不会阻塞),而是可以同时发送其他请求或者做别的事情。而我们知道服务器对于请求的响应是由很多方面的因素影响的,如网络速度、解析速度、资源抢占等等,其响应的顺序是难以预测的。
​ scrapy异步的根源,在于它依赖于Twisted框架。Twisted框架是一个Python的event-driven的框架,这里可以理解为是异步I/O的。爬取内容时输出的顺序可能每次运行时都不一样
​ scrapy也支持多线程

② 使用默认方法下载图片注意点

官方文档

如果使用默认的方法对数据处理:
    	如果单纯获取文本,那么只需start_urls是一个list;
        而如果获取图片,则必须 start_urls与item中存储图片路径字段这两者必须都是 list,并且存储图片路径字段必须是 image_urls
        【注意】:可以在 get_media_requests 中把相应地方重写了

如果涉及下列方式下载图片
    1.用 ImagesPipeline 的默认方法下载或者不修改 ImagesPipeline 的 get_media_requests的返回值
    2.重写 ImagesPipeline 的 get_media_requests 方法,但不修改其返回值(即返回值还是一个 Request请求对象列表)
那么item中存储图片路径字段必须是 list! 即:这里要下载的图片(即使只有一张) url 必须命名为 image_urls 放到 list 中传送给 item
除非重写 ImagesPipeline 的 get_media_requests 的方法的返回值(本是一个请求对象的列表,修改为返回单个Request请求对象即可)

参考链接

③ meta 为只读属性
# request.meta = {"item": item} 这里不行!meta为只读属性!想法是给Requests的meta属性添加值,效果一样,都是为了传参用,但只能是在构造函数中 scrapy.Request(meta=) 才能用,其他地方不允许修改值
④ 导入 settings 失败
 from bizhi import settings  # 记得把根目录标记为: 源/根 后导入

原因: 这个报错的意思是:试图在顶级包(top-level package)之外进行相对导入。也就是说相对导入只适用于顶级包之内的模块

由于在"顶层模块"之外引用包,这里用到"顶层模块"的概念,“顶层模块” 是这执行文件同级的文件

from . import XXX
  或者 
from .. import XXX

  时会遇到这样两个错误:
SystemError: Parent module '' not loaded, cannot perform relative impor
  和   
ValueError: attempted relative import beyond top-level package

其实这两个错误的原因归根结底是一样的:在涉及到相对导入时,package所对应的文件夹必须正确的被python解释器视作package,而不是普通文件夹。否则由于不被视作package,无法利用package之间的嵌套关系实现python中包的相对导入。

文件夹被python解释器视作package需要满足两个条件:

  • 1、文件夹中必须有__init__.py文件,该文件可以为空,但必须存在该文件

  • 2、不能作为顶层模块来执行该文件夹中的py文件(即不能作为主函数的入口 模块的__name__ 不能等于__main__)

    很多时候就是导入和当前执行的py文件同级的package中的模块时报错: attempted relative import beyond top-level package

    此时,该包作为顶层模块(和执行文件同级),已经不被视为一个package了,需要将他们的父级目录标记为源/根

补充:在"from YY import XX"这样的代码中,无论是XX还是YY,只要被python解释器视作package,就会首先调用该package的__init__.py文件。如果都是package,则调用顺序是YY,XX。

也就是说 你不能在一个x.py 文件中 执行 from .模块名 import * 同时运行 python x.py

另外,练习中“from . import XXX”和“from … import XXX”中的’.‘和’…’,可以等同于linux里的shell中’.‘和’…'的作用,表示当前工作目录的package和上一级的package。

Pycharm中的解决方案:把根目录标记为: 源/根 后导入(右键相应文件夹,选择 Mark Directory as Sources 即可)

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1034795.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号