Scrapy框架2

一、进程、线程、协成

  1、进程、线程、协成之间的关系
           1、  线程是计算机中最小的工作单元。
              2、 进程是提供资源供n个线程使用,即进程是最小的管理单元。

      3、协程是人为控制的线程。

      4、总结:1、python中由于有 GIL锁的存在,所以一个进程中同一时刻只有一个线程被CPU调度,所以在计算密集型,使用多进程而在io密集型使用多线程。

         2、使用协成可以实现单线程下的并发,线程中cpu在遇到io操作时就会切到下一个线程中,这样大大的影响的效率于是人为的控制cpu在线程中的切换,当cpu在线程中遇到io会切换到该线程的下一个程序中这就是协成

  2、异步非阻塞模块

   1、gevent模块,基于协程的异步非阻塞模块。
           2、Twisted,基于事件驱动(while循环检测)异步非阻塞模块(框架)。
           3、非阻塞是指不等待 ,异步是指回调,执行某个任务完成之后,自动执行的函数。回调函数

二、scarpy框架

  1、重写scrapy请求头方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# -*- coding: utf-8 -*-
import scrapy
from scrapy.http import Request
from scrapy.selector import HtmlXPathSelector             #response= HtmlXPathSelector(response)主要是在parse函数中使用,from urllib.parse import urlencode from ..items import MyscrapyItem
class BaiduSpider(scrapy.Spider):
    name = 'baidu' #爬虫应用的名称,通过此名称启动爬虫命令
    allowed_domains = ['www.amazon.cn']       # 允许爬取数据的域名
    def __init__(self,keywords=None,*args,**kwargs):
        super(BaiduSpider,self).__init__(*args,**kwargs)
        self.keywords=keywords  #主要是接受一些外部传进来的参数
    def start_requests(self):  #重写scrapy请求头url必须写这个名字的函数
        url='https://www.amazon.cn/s/ref=nb_sb_noss_1?'  #新url
        parmas={
            'field-keywords':self.keywords        #主要是定义一些外部传进来的参数
        }
        url=url+urlencode(parmas,encoding='utf-8') #路径拼接,形成一个完整的url路径
        yield Request(url,callback=self.parse_index)   #yield返回数据给回调函数,也可以是return+[]的方式返回数据给回调函数,yield返回的是生成器,return+[]返回的可迭代对象
        # return [Request(url='www.baidu.com',callback=self.parse),]
 
    def parse_index(self, response):
        urls=response.xpath('//*[contains(@id,"result_"]/div/div[3]/div[1]/a/@href').extract()
        for url in urls:
            print(url)
            yield Request(url,callback=self.parse) #返回详情页的信息
        next_url=response.urljoin(response.xpath('//*[@id="pagnNextLink"]/@href').extract_first())
        yield Request(next_url,callback=self.parse_index)  #返回下一页的url路径,返回的是个Request对象
    def parse(self, response):
        price=response.xpath('//*[@id="priceblock_ourprice"]/text()').extract_first().strip()
        colour=response.xpath('//*[@id="variation_color_name"]/div/span/text()').extract_first().strip()
        item=MyscrapyItem()
        item['price']=price
        item['colour']=colour
        return item  #返回的是个item对象     或者是这种格式,只要返回的是个item对象及格     obj = XiaoHuarItem(price=price, colour=colour, )      yield obj

   2、items文件写法

1
2
3
4
import scrapy
class MyscrapyItem(scrapy.Item):
    price=scrapy.Field()
    colour=scrapy.Field()

   3、自定义pipelines文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from scrapy.exceptions import DropItem    #主动触发异常用的
