scrapy框架常用从头到尾巴

总结scrapy框架

一. 核心组件和中间件

引擎 engine  自动运行,无需关祖,会自动组织所有的请求对象,分发给下载器
下载器 downloader 从引擎处获取到请求对象后,请求数据
爬虫 spiders
	scrapy.Spider 普通的爬虫
	scrapy.CrawSpider 可设置规则的爬虫类
	Rule规则类
	start_requests()开始函数
调度器 scheduler
管道 Item pipeline 
	清理HTML数据
	验证爬去的数据(检查item包含某些字段)
	查重
	将爬去的结果保存到数据库中
	对图片数据进行下载这个可以用图片管道
	
流程:
	1、爬虫引擎获得出使请求开始爬取
	2、爬虫引擎开始请求调度程序,并准备对下一次的请求进行抓取
	3、爬虫调度其返回下一个请求给爬虫引擎
	4、引擎请求发送给下载器,通过下载中间件下载网络数据
	5、一旦下载器完成页面下载,将下载结果返回给爬虫引擎
	6、引擎将下载器的响应通过中间件返回给爬虫进行处理
	7、爬虫处理响应,并通过中间件返回处理后的items,以及新的请求给引擎
	8、引擎发送处理后的items到项目管道,然后把处理结果返回给调度器,调度器计划处理下一个请求抓取
	9、重复该过程,知道爬取所有的url请求

二. 核心的模块和类

scrapy.Spider 普通爬虫类的父类
	- name  爬虫名, 在scrapy crawl 命令中使用
	- start_urls 起始的请求URL资源列表
	- allowed_domains 允许访问的服务器域名列表
	- start_requests() 方法,爬虫启动后执行的第一个方法(流程中的第一步:发起请求)
	- logger  当前爬虫的日志记录器
	- parse() 默认请求成功后,对响应的数据默认解析的方法
scrapy.Reqeust
  - 初始化时参数:  url, method, body, encoding, callback, headers, cookies, dont_filter, priority, meta
  - meta dict格式, 可以设置proxy 代理
scrapy.http.Response/TextResponse/HtmlResponse
	- status 响应状态码
	- meta  响应的元信息, 包含request中的meta信息
	- url  请求的URL
	- request 请求对象
	- headers 响应头
	- body 字节数据
	- text 文本数据
	- css()/xpath() 提取HTML元素信息(基于lxml/bs4)
scrapy.Item 类 ,类似于dict, 作用: 解析出不同结构的数据时,使用不同的Item类,便于数据管道处理.
scrapy.Field 类, 用于Item子类中声明字段属性(数据属性)
scrapy.signals  信号
	- spider_opened  打开爬虫
	- spider_closed  关闭爬虫
	- spider_error   爬虫出现异常
优先级:  
- 请求优先级: 值高 == 优先级大, 值低 ==  优先级小

配置settings中的优先级
- 管道优先级: 值高 == 优先级小, 值低 ==  优先级高
- 中间件优先级: 值高 == 优先级小, 值低 ==  优先级高

作业1:

最新电影信息: https://www.dygod.net/html/gndy/dyzz/index.html

按分类: 创建分类表和电影信息.

作业2:

读书网的出版的图书信息: https://www.dushu.com/book/

三、目录结构

spiders
	__init__.py
	自定义的爬虫文件.py  	自定义的爬虫和回调函数
	
__init__.py
    items.py   定义数据结构的地方,是一个继承scrapy.item类,属性字段的类型是scrapy.Field()
    注意:scrapy.Item类,世纪是一个dict字典,所以在spider的parse()函数返回的的迭代元素应该是dict字典对象,且	字典的key和item的Field()相对应
