zoukankan      html  css  js  c++  java
  • 三、scrapy后续

    CrawlSpiders

    通过下面的命令可以快速创建 CrawlSpider模板 的代码:

    scrapy genspider -t crawl tencent tencent.com

    我们通过正则表达式,制作了新的url作为Request请求参数,现在我们可以用这个...

    class scrapy.spiders.CrawlSpider

    它是Spider的派生类,Spider类的设计原则是只爬取start_url列表中的网页,而CrawlSpider类定义了一些规则(rule)来提供跟进link的方便的机制,从爬取的网页中获取link并继续爬取的工作更适合。

    在网上找了一段源码参考:

     1 class CrawlSpider(Spider):
     2     rules = ()
     3     def __init__(self, *a, **kw):
     4         super(CrawlSpider, self).__init__(*a, **kw)
     5         self._compile_rules()
     6 
     7     #首先调用parse()来处理start_urls中返回的response对象
     8     #parse()则将这些response对象传递给了_parse_response()函数处理,并设置回调函数为parse_start_url()
     9     #设置了跟进标志位True
    10     #parse将返回item和跟进了的Request对象    
    11     def parse(self, response):
    12         return self._parse_response(response, self.parse_start_url, cb_kwargs={}, follow=True)
    13 
    14     #处理start_url中返回的response,需要重写
    15     def parse_start_url(self, response):
    16         return []
    17 
    18     def process_results(self, response, results):
    19         return results
    20 
    21     #从response中抽取符合任一用户定义'规则'的链接,并构造成Resquest对象返回
    22     def _requests_to_follow(self, response):
    23         if not isinstance(response, HtmlResponse):
    24             return
    25         seen = set()
    26         #抽取之内的所有链接,只要通过任意一个'规则',即表示合法
    27         for n, rule in enumerate(self._rules):
    28             links = [l for l in rule.link_extractor.extract_links(response) if l not in seen]
    29             #使用用户指定的process_links处理每个连接
    30             if links and rule.process_links:
    31                 links = rule.process_links(links)
    32             #将链接加入seen集合,为每个链接生成Request对象,并设置回调函数为_repsonse_downloaded()
    33             for link in links:
    34                 seen.add(link)
    35                 #构造Request对象,并将Rule规则中定义的回调函数作为这个Request对象的回调函数
    36                 r = Request(url=link.url, callback=self._response_downloaded)
    37                 r.meta.update(rule=n, link_text=link.text)
    38                 #对每个Request调用process_request()函数。该函数默认为indentify,即不做任何处理,直接返回该Request.
    39                 yield rule.process_request(r)
    40 
    41     #处理通过rule提取出的连接,并返回item以及request
    42     def _response_downloaded(self, response):
    43         rule = self._rules[response.meta['rule']]
    44         return self._parse_response(response, rule.callback, rule.cb_kwargs, rule.follow)
    45 
    46     #解析response对象,会用callback解析处理他,并返回request或Item对象
    47     def _parse_response(self, response, callback, cb_kwargs, follow=True):
    48         #首先判断是否设置了回调函数。(该回调函数可能是rule中的解析函数,也可能是 parse_start_url函数)
    49         #如果设置了回调函数(parse_start_url()),那么首先用parse_start_url()处理response对象,
    50         #然后再交给process_results处理。返回cb_res的一个列表
    51         if callback:
    52             #如果是parse调用的,则会解析成Request对象
    53             #如果是rule callback,则会解析成Item
    54             cb_res = callback(response, **cb_kwargs) or ()
    55             cb_res = self.process_results(response, cb_res)
    56             for requests_or_item in iterate_spider_output(cb_res):
    57                 yield requests_or_item
    58 
    59         #如果需要跟进,那么使用定义的Rule规则提取并返回这些Request对象
    60         if follow and self._follow_links:
    61             #返回每个Request对象
    62             for request_or_item in self._requests_to_follow(response):
    63                 yield request_or_item
    64 
    65     def _compile_rules(self):
    66         def get_method(method):
    67             if callable(method):
    68                 return method
    69             elif isinstance(method, basestring):
    70                 return getattr(self, method, None)
    71 
    72         self._rules = [copy.copy(r) for r in self.rules]
    73         for rule in self._rules:
    74             rule.callback = get_method(rule.callback)
    75             rule.process_links = get_method(rule.process_links)
    76             rule.process_request = get_method(rule.process_request)
    77 
    78     def set_crawler(self, crawler):
    79         super(CrawlSpider, self).set_crawler(crawler)
    80         self._follow_links = crawler.settings.getbool('CRAWLSPIDER_FOLLOW_LINKS', True)
    View Code

    CrawlSpider继承于Spider类,除了继承过来的属性外(name、allow_domains),还提供了新的属性和方法:

    LinkExtractors

    class scrapy.linkextractors.LinkExtractor

    Link Extractors 的目的很简单: 提取链接。

    每个LinkExtractor有唯一的公共方法是 extract_links(),它接收一个 Response 对象,并返回一个 scrapy.link.Link 对象。

    Link Extractors要实例化一次,并且 extract_links 方法会根据不同的 response 调用多次提取链接。

     1 class scrapy.linkextractors.LinkExtractor(
     2     allow = (),
     3     deny = (),
     4     allow_domains = (),
     5     deny_domains = (),
     6     deny_extensions = None,
     7     restrict_xpaths = (),
     8     tags = ('a','area'),
     9     attrs = ('href'),
    10     canonicalize = True,
    11     unique = True,
    12     process_value = None
    13 )

    主要参数:

    1 allow:满足括号中“正则表达式”的值会被提取,如果为空,则全部匹配。
    2 
    3 deny:与这个正则表达式(或正则表达式列表)不匹配的URL一定不提取。
    4 
    5 allow_domains:会被提取的链接的domains。
    6 
    7 deny_domains:一定不会被提取链接的domains。
    8 
    9 restrict_xpaths:使用xpath表达式,和allow共同作用过滤链接。

    rules

    在rules中包含一个或多个Rule对象,每个Rule对爬取网站的动作定义了特定操作。如果多个rule匹配了相同的链接,则根据规则在本集合中被定义的顺序,第一个会被使用。

    1 class scrapy.spiders.Rule(
    2         link_extractor, 
    3         callback = None, 
    4         cb_kwargs = None, 
    5         follow = None, 
    6         process_links = None, 
    7         process_request = None
    8 )
    • link_extractor:是一个Link Extractor对象,用于定义需要提取的链接。

    • callback: 从link_extractor中每获取到链接时,参数所指定的值作为回调函数,该回调函数接受一个response作为其第一个参数。

    • follow:是一个布尔(boolean)值,指定了根据该规则从response提取的链接是否需要跟进。 如果callback为None,follow 默认设置为True ,否则默认为False。

    • process_links:指定该spider中哪个的函数将会被调用,从link_extractor中获取到链接列表时将会调用该函数。该方法主要用来过滤。

    • process_request:指定该spider中哪个的函数将会被调用, 该规则提取到每个request时都会调用该函数。 (用来过滤request)

     注意:当编写爬虫规则时,避免使用parse作为回调函数。由于CrawlSpider使用parse方法来实现其逻辑,如果覆盖了 parse方法,crawl spider将会运行失败。


     Logging

    Scrapy提供了log功能,可以通过 logging 模块使用。

    可以修改配置文件settings.py,任意位置添加下面两行,效果会清爽很多。

    LOG_FILE = "TencentSpider.log"
    LOG_LEVEL = "INFO"

    Log levels

    Scrapy提供5层logging级别:

    • CRITICAL - 严重错误(critical)

    • ERROR - 一般错误(regular errors)
    • WARNING - 警告信息(warning messages)
    • INFO - 一般信息(informational messages)
    • DEBUG - 调试信息(debugging messages)

    logging设置

    通过在setting.py中进行以下设置可以被用来配置logging:

    1. LOG_ENABLED 默认: True,启用logging
    2. LOG_ENCODING 默认: 'utf-8',logging使用的编码
    3. LOG_FILE 默认: None,在当前目录里创建logging输出文件的文件名
    4. LOG_LEVEL 默认: 'DEBUG',log的最低级别
    5. LOG_STDOUT 默认: False 如果为 True,进程所有的标准输出(及错误)将会被重定向到log中。例如,执行 print "hello" ,其将会在Scrapy log中显示。

     Request

     1 # 部分代码
     2 class Request(object_ref):
     3 
     4     def __init__(self, url, callback=None, method='GET', headers=None, body=None, 
     5                  cookies=None, meta=None, encoding='utf-8', priority=0,
     6                  dont_filter=False, errback=None):
     7 
     8         self._encoding = encoding  # this one has to be set first
     9         self.method = str(method).upper()
    10         self._set_url(url)
    11         self._set_body(body)
    12         assert isinstance(priority, int), "Request priority not an integer: %r" % priority
    13         self.priority = priority
    14 
    15         assert callback or not errback, "Cannot use errback without a callback"
    16         self.callback = callback
    17         self.errback = errback
    18 
    19         self.cookies = cookies or {}
    20         self.headers = Headers(headers or {}, encoding=encoding)
    21         self.dont_filter = dont_filter
    22 
    23         self._meta = dict(meta) if meta else None
    24 
    25     @property
    26     def meta(self):
    27         if self._meta is None:
    28             self._meta = {}
    29         return self._meta
    部分源代码

    其中,比较常用的参数:

    url: 就是需要请求,并进行下一步处理的url
    
    callback: 指定该请求返回的Response,由那个函数来处理。
    
    method: 请求一般不需要指定,默认GET方法,可设置为"GET", "POST", "PUT"等,且保证字符串大写
    
    headers: 请求时,包含的头文件。一般不需要。内容一般如下:
            # 自己写过爬虫的肯定知道
            Host: media.readthedocs.org
            User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64; rv:33.0) Gecko/20100101 Firefox/33.0
            Accept: text/css,*/*;q=0.1
            Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
            Accept-Encoding: gzip, deflate
            Referer: http://scrapy-chs.readthedocs.org/zh_CN/0.24/
            Cookie: _ga=GA1.2.1612165614.1415584110;
            Connection: keep-alive
            If-Modified-Since: Mon, 25 Aug 2014 21:59:35 GMT
            Cache-Control: max-age=0
    
    meta: 比较常用,在不同的请求之间传递数据使用的。字典dict型
    
            request_with_cookies = Request(
                url="http://www.example.com",
                cookies={'currency': 'USD', 'country': 'UY'},
                meta={'dont_merge_cookies': True}
            )
    
    encoding: 使用默认的 'utf-8' 就行。
    
    dont_filter: 表明该请求不由调度器过滤。这是当你想使用多次执行相同的请求,忽略重复的过滤器。默认为False。
    
    errback: 指定错误处理函数

    Response

     1 # 部分代码
     2 class Response(object_ref):
     3     def __init__(self, url, status=200, headers=None, body='', flags=None, request=None):
     4         self.headers = Headers(headers or {})
     5         self.status = int(status)
     6         self._set_body(body)
     7         self._set_url(url)
     8         self.request = request
     9         self.flags = [] if flags is None else list(flags)
    10 
    11     @property
    12     def meta(self):
    13         try:
    14             return self.request.meta
    15         except AttributeError:
    16             raise AttributeError("Response.meta not available, this response " 
    17                 "is not tied to any request")
    部分

    大部分参数和上面的差不多:

    status: 响应码
    _set_body(body): 响应体
    _set_url(url):响应url
    self.request = request

    发送POST请求

    • 可以使用 yield scrapy.FormRequest(url, formdata, callback)方法发送POST请求。

    • 如果希望程序执行一开始就发送POST请求,可以重写Spider类的start_requests(self) 方法,并且不再调用start_urls里的url。

     1 class mySpider(scrapy.Spider):
     2     # start_urls = ["http://www.example.com/"]
     3 
     4     def start_requests(self):
     5         url = 'http://www.renren.com/PLogin.do'
     6 
     7         # FormRequest 是Scrapy发送POST请求的方法
     8         yield scrapy.FormRequest(
     9             url = url,
    10             formdata = {"email" : "mr_mao_hacker@163.com", "password" : "axxxxxxxe"},
    11             callback = self.parse_page
    12         )
    13     def parse_page(self, response):
    14         # do something
    View Code

     Downloader Middlewares

    下载中间件是处于引擎(crawler.engine)和下载器(crawler.engine.download())之间的一层组件,可以有多个下载中间件被加载运行。

    1. 当引擎传递请求给下载器的过程中,下载中间件可以对请求进行处理 (例如增加http header信息,增加proxy信息等);

    2. 在下载器完成http请求,传递响应给引擎的过程中, 下载中间件可以对响应进行处理(例如进行gzip的解压等)

    要激活下载器中间件组件,将其加入到 DOWNLOADER_MIDDLEWARES 设置中。 该设置是一个字典(dict),键为中间件类的路径,值为其中间件的顺序(order)。

    这里是一个例子:

    DOWNLOADER_MIDDLEWARES = {
        'mySpider.middlewares.MyDownloaderMiddleware': 543,
    }
    

    编写下载器中间件十分简单。每个中间件组件是一个定义了以下一个或多个方法的Python类:

    class scrapy.contrib.downloadermiddleware.DownloaderMiddleware
    

    process_request(self, request, spider)

    • 当每个request通过下载中间件时,该方法被调用。

    • process_request() 必须返回以下其中之一:一个 None 、一个 Response 对象、一个 Request 对象或 raise IgnoreRequest:

      • 如果其返回 None ,Scrapy将继续处理该request,执行其他的中间件的相应方法,直到合适的下载器处理函数(download handler)被调用, 该request被执行(其response被下载)。

      • 如果其返回 Response 对象,Scrapy将不会调用 任何 其他的 process_request() 或 process_exception() 方法,或相应地下载函数; 其将返回该response。 已安装的中间件的 process_response() 方法则会在每个response返回时被调用。

      • 如果其返回 Request 对象,Scrapy则停止调用 process_request方法并重新调度返回的request。当新返回的request被执行后, 相应地中间件链将会根据下载的response被调用。

      • 如果其raise一个 IgnoreRequest 异常,则安装的下载中间件的 process_exception() 方法会被调用。如果没有任何一个方法处理该异常, 则request的errback(Request.errback)方法会被调用。如果没有代码处理抛出的异常, 则该异常被忽略且不记录(不同于其他异常那样)。

    • 参数:

      • request (Request 对象) – 处理的request
      • spider (Spider 对象) – 该request对应的spider

    process_response(self, request, response, spider)

    当下载器完成http请求,传递响应给引擎的时候调用

    • process_request() 必须返回以下其中之一: 返回一个 Response 对象、 返回一个 Request 对象或raise一个 IgnoreRequest 异常。

      • 如果其返回一个 Response (可以与传入的response相同,也可以是全新的对象), 该response会被在链中的其他中间件的 process_response() 方法处理。

      • 如果其返回一个 Request 对象,则中间件链停止, 返回的request会被重新调度下载。处理类似于 process_request() 返回request所做的那样。

      • 如果其抛出一个 IgnoreRequest 异常,则调用request的errback(Request.errback)。 如果没有代码处理抛出的异常,则该异常被忽略且不记录(不同于其他异常那样)。

    • 参数:

      • request (Request 对象) – response所对应的request
      • response (Response 对象) – 被处理的response
      • spider (Spider 对象) – response所对应的spider

     

     1 # middlewares.py
     2 
     3 #!/usr/bin/env python
     4 # -*- coding:utf-8 -*-
     5 
     6 import random
     7 import base64
     8 
     9 from settings import USER_AGENTS
    10 from settings import PROXIES
    11 
    12 # 随机的User-Agent
    13 class RandomUserAgent(object):
    14     def process_request(self, request, spider):
    15         useragent = random.choice(USER_AGENTS)
    16 
    17         request.headers.setdefault("User-Agent", useragent)
    18 
    19 class RandomProxy(object):
    20     def process_request(self, request, spider):
    21         proxy = random.choice(PROXIES)
    22 
    23         if proxy['user_passwd'] is None:
    24             # 没有代理账户验证的代理使用方式
    25             request.meta['proxy'] = "http://" + proxy['ip_port']
    26         else:
    27             # 对账户密码进行base64编码转换
    28             base64_userpasswd = base64.b64encode(proxy['user_passwd'])
    29             # 对应到代理服务器的信令格式里
    30             request.headers['Proxy-Authorization'] = 'Basic ' + base64_userpasswd
    31             request.meta['proxy'] = "http://" + proxy['ip_port']
    设置中间插件

    Settings

    Scrapy设置(settings)提供了定制Scrapy组件的方法。可以控制包括核心(core),插件(extension),pipeline及spider组件。比如 设置Json Pipeliine、LOG_LEVEL等。

    参考文档:http://scrapy-chs.readthedocs.io/zh_CN/1.0/topics/settings.html#topics-settings-ref

    内置设置参考手册

    • BOT_NAME

      • 默认: 'scrapybot'

      • 当您使用 startproject 命令创建项目时其也被自动赋值。

    • CONCURRENT_ITEMS

      • 默认: 100

      • Item Processor(即 Item Pipeline) 同时处理(每个response的)item的最大值。

    • CONCURRENT_REQUESTS
      • 默认: 16

      • Scrapy downloader 并发请求(concurrent requests)的最大值。

    • DEFAULT_REQUEST_HEADERS
      • 默认: 如下

        {
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
        'Accept-Language': 'en',
        }
        

        Scrapy HTTP Request使用的默认header。

    • DEPTH_LIMIT

      • 默认: 0

      • 爬取网站最大允许的深度(depth)值。如果为0,则没有限制。

    • DOWNLOAD_DELAY
      • 默认: 0

      • 下载器在下载同一个网站下一个页面前需要等待的时间。该选项可以用来限制爬取速度, 减轻服务器压力。同时也支持小数:

      DOWNLOAD_DELAY = 0.25 # 250 ms of delay

      • 默认情况下,Scrapy在两个请求间不等待一个固定的值, 而是使用0.5到1.5之间的一个随机值 * DOWNLOAD_DELAY 的结果作为等待间隔。
    • DOWNLOAD_TIMEOUT

      • 默认: 180

      • 下载器超时时间(单位: 秒)。

    • ITEM_PIPELINES
      • 默认: {}

      • 保存项目中启用的pipeline及其顺序的字典。该字典默认为空,值(value)任意,不过值(value)习惯设置在0-1000范围内,值越小优先级越高。

        ITEM_PIPELINES = {
        'mySpider.pipelines.SomethingPipeline': 300,
        'mySpider.pipelines.ItcastJsonPipeline': 800,
        }
        
    • LOG_ENABLED

      • 默认: True

      • 是否启用logging。

    • LOG_ENCODING

      • 默认: 'utf-8'

      • logging使用的编码。

    • LOG_LEVEL

      • 默认: 'DEBUG'

      • log的最低级别。可选的级别有: CRITICAL、 ERROR、WARNING、INFO、DEBUG 。

    • USER_AGENT
      • 默认: "Scrapy/VERSION (+http://scrapy.org)"

      • 爬取的默认User-Agent,除非被覆盖。

    • PROXIES: 代理设置
      • 示例:

        PROXIES = [
          {'ip_port': '111.11.228.75:80', 'password': ''},
          {'ip_port': '120.198.243.22:80', 'password': ''},
          {'ip_port': '111.8.60.9:8123', 'password': ''},
          {'ip_port': '101.71.27.120:80', 'password': ''},
          {'ip_port': '122.96.59.104:80', 'password': ''},
          {'ip_port': '122.224.249.122:8088', 'password':''},
        ]
        
    • COOKIES_ENABLED = False
      • 禁用Cookies

    爬取腾讯招聘所有页面的职位信息

    先想好需要什么信息:

     1 # -*- coding: utf-8 -*-
     2 
     3 # Define here the models for your scraped items
     4 #
     5 # See documentation in:
     6 # https://doc.scrapy.org/en/latest/topics/items.html
     7 
     8 import scrapy
     9 
    10 '''Item 定义结构化数据字段,用来保存爬取到的数据,有点像Python中的dict,但是提供了一些额外的保护减少错误。
    11 
    12 可以通过创建一个 scrapy.Item 类, 并且定义类型为 scrapy.Field的类属性来定义一个Item(可以理解成类似于ORM的映射关系)。'''
    13 class MyspiderItem(scrapy.Item):
    14     # define the fields for your item here like:
    15     #职位名
    16     name = scrapy.Field()
    17     #详细链接
    18     detailLink = scrapy.Field()
    19     #职位信息
    20     positionInfo = scrapy.Field()
    21     #人数
    22     peopleNumber = scrapy.Field()
    23     #工作地点
    24     workLocation = scrapy.Field()
    25     #发布时间
    26     publishTime = scrapy.Field()
    items.py

    写爬虫代码:(使用框架很简单,其实主要是提取数据)

     1 # -*- coding: utf-8 -*-
     2 import scrapy
     3 from myspider.items import MyspiderItem
     4 from scrapy.linkextractors import LinkExtractor
     5 from scrapy.spiders import CrawlSpider, Rule
     6 
     7 
     8 class TencentSpider(CrawlSpider):
     9     name = 'tencent'
    10     allowed_domains = ['tencent.com']
    11     start_urls = ['http://hr.tencent.com/position.php?&start=0#a']
    12     rules = (
    13         Rule(LinkExtractor(allow=r'position.php?&start=d+'), callback='parse_item', follow=True),
    14     )
    15 
    16     def parse_item(self, response):
    17         #i = {}
    18         #i['domain_id'] = response.xpath('//input[@id="sid"]/@value').extract()
    19         #i['name'] = response.xpath('//div[@id="name"]').extract()
    20         #i['description'] = response.xpath('//div[@id="description"]').extract()
    21         #return i
    22         for each in response.xpath('//*[@class="even"]'):
    23             name = each.xpath('./td[1]/a/text()').extract()[0]
    24             detailLink = each.xpath('./td[1]/a/@href').extract()[0]
    25             positionInfo = each.xpath('./td[2]/text()').extract()[0]
    26 
    27             peopleNumber = each.xpath('./td[3]/text()').extract()[0]
    28             workLocation = each.xpath('./td[4]/text()').extract()[0]
    29             publishTime = each.xpath('./td[5]/text()').extract()[0]
    30             # print name, detailLink, catalog,recruitNumber,workLocation,publishTime
    31 
    32             item = MyspiderItem()
    33             item['name'] = name
    34             item['detailLink'] = detailLink
    35             item['positionInfo'] = positionInfo
    36             item['peopleNumber'] = peopleNumber
    37             item['workLocation'] = workLocation
    38             item['publishTime'] = publishTime
    39 
    40             yield item
    Tencent.py
     1 import json
     2  
     3 class BaiSispiderPipeline():
     4     def __init__(self):
     5         self.filename = open("tencent.json", "w")
     6 
     7     def process_item(self, item, spider):
     8         text = json.dumps(dict(item), ensure_ascii = False) + ",
    "
     9         self.filename.write(text.encode('utf8'))
    10         return item
    11 
    12     def close_spider(self, spider):
    13         self.filename.close()
    Pipelines.py

    之前爬取校花网图片的那个,用CrawlSpider,几行代码就可以匹配到所有页面的链接,自己有去重的功能,爬取多页乃至全部

     1 # -*- coding: utf-8 -*-
     2 import scrapy
     3 from scrapy.linkextractors import LinkExtractor
     4 from scrapy.spiders import CrawlSpider,Rule
     5 from myspider.items import MyspiderItem
     6 
     7 class BaisiSpider(CrawlSpider):
     8     name = 'xiaohua'
     9     allowed_domains = ['www.521609.com']
    10     page_list = LinkExtractor(allow=('list\d+.html'))
    11     start_urls = ['http://www.521609.com/daxuexiaohua/list35.html']
    12     rules = (
    13         Rule(page_list,callback='parseImg',follow=True),
    14     )
    15     def parseImg(self, response):
    16 
    17         # 将我们得到的数据封装到一个 `MyspiderItem` 对象
    18         item = MyspiderItem()
    19 
    20         #提取数据
    21         img_list = response.xpath('//div[@class="index_img list_center"]/ul/li')
    22         for img in img_list:
    23             img_name = img.xpath('./a/img/@alt')[0].extract()
    24             img_url = img.xpath('./a/img/@src')[0].extract()
    25             item['img_name'] = img_name
    26             item['img_url'] = img_url
    27 
    28             # 将获取的数据交给pipelines
    29             yield item
    30 
    31         
    xiaohua.py
  • 相关阅读:
    linux常用命令
    mysql 开发基础系列20 事务控制和锁定语句(上)
    sql server 性能调优之 资源等待 CXPACKET
    mysql 开发基础系列19 触发器
    mysql 开发基础系列18 存储过程和函数(下)
    mysql 开发基础系列17 存储过程和函数(上)
    sql server 性能调优之 资源等待PAGEIOLATCH
    mysql 开发基础系列16 视图
    mysql 开发基础系列15 索引的设计和使用
    sql server 性能调优之 当前用户请求分析 (1)
  • 原文地址:https://www.cnblogs.com/jiangzijiang/p/8480896.html
Copyright © 2011-2022 走看看