class CustomPipeline(object):
    def __init__(self,val):
        self.vale = val              #构造一个对象特有的方法
    def process_item(self, item, spider):       #用于存储爬取的数据,可以存储在文件也可以存储在数据库。
       
       f=opne('item.log','a+')<br>     f.write(item.price,item.colour)<br>     f.close()
        return item        # return表示会被后续的pipeline继续处理,因为有可以一个爬虫项目会有多个爬虫程序一起执行,但是都有写入同一个文件中,那就让第一个文件写入,其余后面的文件不用重复写入。
        
        # raise DropItem()       # 表示将item丢弃,不会被后续pipeline处理
    # if spider.name='baidu':  #这就表示该文件只是存储这个名字的爬虫爬取的数据<br>       #f=opne('item.log','a+')<br>        #f.write(item.price,item.colour)<br>       #f.close()<br>    #return item<br>
    @classmethod
    def from_crawler(cls, crawler):#初始化时候,用于创建pipeline对象,crawler这个类可以得到settings配置文件内的数据,如果需要给pipeline对象创建某个额外的参数就可以用这中方法
        val = crawler.settings.getint('MMMM') #val = crawler.settings.get('MMMM')   读取配置文件的某个信息
        return cls(val)
 
    def open_spider(self,spider): #在爬虫程序开始执行之前调用该方法
        print('000000')
 
    def close_spider(self,spider): #在爬虫程序关闭后调用该方法
        print('111111')<br><br>总结:这五个方法的执行顺序: 先执行from_crawler方法,用于初始化数据的时候读取配置文件,然后再执行爬虫程序的__init__方法,然后程序启动前执行open_spider方法,程序结束前保存数据时执行process_item方法,程序结果后<br>执行close_spider方法。

   4、自定义url去重功能

1
2
3
scrapy默认使用 scrapy.dupefilter.RFPDupeFilter 进行去重,settings相关配置有: DUPEFILTER_CLASS = 'scrapy.dupefilter.RFPDupeFilter'      #去重文件,可以自定义
DUPEFILTER_DEBUG = False  #默认去重功能是关闭的可以在settings中打开去重功能也可以在爬虫程序中手动指定去重 yield Request(url,callback=self.parse,dont_filter=True)
JOBDIR = "保存范文记录的日志路径,如:/root/"  # 最终路径为 /root/requests.seen 默认url是存放在内存中的,只要添加上jobdir就可以吧url存放在指定文件中

   自己写一个去重规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class RepeatUrl:
    def __init__(self):
        self.visited_url = set() #默认是一个集合,也可以是自定义链接一个数据库,文件等
 
    @classmethod
    def from_settings(cls, settings):
        """
        初始化时,调用
        """
        return cls()
 
    def request_seen(self, request):
        """
        检测当前请求是否已经被访问过
        :param request:
        :return: True表示已经访问过;False表示未访问过
        """
        if request.url in self.visited_url:
            return True
        self.visited_url.add(request.url)
        return False
 
    def open(self):
        """
        开始爬去请求时,调用
        :return:
        """
        print('open replication')
 
    def close(self, reason):
        """
        结束爬虫爬取时,调用
        :param reason:
        :return:
        """
        print('close replication')
 
    def log(self, request, spider):
        """
        记录日志
        :param request:
        :param spider:
        :return:
        """
        print('repeat', request.url)<br>注意:写好自定义的去重规则后要将该文件设置到settings中该规程才能生效 比如:DUPEFILTER_CLASS = 'scrapy.dupefilter.RFPDupeFilter'

   5、自定义命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#自定制一键启动所有爬虫命令   from scrapy.commands import ScrapyCommand
 from scrapy.utils.project import get_project_settings
 
 class Command(ScrapyCommand):
     requires_project = True
 
     def syntax(self):
         return '[options]'
 
     def short_desc(self):
         return 'Runs all of the spiders'
 
     def run(self, args, opts):      #这是源码的入口
         spider_list = self.crawler_process.spiders.list()
         for name in spider_list:        #循环所有的爬虫程序名称
             self.crawler_process.crawl(name, **opts.__dict__)        #准备所有的爬虫程序
         self.crawler_process.start()         #开始爬取任务注意:1、在spiders同级创建任意目录,如:commands   2、在其中创建 crawlall.py 文件 (此处文件名就是自定义的命令)   3、在settings.py 中添加配置 COMMANDS_MODULE = '项目名称.目录名称'    4、在项目目录执行命令:scrapy crawlall    #自定制在py文件中启动某个爬虫命令    from scrapy.cmdline import execute    execute(['scrapy', '命令', '爬虫程序名称','-a','传入的参数'])  eg:execute(['scrapy', 'crawl', 'baidu','-a','keywords=iphone7'])注意:自定制的py文件名称必须是entrypoint.py

     6、自定义信号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from scrapy import signals
