Scrapy框架

一 、介绍

    Scrapy一个开源和协作的框架,其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的,使用它可以以快速、简单、可扩展的方式从网站中提取所需的数据。但目前Scrapy的用途十分广泛,可用于如数据挖掘、监测和自动化测试等领域,也可以应用在获取API所返回的数据(例如 Amazon Associates Web Services ) 或者通用的网络爬虫。

    Scrapy 是基于twisted框架开发而来,twisted是一个流行的事件驱动的python网络框架。因此Scrapy使用了一种非阻塞(又名异步)的代码来实现并发。整体架构大致如下

  1、scrapy的工作流程

    第一步:爬虫从spiders爬行器中发送request请求给engine引擎。

    第二部:引擎发送request请求给scheduler调度器,调度器从中调度请求,并要求下一个请求进来。

    第三部:调度程序将第一个请求返回给引擎。

    第四部:引擎将请求发送给下行加载程序downloader,通过下行加载中间件,并且准备下载网页。

    第五步:一旦页面完成下载后,Downloader会生成一个响应(使用该页面)并将其发送到引擎,通过Downloader中间件到达downloader下载器

    第六步:引擎接收来自Downloader的响应,并将其发送给爬行器进行处理,通过爬行器中间件,到达爬行器

    第七步:爬行器处理响应信息,并将处理后的结果通过爬行器中间件返回到引擎。

    第八步:引擎将处理过的数据发送到项目管道,然后将处理过的请求发送到调度程序,并请求可能的下一个请求进行爬行。

  总结:

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

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

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

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

    5、项目管道在items被提取后负责处理它们,主要包括清理、验证、持久化(比如存到数据库)等操作

    6、下载器中间件位于Scrapy引擎和下载器之间,主要用来处理从EGINE传到DOWLOADER的请求request,已经从DOWNLOADER传到EGINE的响应response,你可用该中间件做以下几件事情

      1、在发送到下载程序之前处理一个请求(即在剪贴簿发送请求到网站之前);

      2、在将其传递给蜘蛛之前,更改接收到响应;

      3、发送一个新的请求,而不是传递接收到的响应;

      4、在不获取web页面的情况下对爬行器进行响应;

      5、悄悄地放弃一些请求。

    7、爬虫中间件

      位于EGINE和SPIDERS之间,主要工作是处理SPIDERS的输入(即responses)和输出(即requests)

官网链接:https://docs.scrapy.org/en/latest/topics/architecture.html

二 安装

#Windows平台
    1、pip3 install wheel #安装后,便支持通过wheel文件安装软件,wheel文件官网:https://www.lfd.uci.edu/~gohlke/pythonlibs
    3、pip3 install lxml
    4、pip3 install pyopenssl
    5、下载并安装pywin32:https://sourceforge.net/projects/pywin32/files/pywin32/
    6、下载twisted的wheel文件:http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
    7、执行pip3 install 下载目录Twisted-17.9.0-cp36-cp36m-win_amd64.whl
    8、pip3 install scrapy
  
#Linux平台
    1、pip3 install scrapy

三 命令行工具

#1 查看帮助
    scrapy -h      或者是scrapy -l
    scrapy <command> -h

#2 有两种命令:其中Project-only必须切到项目文件夹下才能执行这种命令是局部命令,而Global的命令则不需要,是属于全局命令
    Global commands:全局命令
       1、startproject #创建一个爬虫项目,相当于创建了个爬虫的整体框架,   
        scrapy startproject 项目名称 2、genspider #创建一个爬虫程序,可以在配置文件中将网址域名注释调,该网址域名的作用就是在网站的html中只允许爬该网址的数据
        scrapy genspider 爬虫名称 需要爬的网址域名 3、settings #如果是在项目目录下,则得到的是该项目中settings的配置,否则不是,
        scrapy settings --get=settings文件中的配置变量 4、runspider #运行一个独立的python文件,不必创建项目,这个python文件可以是一个爬虫程序
        scrapy runspider 爬虫文件的绝对路径 5、shell # 在交互式调试,如选择器规则正确与否都是在该交互式环境下测试的,相当于get下来数据后将数据封装到了个response对象内。
        scrapy shell url地址
        view(response);response.text;response.body;
6、fetch #独立于程单纯地爬取一个页面,可以拿到请求头
        scrapy fetch url地址
        scrapy fetch --header url地址 7、 view #下载完毕后直接弹出浏览器,以此可以分辨出哪些数据是ajax请求
        scrapy view rul路径 #如果页面显示内容不全,不全的内容则是ajax请求实现的,以此快速定位问题 8、version  #
        scrapy version 查看scrapy的版本
        scrapy version -v 查看scrapy所依赖的库版本
      
Project-only commands:局部命令,必须在项目目录下才能执行 1、crawl #运行爬虫,必须创建项目程序才行,确保配置文件settings中ROBOTSTXT_OBEY = False,机器人策略,关闭该策略后就不会受机器人策略的影响
        scrapy crawl 爬虫项目文件名 #和之前的runspider方法相比该方法必须在项目目录下运行且受机器人策略的影响
        scrapy crawl 爬虫项目文件名 --nolog #运行爬虫项目不打印日志
        scrapy crawl 爬虫项目文件名 -a 参数名=参数值 #给爬虫项目传参,但是项目内部必须重新__int__方法来接受外部参数
        scrapy crawl 爬虫项目文件名 -a 参数名=参数值 --nolog #运行爬虫项目不打印日志并且传参数给爬虫项目