setting.py
	1、项目名称,默认的USER_AGENT由它来构成也作为日志记录的日志名
	2、爬虫应用路径
		SPIDER_MODULES=['Amazon.spiders']
		NEWSPIDER_MODULE='Amazon.spiders'
	3、客户端User-Agent请求头
	USER_AGENT='自己写'
	
	4、是否遵循爬虫协议
			ROBOTSTXT=False
			
	5、是否支持cookie,cookiejar进行操作cookie,默认开启
			COOKIE_ENABLED=False
	
	6、Telnet用于查看当前爬虫的信息,操作爬虫等....使用telnet ip port ,然后通过命令操作
	TELNETCONSOLE_ENABLED=False
	TELNETCONSOLE_HOST='127.0.0.1'
	TELNETCONSOLE_PORT=[6023,]
	
	7、Scrapy发送HTTP请求默认使用的请求头
	DEFAUT_REQUEST_HEADERS={
		.....
	}
	
        #===>第二部分:并发与延迟<===
    #1、下载器总共最大处理的并发请求数,默认值16
    #CONCURRENT_REQUESTS = 32

    #2、每个域名能够被执行的最大并发请求数目,默认值8
    #CONCURRENT_REQUESTS_PER_DOMAIN = 16

    #3、能够被单个IP处理的并发请求数,默认值0,代表无限制,需要注意两点
    #I、如果不为零,那CONCURRENT_REQUESTS_PER_DOMAIN将被忽略,即并发数的限制是按照每个IP来计算,而不是每个域名
    #II、该设置也影响DOWNLOAD_DELAY,如果该值不为零,那么DOWNLOAD_DELAY下载延迟是限制每个IP而不是每个域
    #CONCURRENT_REQUESTS_PER_IP = 16

    #4、如果没有开启智能限速,这个值就代表一个规定死的值,代表对同一网址延迟请求的秒数
    #DOWNLOAD_DELAY = 3

    #===>第三部分:智能限速/自动节流:AutoThrottle extension<===
    #一:介绍
    from scrapy.contrib.throttle import AutoThrottle #http://scrapy.readthedocs.io/en/latest/topics/autothrottle.html#topics-autothrottle
    设置目标:
    1、比使用默认的下载延迟对站点更好
    2、自动调整scrapy到最佳的爬取速度,所以用户无需自己调整下载延迟到最佳状态。用户只需要定义允许最大并发的请求,剩下的事情由该扩展组件自动完成
    #二:如何实现?
    在Scrapy中,下载延迟是通过计算建立TCP连接到接收到HTTP包头(header)之间的时间来测量的。
    注意,由于Scrapy可能在忙着处理spider的回调函数或者无法下载,因此在合作的多任务环境下准确测量这些延迟是十分苦难的。 不过,这些延迟仍然是对Scrapy(甚至是服务器)繁忙程度的合理测量,而这扩展就是以此为前提进行编写的。


    #三:限速算法
    自动限速算法基于以下规则调整下载延迟
    #1、spiders开始时的下载延迟是基于AUTOTHROTTLE_START_DELAY的值
    #2、当收到一个response,对目标站点的下载延迟=收到响应的延迟时间/AUTOTHROTTLE_TARGET_CONCURRENCY
    #3、下一次请求的下载延迟就被设置成:对目标站点下载延迟时间和过去的下载延迟时间的平均值
    #4、没有达到200个response则不允许降低延迟
    #5、下载延迟不能变的比DOWNLOAD_DELAY更低或者比AUTOTHROTTLE_MAX_DELAY更高

    #四:配置使用
    #开启True,默认False
    AUTOTHROTTLE_ENABLED = True
    #起始的延迟
    AUTOTHROTTLE_START_DELAY = 5
    #最小延迟
    DOWNLOAD_DELAY = 3
    #最大延迟
    AUTOTHROTTLE_MAX_DELAY = 10
    #每秒并发请求数的平均值,不能高于 CONCURRENT_REQUESTS_PER_DOMAIN或CONCURRENT_REQUESTS_PER_IP,调高了则吞吐量增大强奸目标站点,调低了则对目标站点更加”礼貌“
    #每个特定的时间点,scrapy并发请求的数目都可能高于或低于该值,这是爬虫视图达到的建议值而不是硬限制
    AUTOTHROTTLE_TARGET_CONCURRENCY = 16.0
    #调试
    AUTOTHROTTLE_DEBUG = True
    CONCURRENT_REQUESTS_PER_DOMAIN = 16
    CONCURRENT_REQUESTS_PER_IP = 16



    #===>第五部分:中间件、Pipelines、扩展<===
    #1、Enable or disable spider middlewares
    # See http://scrapy.readthedocs.org/en/latest/topics/spider-middleware.html
    #SPIDER_MIDDLEWARES = {
    #    'Amazon.middlewares.AmazonSpiderMiddleware': 543,
    #}

    #2、Enable or disable downloader middlewares
    # See http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html
    DOWNLOADER_MIDDLEWARES = {
       # 'Amazon.middlewares.DownMiddleware1': 543,
    }

    #3、Enable or disable extensions
    # See http://scrapy.readthedocs.org/en/latest/topics/extensions.html
    #EXTENSIONS = {
    #    'scrapy.extensions.telnet.TelnetConsole': None,
    #}

    #4、Configure item pipelines
    # See http://scrapy.readthedocs.org/en/latest/topics/item-pipeline.html
    ITEM_PIPELINES = {
       # 'Amazon.pipelines.CustomPipeline': 200,
    }

    #===>第六部分:缓存<===
    """
    1. 启用缓存
        目的用于将已经发送的请求或相应缓存下来,以便以后使用

        from scrapy.downloadermiddlewares.httpcache import HttpCacheMiddleware
        from scrapy.extensions.httpcache import DummyPolicy
        from scrapy.extensions.httpcache import FilesystemCacheStorage
    """
    # 是否启用缓存策略
    # HTTPCACHE_ENABLED = True

    # 缓存策略:所有请求均缓存,下次在请求直接访问原来的缓存即可
    # HTTPCACHE_POLICY = "scrapy.extensions.httpcache.DummyPolicy"
    # 缓存策略:根据Http响应头:Cache-Control、Last-Modified 等进行缓存的策略
    # HTTPCACHE_POLICY = "scrapy.extensions.httpcache.RFC2616Policy"

    # 缓存超时时间
    # HTTPCACHE_EXPIRATION_SECS = 0

    # 缓存保存路径
    # HTTPCACHE_DIR = 'httpcache'

    # 缓存忽略的Http状态码
    # HTTPCACHE_IGNORE_HTTP_CODES = []

    # 缓存存储的插件
    # HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'
    
    
    
  middleware.py
  中间件
  process_request(self, request, spider):  None|Request|Response|raise IgnoreRequest