class MyExtension(object):
    def __init__(self, value):
        self.value = value    #定义自己想要的方法
 
    @classmethod
    def from_crawler(cls, crawler): #扩展功能,在扩展功能里注册信号
        val = crawler.settings.getint('MMMM')#读取配置文件信息
        ext = cls(val)
        crawler.signals.connect(ext.spider_opened, signal=signals.spider_opened) #注册信号
        crawler.signals.connect(ext.spider_closed, signal=signals.spider_closed) #注册信号,可以注册很多信号,看源码
        return ext   #如果不需要扩展功能那么就直接注册信号即可
 
    def spider_opened(self, spider): #爬虫程序启动时触发的信号
        print('open')
 
    def spider_closed(self, spider): #爬虫程序关闭时触发的信号
        print('close')注释:1、在项目目录下创建一个py文件,将自定义的信号写入该文件,建议py文件名为extensions   2、在settings中注册自定义信号的文件       eg:EXTENSIONS = {'scrapy.extensions.telnet.TelnetConsole': 300,}

    7、中间件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#下载中间件class DownMiddleware1(object):
    def process_request(self, request, spider): #请求过去的中间件带的时request
        """
        请求需要被下载时,经过所有下载器中间件的process_request调用
        return: 
            None,继续后续中间件去下载,默认就是什么都不返回;
            Response对象,停止process_request的执行,开始执行process_response,先导入Response(from scrapy.http import Response)       eg:return  Response(url='http://www.baidu.com',request=request)  #只要返回Response对象就不会执行后续的Request,而是直接执行Response
            Request对象,停止中间件的执行,将Request重新调度器    #如果返回的是Request对象那就不会执行后续的Request和Reques而是直接到引擎,重新调度
            raise IgnoreRequest异常,停止process_request的执行,开始执行process_exception
        """
        pass
 
    def process_response(self, request, response, spider): #返回数据的中间件带的时response
        """
        spider处理完成,返回时调用,默认返回的就是Response
        return:
            Response 对象:转交给其他中间件process_response
            Request 对象:停止中间件,request会被重新调度下载
            raise IgnoreRequest 异常:调用Request.errback
        """
        return response
 
    def process_exception(self, request, exception, spider):
        """
        当下载处理器(download handler)或 process_request() (下载中间件)抛出异常的时候调用
        :return:
            None:继续交给后续中间件处理异常;
            Response对象:停止后续process_exception方法,正确执行后续方法
            Request对象:停止中间件后续方法,request将会被重新调用下载
        """
        return None注释:1、在项目目录下创建.py文件,将中间件代码写在该文件中,建议文件名称为DownMiddleware   2、在settings中注册中间件,DOWNLOADER_MIDDLEWARES = {'chouti.middlewares.MyCustomDownloaderMiddleware': 600,},写了几个中间件就注册几个中间件   3、可以在中间件的process_request中将所有的Request请求添加上请求头,添加cookies等操作,也可以添加代理,判断那个Request需要代理。

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#爬虫中间件
class SpiderMiddleware(object):
    def process_spider_input(self,response, spider):
        """下载完成,执行,然后交给parse处理,默认返回none"""
        pass
    def process_spider_output(self,response, result, spider):
        """spider处理完成,返回时调用,return: 必须返回包含 Request 或 Item 对象的可迭代对象(iterable)"""
        return result
    def process_spider_exception(self,response, exception, spider):
        """异常调用,return: None,继续交给后续中间件处理异常;含 Response 或 Item 的可迭代对象(iterable),交给调度器或pipeline"""
        return None
    def process_start_requests(self,start_requests, spider):
        """爬虫启动时调用,return: 包含 Request 对象的可迭代对象,由于爬虫只是启动一次所以process_start_requests也只是调用一次 """
        return start_requests注释:1、在项目目录下创建.py文件,将中间件代码写在该文件中,建议文件名称为SpiderMiddleware   2、在settings中注册中间件,SPIDER_MIDDLEWARES = 'chouti.middlewares.ChoutiSpiderMiddleware': 600,},写了几个中间件就注册几个中间件

   8、自定义代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