2、check #检测项目(所有的爬虫程序)中有无语法错误
        scrapy check 3、list #列出项目中所包含的爬虫名
        scrapy list 4、edit #编辑器,一般不用 5、 parse # #以此可以验证我们的回调函数是否正确,回调函数一般写在爬虫程序文件下
        scrapy parse url地址 --callback 回调函数 6、bench #压力测试
        scrapy bench #3 官网链接 https://docs.scrapy.org/en/latest/topics/commands.html
总结:在命令行的命令或者是参数都是不用加引号的,
   爬虫项目的settings配置文件添加配置时都变量名必须要大写,否则不识别

四 项目结构以及爬虫应用简介

project_name/
   scrapy.cfg
   project_name/
       __init__.py
       items.py
       pipelines.py
       settings.py
       spiders/
           __init__.py
           爬虫1.py
           爬虫2.py
           爬虫3.py

文件说明:

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

注意:一般创建爬虫文件时,以网站域名命名

import scrapy
 
class XiaoHuarSpider(scrapy.spiders.Spider):
    name = "xiaohuar"                            # 爬虫名称 *****
    allowed_domains = ["xiaohuar.com"]  # 允许爬取的域名
    start_urls = [
        "http://www.xiaohuar.com/hua/",   # 其实URL地址,该url地址是程序默认创建的,如果需要爬取该地址外的其他地址数据就需要自己处理请求参数,重新请求方法
    ]
    def parse(self, response):
        # 访问起始URL并获取结果后的回调函数
import sys,os
sys.stdout=io.TextIOWrapper(sys.stdout.buffer,encoding='gb18030')

五 Spiders

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

强调:配置文件的选项必须是大写,如X='1'

# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
class BaiduSpider(CrawlSpider):
    name = 'xiaohua'
    allowed_domains = ['www.xiaohuar.com']
    start_urls = ['http://www.xiaohuar.com/v/']
    # download_delay = 1
    rules = (
        Rule(LinkExtractor(allow=r'p-d-d+.html$'), callback='parse_item',follow=True,),
    )

    def parse_item(self, response):
        if url:
            print('======下载视频==============================', url)
            yield scrapy.Request(url,callback=self.save)

    def save(self,response):
        print('======保存视频==============================',response.url,len(response.body))

        import time
        import hashlib
        m=hashlib.md5()
        m.update(str(time.time()).encode('utf-8'))
        m.update(response.url.encode('utf-8'))

        filename=r'E:\mv\%s.mp4' %m.hexdigest()
        with open(filename,'wb') as f:
            f.write(response.body)

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

六 Selectors

response.selector.css()
response.selector.xpath()
可简写为
response.css()
response.xpath()

  1、查找标签
    response.css('div a ')#通过标签名查找标签的内容
    response.xpath('//body/a') #开头的//代表从整篇文档中寻找,body之后的/代表body的儿子     response.xpath('//body//a') #开头的//代表从整篇文档中寻找,body之后的//代表body的子子孙孙
    总结://与/的区别:儿子和后代的区别,但是得到的都是一个列表
  2、查找标签下的内容(text方法)      
    response.css('div a::text')#先定位标签然后通过::的方式查找标签的内容

    response.xpath('//body//a/text()')   3、查找标签下的内容
    extract与extract_first:从selector对象中解出内容    response.xpath('//div/a/text()').extract() #得到的是个解析后的内容,是个列表形式     response.xpath('//div/a/text()').extract_first()#得到的是个解析后的内容,是个字符串形式
  4、查找标签下的属性:    response.xpath('//div/a/@href').extract_first() #得到的是标签的href属性,可以跟标签的任意属性xpath的属性加前缀@ response.css('div a::attr(href)').extract_first()   5、嵌套查找加混合查找     response.xpath('//div').css('a').xpath('@href').extract_first()   6、设置查找默认值     response.xpath('//div[@id="xxx"]').extract_first(default="not found")#为了防止查找不到匹配的标签而设置默认值   7、按照标签属性查找     response.xpath('//div[@id="images"]/a[@href="image3.html"]/text()').extract() #如果按照属性查找标签就必须加上中括号,再写匹配规则     response.css('#images a[@href="image3.html"]/text()').extract()   8、按照标签属性模糊查找     response.xpath('//a[contains(@href,"image")]/@href').extract()#如果按照标签属性模糊查找就必须加上contains()方法,然后再在里面写匹配规则,匹配规则用逗号隔开     response.css('a[href*="image"]::attr(href)').extract()     response.xpath('//a[contains(@href,"image")]/img/@src').extract()     response.css('a[href*="imag"] img::attr(src)').extract()     response.xpath('//*[@href="image1.html"]')     response.css('*[href="image1.html"]')   9、正则表达式查找     response.xpath('//a/text()').re(r'Name: (.*)')     response.xpath('//a/text()').re_first(r'Name: (.*)')   10、xpath相对路径       >>> res=response.xpath('//a[contains(@href,"3")]')[0]       >>> res.xpath('img')       [<Selector xpath='img' data='<img src="image3_thumb.jpg">'>]       >>> res.xpath('./img')       [<Selector xpath='./img' data='<img src="image3_thumb.jpg">'>]       >>> res.xpath('.//img')       [<Selector xpath='.//img' data='<img src="image3_thumb.jpg">'>]       >>> res.xpath('//img') #这就是从头开始扫描   11、、带变量的xpath     response.xpath('//div[@id=$xxx]/a/text()',xxx='images').extract_first()#带变量查找     response.xpath('//div[count(a)=$yyy]/@id',yyy=5).extract_first() #求有5个a标签的div的id
复制代码
原文地址:https://www.cnblogs.com/xuanan/p/7816069.html