process_response(self, request, response, spider): Request|Response|raise IgnoreRequest
process_exception(self, request, exception, spider): None


pipelines  管道类
用来处理下载数据的后续处理

四、scrapy shell

1、response 对象

​ response.body 二进制

​ response.text 文本

​ response.url 请求路由

​ response.status 状态码

2、response解析

​ response.xapth() xpath进行解析,返回ItemList

​ response.css() css进行解析,返回ItemList

3、selector对象

​ extract() 使用xpath请求到的对象是一个selector对象,需要进一步使用extract()方法拆包,转换为unicode字符串

​ extract() 返回第一个解析到的值,如果列表为空,此种方法也不会报错,会返回一个空值

​ xapth() xpath进行解析

​ css() css进行解析

​ select() 可以继续使用select方法对Selector对象进一步提取信息

4、item对象

​ dict(itemobj)可以使用dict直接将item对象转换成字典对象

​ item(dicobj) 也可以使用字典对象创建一个Item对象

五、爬虫停止条件

1、Scrapy的CloseSpider扩展会满足条件时自动终止爬虫程序

2、可选项

​ CLOSESPIDER_TIMEOUT(秒)指定时间过后

​ CLOSESPIDER_ITEMCOUNT 抓取了指定数目的Item之后

​ CLOSESPIDER_PAGECOUNT 收到了指定数目的响应之后

​ CLOSESPIDER_ERRORCOUNT 发生了指定数目的错误之后

例如

$ scrapy crawl fast -s CLOSESPIDER_ITEMCOUNT=10

$ scrapy crawl fast -s CLOSESPIDER_PAGECOUNT=10

$ scrapy crawl fast -s CLOSESPIDER_TIMEOUT=10

六、规则爬虫

