1. 介绍

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

它是爬虫界最知名的框架。就好比web框架中的django

Scrapy之所以能实现异步,得益于twisted框架
twisted有事件队列,哪一个事件有活动,就会执行!

 

整体架构大致如下:

蓝条部分,表示中间件!

python 全栈开发,Day137(爬虫系列之第4章-scrapy框架)

 

可以将SPIDERS,SCHEDULER,DOWNLOADER,ITEM PIPELINE理解为4个人。

它们不直接通讯,都是通过ENGINE来完成通讯的!

 

'''
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,
你可用该中间件做以下几件事:
    (1) process a request just before it is sent to the Downloader (i.e. right before Scrapy sends the request to the website);
    (2) change received response before passing it to a spider;
    (3) send a new Request instead of passing received response to a spider;
    (4) pass response to a spider without fetching a web page;
    (5) silently drop some requests.

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

 

官网链接

 

在调度器中,可以设置将重复的网址去重。如果爬取失败,需要再次爬取,那么就不能去重。

所以要不要去重,取决于项目需求而定。

 

如果访问5个url,当有一个url卡住了。那么它就会切换到队列中其他的url。因为它是异步请求,这里就可以高效利用CPU

 

2. scrapy 的工作流程

之前学习的 爬虫的基本流程

python 全栈开发,Day137(爬虫系列之第4章-scrapy框架)

那么 scrapy是如何帮助我们抓取数据的呢?

python 全栈开发,Day137(爬虫系列之第4章-scrapy框架)

 

3. 数据流

Scrapy中的数据流由执行引擎控制,其过程如下:

1.引擎打开一个网站(open adomain),找到处理该网站的Spider并向该spider请求第一个要爬取的URL(s)。

2.引擎从Spider中获取到第一个要爬取的URL并在调度器(Scheduler)以Request调度。

3.引擎向调度器请求下一个要爬取的URL。

4.调度器返回下一个要爬取的URL给引擎,引擎将URL通过下载中间件(请求(request)方向)转发给下载器(Downloader)。

5.一旦页面下载完毕,下载器生成一个该页面的Response,并将其通过下载中间件(返回(response)方向)发送给引擎。    

6.引擎从下载器中接收到Response并通过Spider中间件(输入方向)发送给Spider处理。

7.Spider处理Response并返回爬取到的Item及(跟进的)新的Request给引擎。

8.引擎将(Spider返回的)爬取到的Item给ItemPipeline,将(Spider返回的)Request给调度器。

9.(从第二步)重复直到调度器中没有更多地request,引擎关闭该网站。

 

 

上面的内容,参考链接:

https://blog.csdn.net/xun527/article/details/78450789

 

每访问一个url,就会经历上面列举的9个步骤,它是一个反复过程。

如果访问失败,可以再次请求访问!

 

spiders包含了爬取和解析这2步。

为什么要有中间件?

请求一个网页,有几十个cookie,每一次发送,都得带上cookie,很麻烦。
比如使用代理,需要换IP访问。封锁一个,换一个!
加上代理访问,需要使用中间件!那么无论任何请求,都可以带上cookie。

item就是解析好的数据

 

2. 安装

Windows平台

windows平台安装比较难搞,它不像linux一样,一条命令就搞定了!很多人学习scrapy,光安装就卡了半天。

为了不让 学习如痴如醉的你放弃scrapy,这里列举出详细的安装过程!

 

先说明一下安装环境。使用的是windows 10 家庭版,64位操作系统。已经安装好了Python 3.6

搜索cmd,必须使用管理员身份运行

 python 全栈开发,Day137(爬虫系列之第4章-scrapy框架)

 

安装 wheel

C:\Windows\system32>pip3 install wheel

 

安装 lxml

C:\Windows\system32>pip3 install lxml

 

安装 pyopenssl

C:\Windows\system32>pip3 install pyopenssl

 

安装 pywin32

打开网址:

https://sourceforge.net/projects/pywin32/files/pywin32/

 点击 Build 221

python 全栈开发,Day137(爬虫系列之第4章-scrapy框架)

 下载pywin32-221.win-amd64-py3.6.exe

python 全栈开发,Day137(爬虫系列之第4章-scrapy框架)

 直接运行

python 全栈开发,Day137(爬虫系列之第4章-scrapy框架)

 点击下一步

python 全栈开发,Day137(爬虫系列之第4章-scrapy框架)

  点击下一步

 python 全栈开发,Day137(爬虫系列之第4章-scrapy框架)

最后就可以安装完成了!

 

安装 twisted

打开网页

https://pypi.org/project/Twisted/#files

这里不要下载,为什么呢?看文件名,它只支持Python 2.7。但我的是Python 3.6啊!

python 全栈开发,Day137(爬虫系列之第4章-scrapy框架)

 

打开另外一个网页:

https://github.com/zerodhatech/python-wheels

 下载文件 Twisted-17.9.0-cp36-cp36m-win_amd64.whl

python 全栈开发,Day137(爬虫系列之第4章-scrapy框架)

点击下载

python 全栈开发,Day137(爬虫系列之第4章-scrapy框架)

 点击文件属性,复制路径

 python 全栈开发,Day137(爬虫系列之第4章-scrapy框架)

安装

C:\Windows\system32>pip3 install C:\Users\vr\Downloads\Twisted-17.9.0-cp36-cp36m-win_amd64.whl

 

安装 scrapy

C:\Windows\system32>pip3 install scrapy

 到这里,scrapy就安装完成了!

 

Linux平台

Linux平台安装比较简单,一条命令就可以搞定了

pip3 install scrapy

 

3. 命令行工具

1. 查看帮助

scrapy -h
scrapy <command> -h

 

2. 两种命令

有两种命令:其中Project-only必须切到项目文件夹下才能执行,而Global的命令则不需要

Global commands

startproject #创建项目
genspider    #创建爬虫程序
settings     #如果是在项目目录下,则得到的是该项目的配置
runspider    #运行一个独立的python文件,不必创建项目
shell        #scrapy shell url地址  在交互式调试,如选择器规则正确与否
fetch        #独立于程单纯地爬取一个页面,可以拿到请求头
view         #下载完毕后直接弹出浏览器,以此可以分辨出哪些数据是ajax请求
version      #scrapy version 查看scrapy的版本,scrapy version -v查看scrapy依赖库的版本

 

Project-only commands

crawl        #运行爬虫,必须创建项目才行,确保配置文件中ROBOTSTXT_OBEY = False
check        #检测项目中有无语法错误
list         #列出项目中所包含的爬虫名
edit         #编辑器,一般不用
parse        #scrapy parse url地址 --callback 回调函数  #以此可以验证我们的回调函数是否正确
bench        #scrapy bentch压力测试

 

举例:

创建一个爬虫项目DianShang

C:\Users\xiao>e:
E:\>cd E:\python_script\爬虫\day3
E:\python_script\爬虫\day3>scrapy startproject DianShang

 

生成一个爬虫程序。注意:左边的jd表示模块名,右边的jd.com表示要访问的url。

一个项目还可以访问多个url

E:\python_script\爬虫\day3>cd DianShang
E:\python_script\爬虫\day3\DianShang>scrapy genspider jd jd.com

注意:项目名和爬虫程序名不能重复!

 

运行scrapy项目。注意:这里指定的模块名必须和上面一致!

E:\python_script\爬虫\day3\DianShang>scrapy crawl jd

一个项目,也可以启动多个模块

 

主要用到命令,就是上面演示的3个!

1 scrapy startproject DianShang   # 创建爬虫项目
2 scrapy genspider jd jd.com      # 生成一个爬虫程序
3 scrapy crawl jd                 # 运行scrapy项目

 

3. 官网链接

https://docs.scrapy.org/en/latest/topics/commands.html

 

4. 目录结构

project_name/
    scrapy.cfg              #  项目的主配置信息,用来部署scrapy时使用
    project_name/           #  爬虫项目名称
       __init__.py 
       items.py            #  数据存储模板,用于结构化数据,如:Django的Model 
       pipelines.py        #  数据持久化
       settings.py         #  用户级配置文件
       spiders/            #  爬虫程序的目录
           __init__.py
           爬虫1.py        # 爬虫程序1
           爬虫2.py 
           爬虫3.py

 

文件说明:

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

注意:

1、一般创建爬虫文件时,以网站域名命名

2、默认只能在终端执行命令,为了更便捷操作:

#在项目根目录下新建:entrypoint.py
from scrapy.cmdline import execute
execute(['scrapy', 'crawl', 'xiaohua'])

框架基础:spider类,选择器

 

举例:

上面已经创建好了DianShang项目,修改settings.py,将下面的配置改为False

ROBOTSTXT_OBEY = False

这是配置是否遵循robots协议,一般情况下,是不遵循的。为啥呢?你要真的遵循的话,还怎么爬呀!

涉及到金融,影响国家安全,影响社会和谐稳定...的信息,切莫爬取,否则后果不堪设想!

 

每次使用命令行运行 scrapy crawl jd  太麻烦了。

在项目根目录新建一个文件 bin.py。注意,它和scrapy.cfg是同级的!

#在项目根目录下新建:bin.py
from scrapy.cmdline import execute
# 最后一个参数是:爬虫程序名
execute(['scrapy', 'crawl', 'jd'])

 

直接执行bin.py,就可以启动爬虫程序了!

执行之后,会输出一段红色字符串。注意:它不是报错!

也可以关闭掉,修改 bin.py

#在项目根目录下新建:bin.py
from scrapy.cmdline import execute
# 第三个参数是:爬虫程序名
execute(['scrapy', 'crawl', 'jd','--nolog'])

再次执行,就不会输出了!

 

二、Spider类

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

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#topics-item-pipeline)
   或者导出到不同的文件(通过Feed exports:https://docs.scrapy.org/en/latest/topics/feed-exports.html#topics-feed-exports)

 

举例:

打开DianShang项目,修改DianShang-->spiders-->jd.py

# -*- coding: utf-8 -*-
import scrapy


class JdSpider(scrapy.Spider):
    name = 'jd'
    allowed_domains = ['jd.com']
    start_urls = ['http://jd.com/']

    def parse(self, response):
        print(response,type(response))

执行bin.py,输出如下:

<200 https://www.jd.com/> <class 'scrapy.http.response.html.HtmlResponse'>

 

start_requests

使用Ctrl+鼠标左键,点击这一段代码中的Spider,查看源代码

class JdSpider(scrapy.Spider):

查看start_requests方法,看最后2行代码。

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)

它执行了for循环,self.start_urls就是在JdSpider类中定义的start_urls变量,它是一个列表!

如果列表为空,不会执行yield

最后使用生成器返回了一个Request对象

 

再查看Request源码

class Request(object_ref):

    def __init__(self, url, callback=None, method='GET', headers=None, body=None,
                 cookies=None, meta=None, encoding='utf-8', priority=0,
                 dont_filter=False, errback=None, flags=None):

        self._encoding = encoding  # this one has to be set first
        self.method = str(method).upper()
        self._set_url(url)
        self._set_body(body)
        assert isinstance(priority, int), "Request priority not an integer: %r" % priority
        self.priority = priority

        if callback is not None and not callable(callback):
            raise TypeError('callback must be a callable, got %s' % type(callback).__name__)
        if errback is not None and not callable(errback):
            raise TypeError('errback must be a callable, got %s' % type(errback).__name__)
        assert callback or not errback, "Cannot use errback without a callback"
        self.callback = callback
        self.errback = errback

        self.cookies = cookies or {}
        self.headers = Headers(headers or {}, encoding=encoding)
        self.dont_filter = dont_filter

        self._meta = dict(meta) if meta else None
        self.flags = [] if flags is None else list(flags)

 

参数解释:

  • url(string) - 此请求的网址
  • callback(callable) - 将使用此请求的响应(一旦下载)作为其第一个参数调用的函数。有关更多信息,请参阅下面的将附加数据传递给回调函数。如果请求没有指定回调,parse()将使用spider的 方法。请注意,如果在处理期间引发异常,则会调用errback。
  • method(string) - 此请求的HTTP方法。默认为'GET'。
  • meta(dict) - 属性的初始值Request.meta。如果给定,在此参数中传递的dict将被浅复制。
  • body(str或unicode) - 请求体。如果unicode传递了a,那么它被编码为 str使用传递的编码(默认为utf-8)。如果 body没有给出,则存储一个空字符串。不管这个参数的类型,存储的最终值将是一个str(不会是unicode或None)。
  • headers(dict) - 这个请求的头。dict值可以是字符串(对于单值标头)或列表(对于多值标头)。如果 None作为值传递,则不会发送HTTP头。
  • cookie(dict或list) - 请求cookie。

  • encoding(string) - 此请求的编码(默认为'utf-8')。此编码将用于对URL进行百分比编码,并将正文转换为str(如果给定unicode)。
  • priority(int) - 此请求的优先级(默认为0)。调度器使用优先级来定义用于处理请求的顺序。具有较高优先级值的请求将较早执行。允许负值以指示相对低优先级。
  • dont_filter(boolean) - 表示此请求不应由调度程序过滤。当您想要多次执行相同的请求时忽略重复过滤器时使用。小心使用它,或者你会进入爬行循环。默认为False。
  • errback(callable) - 如果在处理请求时引发任何异常,将调用的函数。这包括失败的404 HTTP错误等页面。它接收一个Twisted Failure实例作为第一个参数。有关更多信息,请参阅使用errbacks在请求处理捕获异常

  • flags(list) - 是一个包含属性初始值的 Request.flags列表。如果给定,列表将被浅复制。

 

关于参数的更多信息,请参考链接:

https://blog.csdn.net/weixin_37947156/article/details/74974208

 

一般情况下,要自己定义start_requests方法。为什么呢?因为它不一定能够访问目标网站。

比如访问 https://github.com/  要获取我的个人信息。直接用GET请求,不带任何信息,是获取不到的。必须登录才行!

 

举例:

进入DianShang项目,修改 DianShang-->spiders-->jd.py,增加start_requests方法

# -*- coding: utf-8 -*-
import scrapy
from scrapy import Request  # 导入模块

class JdSpider(scrapy.Spider):
    name = 'jd'
    allowed_domains = ['jd.com']
    # start_urls = ['http://jd.com/']  # 占时没有用了

    def start_requests(self):
        r1 = Request(url="http://jd.com/")
        yield r1  # 务必使用yield 返回

    def parse(self, response):
        print(response,type(response))
View Code

相关文章:

猜你喜欢
相关资源
相似解决方案