代理,需要在环境变量中设置
    from scrapy.contrib.downloadermiddleware.httpproxy import HttpProxyMiddleware
     
    方式一:使用默认<br>     import os
        os.environ
        {<br>      http_proxy:http://root:woshiniba@192.168.11.11:9999/     #有的代理链接是需要用户名和密码的,这个就是root为用户名,woshiniba为密码
         https_proxy:http://192.168.11.11:9999/<br>      } #该方法只需要在爬虫程序文件的类的__init__中添加上就可以了,程序会判断只要是_prxoy结尾的就会吧这个代理拿出来<br>
    方式二:使用自定义下载中间件
    def to_bytes(text, encoding=None, errors='strict'):
        if isinstance(text, bytes):
            return text
        if not isinstance(text, six.string_types):
            raise TypeError('to_bytes must receive a unicode, str or bytes '
                            'object, got %s' % type(text).__name__)
        if encoding is None:
            encoding = 'utf-8'
        return text.encode(encoding, errors)
         
    class ProxyMiddleware(object):#代理中间件
        def process_request(self, request, spider):
            PROXIES = [
                {'ip_port': '111.11.228.75:80', 'user_pass': ''},
                {'ip_port': '120.198.243.22:80', 'user_pass': ''},
                {'ip_port': '111.8.60.9:8123', 'user_pass': ''},
                {'ip_port': '101.71.27.120:80', 'user_pass': ''},
                {'ip_port': '122.96.59.104:80', 'user_pass': ''},
                {'ip_port': '122.224.249.122:8088', 'user_pass': ''},
            ]
            proxy = random.choice(PROXIES)#随机选择一个代理,高明就高明在这个地方,默认代理是不能随机生成的,所有也会有被封的风险。
            if proxy['user_pass'] is not None:
                request.meta['proxy'] = to_bytes("http://%s" % proxy['ip_port'])#添加上代理
                encoded_user_pass = base64.encodestring(to_bytes(proxy['user_pass']))#代理加密,固定加密方法
                request.headers['Proxy-Authorization'] = to_bytes('Basic ' + encoded_user_pass) #代理授权
                #print "**************ProxyMiddleware have pass************" + proxy['ip_port']
            else:
                #print "**************ProxyMiddleware no pass************" + proxy['ip_port']
                request.meta['proxy'] = to_bytes("http://%s" % proxy['ip_port'])#添加代理
     
   注释:1、在爬虫项目路径中创建代理文件<br>     2、在settings中注册上代理 DOWNLOADER_MIDDLEWARES = { 'step8_king.middlewares.ProxyMiddleware': 500,}

   9、settings配置文件详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
1. 爬虫名称
BOT_NAME = 'step8_king'
 
2. 爬虫应用路径
SPIDER_MODULES = ['step8_king.spiders'] #其实就是创建的爬虫程序名称+spiders
NEWSPIDER_MODULE = 'step8_king.spiders'<br>
3. 客户端 user-agent请求头
# USER_AGENT = 'step8_king (+http://www.yourdomain.com)'
 
4. 禁止爬虫配置(机器人协议)
# ROBOTSTXT_OBEY = True<br>
5. 并发请求数
# CONCURRENT_REQUESTS = 4  #一个线程最大能并发多少个请求,力度粗
 
6. 延迟下载秒数
# DOWNLOAD_DELAY = 2   默认是秒,每多少秒一次下载
 
7. 单域名访问并发数,并且延迟下次秒数也应用在每个域名 力度细
# CONCURRENT_REQUESTS_PER_DOMAIN = 2
# 单IP访问并发数,如果有值则忽略:CONCURRENT_REQUESTS_PER_DOMAIN,并且延迟下次秒数也应用在每个IP 力度细
# CONCURRENT_REQUESTS_PER_IP = 3
 
8. 是否支持cookie,cookiejar进行操作cookie
# COOKIES_ENABLED = True
# COOKIES_DEBUG = True<br>
9. Telnet用于查看当前爬虫的信息,操作爬虫等...
#  使用telnet ip port ,然后通过命令操作
# TELNETCONSOLE_ENABLED = True
# TELNETCONSOLE_HOST = '127.0.0.1'
# TELNETCONSOLE_PORT = [6023,]
 
10. 默认请求头,死板
# 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',
# }
 
11. 定义pipeline处理请求
# ITEM_PIPELINES = {
#    'step8_king.pipelines.JsonPipeline': 700,
#    'step8_king.pipelines.FilePipeline': 500,
# }
 
12. 自定义扩展,基于信号进行调用
# Enable or disable extensions
# See http://scrapy.readthedocs.org/en/latest/topics/extensions.html
# EXTENSIONS = {
#     # 'step8_king.extensions.MyExtension': 500,
# }
 
 
13. 爬虫允许的最大深度,可以通过meta查看当前深度;0表示无深度
# DEPTH_LIMIT = 3
 
14. 爬取时,0表示深度优先Lifo(默认);1表示广度优先FiFo
 
# 后进先出,深度优先
# DEPTH_PRIORITY = 0
# SCHEDULER_DISK_QUEUE = 'scrapy.squeue.PickleLifoDiskQueue'
# SCHEDULER_MEMORY_QUEUE = 'scrapy.squeue.LifoMemoryQueue'<br>
# 先进先出,广度优先
# DEPTH_PRIORITY = 1
# SCHEDULER_DISK_QUEUE = 'scrapy.squeue.PickleFifoDiskQueue'
# SCHEDULER_MEMORY_QUEUE = 'scrapy.squeue.FifoMemoryQueue'
 
15. 调度器队列
# SCHEDULER = 'scrapy.core.scheduler.Scheduler'
# from scrapy.core.scheduler import Scheduler
 
 
16. 访问URL去重
# DUPEFILTER_CLASS = 'step8_king.duplication.RepeatUrl'
"""
17. 自动限速算法
    from scrapy.contrib.throttle import AutoThrottle
    自动限速设置
    1. 获取最小延迟 DOWNLOAD_DELAY
    2. 获取最大延迟 AUTOTHROTTLE_MAX_DELAY
    3. 设置初始下载延迟 AUTOTHROTTLE_START_DELAY
    4. 当请求下载完成后,获取其"连接"时间 latency,即:请求连接到接受到响应头之间的时间
    5. 用于计算的... AUTOTHROTTLE_TARGET_CONCURRENCY
    target_delay = latency / self.target_concurrency
    new_delay = (slot.delay + target_delay) / 2.0 # 表示上一次的延迟时间
    new_delay = max(target_delay, new_delay)
    new_delay = min(max(self.mindelay, new_delay), self.maxdelay)
    slot.delay = new_delay
"""

   10、https证书设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Https访问时有两种情况:
1. 要爬取网站使用的可信任证书(默认支持)
    DOWNLOADER_HTTPCLIENTFACTORY = "scrapy.core.downloader.webclient.ScrapyHTTPClientFactory"
    DOWNLOADER_CLIENTCONTEXTFACTORY = "scrapy.core.downloader.contextfactory.ScrapyClientContextFactory"
     
2. 要爬取网站使用的自定义证书
    DOWNLOADER_HTTPCLIENTFACTORY = "scrapy.core.downloader.webclient.ScrapyHTTPClientFactory"
    DOWNLOADER_CLIENTCONTEXTFACTORY = "step8_king.https.MySSLFactory"
     
    # https.py
    from scrapy.core.downloader.contextfactory import ScrapyClientContextFactory
    from twisted.internet.ssl import (optionsForClientTLS, CertificateOptions, PrivateCertificate)
     
    class MySSLFactory(ScrapyClientContextFactory):
        def getCertificateOptions(self):
            from OpenSSL import crypto
            v1 = crypto.load_privatekey(crypto.FILETYPE_PEM, open('/Users/wupeiqi/client.key.unsecure', mode='r').read()) #打开本地保存的证书文件
            v2 = crypto.load_certificate(crypto.FILETYPE_PEM, open('/Users/wupeiqi/client.pem', mode='r').read())
            return CertificateOptions(
                privateKey=v1,  # pKey对象
                certificate=v2,  # X509对象
                verify=False,
                method=getattr(self, 'method', getattr(self, '_ssl_method', None))
            )
其他:
    相关类
        scrapy.core.downloader.handlers.http.HttpDownloadHandler
        scrapy.core.downloader.webclient.ScrapyHTTPClientFactory
        scrapy.core.downloader.contextfactory.ScrapyClientContextFactory
    相关配置
        DOWNLOADER_HTTPCLIENTFACTORY
        DOWNLOADER_CLIENTCONTEXTFACTORY

 

posted @   mx轩  阅读(269)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示