1、导入

from scrapy.linkextractors import LinkExtractor

2、创建对象

extractor = LinkExtractor(提取规则)

3、提取规则

直接表达式 extractor = LinkExtractor(r'list_23_\d+\.html')

allow 	提取符合这则的链接

deny 不提取符合正则的链接

restrict_xpaths xpath条件
restrict_css 	css条件

4、提取链接

​ links = extractor.extract_links(response)

for link in links:
print(link.url, link.text)

5、创建爬虫文件

​ scrapy genspider -t crawl 爬虫名 域名

​ 使用模板 crawlspider 创建爬虫类

七、日志LOG

级别

​	CRITICAL 严重错误

​	ERROR 一般错误

​	WARNNING  警告信息

​	INFO 	一般信息

​	DEBUG 	调试信息



设置级别

​	LOG_LEVEL ="日志等级"

​	LOG_FILE ="name.log"



扩展

​	loggin 的日志模块

spider.logger.info('Spider opened: %s' % spider.name)







scrapy框架的中间件中添加日志处理

 		 handler = StreamHandler()
        handler.setLevel(logging.INFO)
        handler.setFormatter(logging.Formatter(fmt="%(levelname)s: %(message)s"))
        # 添加日志的处理器, spider.logger -> logging.LoggerAdapter类实例对象
        spider.logger.logger.addHandler(handler)

        # 向scrapy.core.engine日志记录器添加StreamHandler
        # scrapy框架中存在哪些日志记录器???
        logger_engine = logging.getLogger('scrapy.core.engine')
        logger_engine.addHandler(handler)

sys.execpthook

import sys

def download(url):

    print('开始下载', url)

    # 程序在运行的过程中可能会出现的异常

    # 针对此异常,我们的程序并没有处理

    raise Exception('--下载超时-')

def global_except(except_type, msg, trackback):

    print('-------上报程序中断/异常信息------')

    print(except_type, msg)

    print(dir(trackback))

    # 显示当前异常帧frame

    print(trackback.tb_frame.f_lineno,

          trackback.tb_frame.f_code.co_name,

          trackback.tb_frame.f_locals)

    # 显示下一个常帧的跟踪位置信息

    print(trackback.tb_next.tb_frame.f_lineno,

          trackback.tb_frame.f_code.co_name,

          trackback.tb_next.tb_frame.f_locals)

    print(trackback.tb_next.tb_next)

    print('-----------------------')

if __name__ == '__main__':

    sys.excepthook = global_except

    download('http://www.baidu.com/s?kw=123')

八、处理POST请求

Request和Response
Request 
		url
		callback
		meta
Response
		text
		meta
		status
		url
		headers

start_requests(self)
spiders 文集爱你中删除start_urls和parse()函数
yield scrapy.FormRequest()
	url      POST 请求
	formdata     dict格式
	headers     dict
	cookies       dict
	callback    回调函数
	
yield scrapy.Request()
yield     方法调用一般加yield表示返回这个对西那个给引擎

八、代理

settings文件配置 开启DOWNLOADER_MIDDLEWARES

middlewares文件修改 实现下载中间件制定的类中的process_request方法

添加代码:request.meta[‘proxy’] = ‘http://101.236.60.8:8866’

九、写入MYSQL

首先在__init__中用pymysql创建一个链接

conn = pymysql.Connect(
						。。。。。
                       db='gcdm',
                       charset='utf8')
                       
 
然后创建一个db模块,相当于一个小型的dao

from dm530 import conn

def exists_author(name):                       #这个函数封装的时有没有这个动漫
    sql="""
        select id from dm
        where name=%s
    """
    with conn.cursor() as c:
        try:
            c.execute(sql)
            ret=c.fetchall()
            return ret
        except:
            return None

def query(sql,args=None):
    with conn.cursor() as c:                               #这个函数封装的时查找动漫的原生sql
        try:
            c.execute(sql,args=args)
            ret=c.fetchall()
            return ret
        except Exception as e:
            print('--**********->',e)
            return False

def raw(sql,args=None):
    with conn.cursor() as c:
        try:                                                                 #这个函数封装的是提交的原生sql
            c.execute(sql,args)
            conn.commit()
        except Exception as e:
            print('--///->',e,sql,args)
            conn.rollback()
            
            
 自定义一个管道
class MysqlPipeline:
    def __init__(self):
        sql1 = 'select table_name from information_schema where tb_dm'
        sql2="create table tb_dm(id varchar(32),alex varchar(100),area varchar(15),category varchar(128),cover varchar(128), path varchar(128))"

        if not db.query(sql1):
            db.raw(sql2)

    def process_item(self, item, spider):

        sql="insert into %s(%s) values(%s)"
        item['id']=item.pop('_id')
        item.pop('name')
        field=','.join([i for i in item])
        values=','.join('%%(%s)s'%i for i in item)
        if isinstance(item,Dm530Item):
            tb_name='tb_dm'
        else:
            tb_name=None
            print("普通的item")

        if tb_name:
            db.raw(sql%(tb_name,field,values),dict(item))

        return item

十、使用分布式爬虫

在爬虫文件中,首先让爬虫类继承RedisCrawlSpider'
from scrapy_redis.spiders import RedisCrawlSpider
安装 pip install scrapy-redis

在setting里面设置
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"         #用来去重,自带的  ,去重过滤器
SCHEDULER = 'scrapy_redis.scheduler.Scheduler'      #使用自带的调度器
SCHEDULER_PERSIST= True      #是否保留驱虫的记录,当等于True时,当重启时会继续调用原来的
REDIS_URL='redis://@127.0.0.1:6379/2'
REDIS_ENCODING ='utf-8'
REDIS_HOST = '127.0.0.1'
REDIS_PORT =6379

然后在爬虫类里面定义  redis_key = 'gcdm:start_urls'
将服务启动起来之后就需要往Redis中去推任务

from redis import Redis
_rd=Redis('127.0.0.1',port=6379,db=2)

if __name__ == '__main__':
    _rd.lpush('gcdm:start_urls','http://www.dm530.cc/')
    
然后分布式爬虫就启动起来了


十一、使用规则爬虫

首先创建项目之后,初始化项目,使用scrapy crawl -t crawl 爬虫名  域名

进入爬虫类在rulers里面定义规则
rules = (
        Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),
    )
  LinkExtractor 常用的从参数可以设置  allow 选取的正则
  deny   不选取的正则    
  restrict_xpaths 	可以用xpath提取到链接的父类上
  restrict_css 			可以用css提取到链接的父类上
  
  
 在源码中有 _requests_to_follow这个函数,首先会判断副response是不HtmlResponse格式的,然后定义一个集合,
 通过rule.link_extractor.extract_links(response)提取到规则下面的链接,然后循环遍历,rule.process_links,他会用Rlue里面的_get_method 方法,这个函数最后回调用getattr(spider,process_linkes),把里面的href提取出来,然后放到定义的集合下面,然后创建Request对象,然后调用process_request进行下载


callback表示回调函数,也就是说在LinkExtractor中会根据你的规则创建一个请求去访问,当返回response时就会交给你的callback函数去解析

follow表示,的那个访问返回一个新页面时,要不要用LinkExtractor规则里的规则去继续使用在返回的页面中




十二、使用数据管道

class Dm530Pipeline:
    def process_item(self, item, spider):
        item.pop('images')
        item.pop('image_urls')
        item['category'] = ','.join(item['category'])
        return item
        
        
    对数据的格式进行改变

十三 、使用selinum

在scrapy中可以在process_request用selinum实现抓取js渲染的页面
    def process_request(self, request, spider):
        # Called for each request that goes through the downloader
        # middleware.
        if not request.url.endswith('.jpg'):
            spider.browser.get(request.url)
            for i in range(1,3):
                js="var d = document.documentElement.scrollTop=%s;"%(i*1000)
                spider.browser.execute_script(js)
                time.sleep(2)
            html=spider.browser.page_source
            return HtmlResponse(request.url,body=html.encode('utf-8'))

十四、写入到Mongodb中

将数据写入Mongo中,首先在scrapy框架中添加Mongo
先在Mongo中创建一个库
db.createCollection('gcdm1')
然后在你的环境下pip install pymongo
在中间件from_crawler中添加一个关闭信号,然后在爬虫开启和爬虫关闭的时候分别打开和关闭Mongo链接

    def spider_opened(self, spider):
        self.Client=MongoClient('127.0.0.1',port=27017)
        spider.db=self.Client.gcdm1
        spider.logger.info('Spider opened: %s' % spider.name)
        
    def spider_closed(self, spider):
        self.Client.close()
        spider.logger.info('Spider closed: %s' % spider.name)
        
        
     在管道中开一个管道用来存放
    
    class MongoPipeline:
    def process_item(self, item, spider):
        item['_id']=uuid.uuid4().hex          #需要在items中添加_id字段
        spider.db.gcdm1.insert_one(item)
        return item
        
       

十五、使用图片管道


首先在setting里面设置图片存储路径
IMAGES_STORE='images'
首先在items里面定制一个字段类
import scrapy
class Dm530Item(scrapy.Item):
    name=scrapy.Field()
    alex=scrapy.Field()
    area=scrapy.Field()
    category=scrapy.Field()
    cover=scrapy.Field()
    image_urls=scrapy.Field()
    images=scrapy.Field()
    
然后把在爬虫类里面,给这个类的实例,并用它来配置
    def parse_item(self, response):
        。。。。。。。
        item['image_urls']=[item['category']]
        item['images']=[]
        return item
    
    
在settings里面把ITEM_PIPELINES中的
ITEM_PIPELINES = {'scrapy.pipelines.images.ImagesPipeline': 1}  这里和官网不太一样,scrapy没有contirb,pipelines改为pipelines


图片存储:文件系统存储
首先,文件系统会对你的url进行sha1 hash,然后会在你的url前面添加 setting中的IMAGES_STORE/full/.....jpg
这就是你的文件存储路径

在settings里面可以添加
IMAGES_EXPIRES=90
则记为90天后失效

缩略图生成设置土坯那字典

IMAGES_THUMBS = {
    'small': (50, 50),
    'big': (270, 270),
}
这时所旅途的格式为
<IMAGES_STORE>/full/63bbfea82b8880ed33cdb762aa11fab722a90a24.jpg        完整图片
<IMAGES_STORE>/thumbs/small/63bbfea82b8880ed33cdb762aa11fab722a90a24.jpg    小图
<IMAGES_STORE>/thumbs/big/63bbfea82b8880ed33cdb762aa11fab722a90a24.jpg       大图



过滤出小尺寸
IMAGES_MIN_HEIGHT=110
IMAGES_MIN_WIDTH=110

十六、自定义图片管道

class MyImagesPipeline(ImagesPipeline):
    def get_media_requests(self, item, info):
        # 发起下载图片的请求
        yield Request(item['image_urls'][0],
                      meta=dict(name=item['name']))

    def item_completed(self, results, item, info):
        # 将下载的保存的地址更新到item中,并返回item
        print('item_completed-->>', results)             #item_completed-->> [(True, {'url': 'http://img8.dm530.net/pic/uploadimg/2020-			    4/20204423475664192.jpg', 'path': '游戏王SEVENS/62e00b1ecfeb4ab0bfaa57cc2757ca53.jpg', 'cheum': '47683e74a0e1d7e8b55466cc4ad69ab7'})]

        paths = [x['path'] for ok, x in results if ok]
        item['path'] = paths[0]                   #给items中的字段体加path
        return item  # 最终的item

    def file_path(self, request, response=None, info=None):
        # 返回相于settings中配置的IMAGES_STORE目录的文件路径
        # eg. 上海师范大学天华学院校花何丹丹/xx.jpg
        dir_name = request.meta['name']
        dir_path = os.path.join(IMAGES_STORE, dir_name)
        if not os.path.exists(dir_path):
            os.mkdir(dir_path) # 用户的第一次图片下载

        file_name = uuid.uuid4().hex+'.jpg'
        return '%s/%s' % (dir_name, file_name)
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值