精华内容
下载资源
问答
  • 一周爬虫集训任务一:学习get与post请求+正则表达式1 任务2 get与post请求2.1 get与post的区别2.2 requests库和urllib库的区别2.3 断网后发出申请的结果2.4 申请返回的状态码2.5 什么是请求头,如何添加请求头3 正则...

    1 任务

    【任务一】(3天)
    1.1 学习get与post请求
    学习get与post请求,尝试使用requests或者是urllib用get方法向https://www.baidu.com/发出一个请求,并将其返回结果输出。
    如果是断开了网络,再发出申请,结果又是什么。了解申请返回的状态码。
    了解什么是请求头,如何添加请求头。
    1.2 正则表达式
    学习什么是正则表达式并尝试一些正则表达式并进行匹配。
    然后结合requests、re两者的内容爬取https://movie.douban.com/top250 里的内容
    要求抓取名次、影片名称、年份、导演等字段。
    参考资料: https://desmonday.github.io/2019/03/02/python爬虫学习-day2正则表达式/
    

    2 get与post请求

    2.1 get与post的区别

    1. HTTP和HTTPS分别是什么?
      HTTP协议(超文本传输协议): 是一种发布和接收HTML页面的方法,其设计目的是保证客户机与服务器之间的通信,HTTP 的工作方式是客户机与服务器之间的请求-应答协议。

      HTTPS: 简单讲就是HTTP的安全版,在HTTP下加入SSL层。

      举例:客户端(浏览器)向服务器提交** HTTP 请求**;服务器向客户端返回响应。响应包含关于请求的状态信息以及可能被请求的内容。

    2. 两种 HTTP 请求方法:get 和 post
      在客户机和服务器之间进行请求-响应时,两种最常被用到的方法是:getpost

      本质: get和post本质上都是TCP链接,并无差别,只是由于HTTP的规定和浏览器/服务器的限制,导致他们在应用过程中体现出一些不同。

      get - 用于从服务器上获取数据;get产生一个TCP数据包;对于get方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);

      post - 用于向服务器传送数据;post产生两个TCP数据包;对于post,浏览器先发送header,服务器响应100(continue),然后再发送data,服务器响应200(返回数据)。

    2.2 requests库和urllib库的区别

    在使用python爬虫时,需要模拟发起网络请求,主要用到的库有requests库和python内置的urllib库,一般建议使用requests,它是对urllib的再次封装,它们使用的主要区别
    requests可以直接构建常用的get和post请求并发起,urllib一般要先构建get或者post请求,然后再发起请求。

    然后分别用requests库和urllib库向百度发送一个请求,并将其返回结果输出。

    1. requests发送请求
    ## requests发送请求
    import requests
    url = "https://www.baidu.com"
    ##发送get请求
    r = requests.get(url)
    ##改变编码方式
    r.encoding = 'utf-8'
    ##获取响应内容
    print(r.text)

    结果如下:

    <!DOCTYPE html>
    <!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus=autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=百度一下 class="bg s_btn" autofocus></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>新闻</a> <a href=https://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>地图</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>视频</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>贴吧</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&amp;tpl=mn&amp;u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>登录</a> </noscript> <script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">登录</a>');
                    </script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">更多产品</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>关于百度</a> <a hr
    1. urllib发送请求
    ## urllib发送请求
    import urllib.request
    url = "https://www.baidu.com"
    html = urllib.request.urlopen(url).read()
    data = html.decode("utf-8")
    print(data)

    结果如下:

    <html>
    <head>
     <script>
      location.replace(location.href.replace("https://","http://"));
     </script>
    </head>
    <body>
     <noscript><meta http-equiv="refresh" content="0;url=http://www.baidu.com/"></noscript>
    </body>
    </html>

    2.3 断网后发出申请的结果

    1. requests发送请求
      在这里插入图片描述
    2. urllib发送请求
      在这里插入图片描述

    2.4 申请返回的状态码

    1. Python中获取网页状态码的两个方法
    • requests中可以用.status_code函数获取状态码。
    import requests
    code=requests.get("https://www.baidu.com").status_code
    print(code)

    结果为:200

    • urllib中可以用.code函数获取状态码。
    import urllib.request
    status=urllib.request.urlopen("https://www.baidu.com").code
    print(status)

    结果为:200

    1. 状态码介绍
      HTTP状态码的英文为HTTP Status Code。状态代码由三位数字组成,第一个数字定义了响应的类别,且有五种可能取值。
        1xx:指示信息–表示请求已接收,继续处理。
        2xx:成功–表示请求已被成功接收、理解、接受。
        3xx:重定向–要完成请求必须进行更进一步的操作。
        4xx:客户端错误–请求有语法错误或请求无法实现。
        5xx:服务器端错误–服务器未能实现合法的请求。

      常见状态代码、状态描述的说明如下。
        200 OK:客户端请求成功。
        400 Bad Request:客户端请求有语法错误,不能被服务器所理解。
        401 Unauthorized:请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用。
        403 Forbidden:服务器收到请求,但是拒绝提供服务。
        404 Not Found:请求资源不存在,举个例子:输入了错误的URL。
        500 Internal Server Error:服务器发生不可预期的错误。
        503 Server Unavailable:服务器当前不能处理客户端的请求,一段时间后可能恢复正常。
        
      状态码详细列表可见参考4状态码列表

    2.5 什么是请求头,如何添加请求头

    一个HTTP请求报文由请求行(request line)、请求头(header)、空行和请求数据4个部分组成。

    1. 请求头定义: 通俗来讲,就是能够告诉被请求的服务器需要传送什么样的格式的信息

      HTTP客户程序(例如浏览器),向服务器发送请求的时候必须指明请求类型(一般是GET或者POST)。如有必要,客户程序还可以选择发送其他的请求头。大多数请求头并不是必需的,但Content-Length除外。对于POST请求来说Content-Length必须出现。

      常用请求头可见:参考5常用的HTTP请求头与响应头

    2. 添加请求头
      请求头部由关键字/值对组成,每行一对,关键字和值用英文冒号“:”分隔。请求头部通知服务器有关于客户端请求的信息。
      (1)在requests中添加请求头:

    ##1.在requests中添加请求头:
    import requests
    url = "http://baidu.com"
    ## 请求头信息
    header = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*,q=0.8',
    'Accept-Encoding': 'gzip, deflate, br',
    'Accept-Language': 'zh-CN,zh;q=0.9',
    'Connection': 'keep-alive' }
    r = requests.post(url, headers=header)
    head = r.headers #响应头
    print(head)
    print('-'*80)
    print(r.request.headers) #请求头

    结果为:

    {'Date': 'Sat, 06 Apr 2019 14:18:06 GMT', 'Server': 'Apache', 'Last-Modified': 'Tue, 12 Jan 2010 13:48:00 GMT', 'ETag': '"51-47cf7e6ee8400"', 'Accept-Ranges': 'bytes', 'Content-Length': '81', 'Cache-Control': 'max-age=86400', 'Expires': 'Sun, 07 Apr 2019 14:18:06 GMT', 'Connection': 'Keep-Alive', 'Content-Type': 'text/html'}
    --------------------------------------------------------------------------------
    {'User-Agent': 'python-requests/2.19.1', 'Accept-Encoding': 'gzip, deflate, br', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*,q=0.8', 'Connection': 'keep-alive', 'Accept-Language': 'zh-CN,zh;q=0.9', 'Cont

    (2)在urllib中添加请求头:

    ##2.在urllib中添加请求头:
    import urllib.request
    url = "https://www.baidu.com"
    header = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*,q=0.8',
    'Accept-Encoding': 'gzip, deflate, br',
    'Accept-Language': 'zh-CN,zh;q=0.9',
    'Connection': 'keep-alive' }
    request = urllib.request.Request(url=url,headers=header)
    resp =urllib.request.urlopen(request)
    print(resp.headers)

    结果为:

    Accept-Ranges: bytes
    Cache-Control: no-cache
    Content-Length: 227
    Content-Type: text/html
    Date: Sat, 06 Apr 2019 14:28:00 GMT
    Etag: "5c9c7bd5-e3"
    Last-Modified: Thu, 28 Mar 2019 07:46:29 GMT
    P3p: CP=" OTI DSP COR IVA OUR IND COM "
    Pragma: no-cache
    Server: BWS/1.1
    Set-Cookie: BD_NOT_HTTPS=1; path=/; Max-Age=300
    Set-Cookie: BIDUPSID=03DAF39DA0960819ADF4D9075702C9C0; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
    Set-Cookie: PSTM=1554560880; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
    Strict-Transport-Security: max-age=0
    X-Ua-Compatible: IE=Edge,chrome=1
    Connection: close

    3 正则表达式

    3.1 什么是正则表达式

    1. 正则表达式定义: 正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。
      python正则表达式: re模块使python语言拥有全部的正则表达式功能。

    2. 为什么使用正则表达式?
      通过使用正则表达式,可以:
      (1)测试字符串内的模式。例如,可以测试输入字符串,以查看字符串内是否出现电话号码模式或信用卡号码模式。这称为数据验证。
      (2)替换文本。可以使用正则表达式来识别文档中的特定文本,完全删除该文本或者用其他文本替换它。
      (3)基于模式匹配从字符串中提取子字符串。可以查找文档内或输入域内特定的文本。

    3. 使用步骤:
      (1)使用 compile()函数将正则表达式的字符串编译成一个 pattern 对象
      (2)通过 pattern 对象的一些方法对文本进行匹配,匹配结果是一个 match对象
      (3)用 match 对象的方法,对结果进行操作

    4. 常用方法:
      (1)re.match: 从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。
      函数语法:re.match(pattern, string, flags=0)
      函数参数说明:pattern:匹配的正则表达;string:要匹配的字符串;flags:标志位,用于控制正则表达式的匹配方式

      我们可以使用group(num)groups() 匹配对象函数来获取匹配表达式

      匹配对象方法描述
      group(num=0)匹配的整个表达式的字符串,group() 可以一次输入多个组号,在这种情况下它将返回一个包含那些组所对应值的元组。
      groups()返回一个包含所有小组字符串的元组,从 1 到 所含的小组号。

      实例代码:

      ##正则化
      import re
      print(re.match('www', 'www.runoob.com').span())  # 在起始位置匹配
      print(re.match('com', 'www.runoob.com'))         # 不在起始位置匹配

      结果为:

      (0, 3)
      None

      (2)re.search:任何位置开始查找,返回第一个成功的匹配。匹配成功re.search方法返回一个匹配的对象,否则返回None。
      函数语法:re.search(pattern, string, flags=0)
      函数参数说明:pattern:匹配的正则表达;string:要匹配的字符串;flags:标志位,用于控制正则表达式的匹配方式

      实例代码:

      print(re.search('www', 'www.runoob.com').span())  # 在起始位置匹配
      print(re.search('com', 'www.runoob.com').span())  # 不在起始位置匹配

      结果为:

      (0, 3)
      (11, 14)

      re.match与re.search的区别: re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配 整个字符串 ,直到找到一个匹配。

      (3)re.sub: Python 的 re 模块提供了re.sub用于替换字符串中的匹配项
      函数语法:re.sub(pattern, repl, string, count=0, flags=0)
      函数参数说明:pattern:正则中的模式字符串;repl:替换的字符串,也可为一个函数;string:要被查找替换的原始字符串;count:模式匹配后替换的最大次数,默认 0 表示替换所有的匹配。

      实例代码:

       ##re.sub用于替换字符串中的匹配项。
      import re
       phone = "2004-959-559 # 这是一个国外电话号码"
      
      # 删除字符串中的 Python注释
       num = re.sub(r'#.*$', "", phone)
       print("电话号码是: ", num)
      
      # 删除非数字(-)的字符串
      num = re.sub(r'\D', "", phone)
      print("电话号码是 : ", num)

      结果为:

       电话号码是:  2004-959-559 
       电话号码是 :  2004959559

      (4)re.compile 函数: compile 函数用于编译正则表达式,生成一个正则表达式( Pattern )对象,供 match() 和 search() 这两个函数使用。
      函数语法:re.compile(pattern[, flags])
      函数参数说明:pattern: 一个字符串形式的正则表达式;flags:可选,表示匹配模式,比如忽略大小写,多行模式等。

      (5)re.findall函数: 在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。
      注意: match 和 search 是匹配一次, findall 匹配所有
      函数语法:findall(string[, pos[, endpos]])
      函数参数说明:string:待匹配的字符串;pos:可选参数,指定字符串的起始位置,默认为 0;endpos:可选参数,指定字符串的结束位置,默认为字符串的长度。

      实例代码:

      ## findall 匹配所有
       pattern = re.compile(r'\d+')  # 查找数字
       result1 = pattern.findall('runoob 123 google 456')
       result2 = pattern.findall('run88oob123google456', 0, 10)
       print(result1)
       print(result2)

      结果为:

      ['123', '456']
      ['88', '12']

      (6)re.finditer函数: 和 findall 类似,在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回。
      函数语法:re.finditer(pattern, string, flags=0)
      函数参数说明:pattern:匹配的正则表达式;string:要匹配的字符串;flags:标志位,用于控制正则表达式的匹配方式。

      实例代码:

      import re
      
      it = re.finditer(r"\d+","12a32bc43jf3") 
      for match in it: 
      print (match.group() )

      输出结果:

      12 
      32 
      43 
      3

      (7)re.split函数: split 方法按照能够匹配的子串将字符串分割后返回列表
      函数语法:re.split(pattern, string[, maxsplit=0, flags=0])
      函数参数说明:pattern:匹配的正则表达式;string:要匹配的字符串;maxsplit:分隔次数,maxsplit=1 分隔一次,默认为 0,不限制次数;flags:标志位,用于控制正则表达式的匹配方式。

      代码实例:

      ##split分割
      a = re.split('\W+', 'runoob, runoob, runoob.')##\W 匹配非字母数字及下划线
      print(a)

      输出结果:

      ['runoob', 'runoob', 'runoob', '']
    5. 正则表达式模式
      模式字符串使用特殊的语法来表示一个正则表达式:
      (1)字母和数字表示他们自身。一个正则表达式模式中的字母和数字匹配同样的字符串。
      (2)多数字母和数字前加一个反斜杠时会拥有不同的含义。
      (3)标点符号只有被转义时才匹配自身,否则它们表示特殊的含义。
      (4)反斜杠本身需要使用反斜杠转义。
      (5)由于正则表达式通常都包含反斜杠,所以你最好使用原始字符串来表示它们。模式元素(如 r’\t’,等价于 ‘\t’)匹配相应的特殊字符。
      下表列出了正则表达式模式语法中的特殊元素。如果你使用模式的同时提供了可选的标志参数,某些模式元素的含义会改变。

      模式描述
      ^匹配字符串的开头
      $匹配字符串的末尾
      .匹配任意字符,除了换行符,当re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符
      […]用来表示一组字符,单独列出:[amk] 匹配 ‘a’,‘m’或’k’
      [^…]不在[]中的字符:[^abc] 匹配除了a,b,c之外的字符
      re*匹配0个或多个的表达式
      re+匹配1个或多个的表达式
      re?匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式
      re{ n}精确匹配 n 个前面表达式。例如, o{2} 不能匹配 “Bob” 中的 “o”,但是能匹配 “food” 中的两个 o。
      re{ n,}匹配 n 个前面表达式。例如, o{2,} 不能匹配"Bob"中的"o",但能匹配 "foooood"中的所有 o。“o{1,}” 等价于 “o+”。“o{0,}” 则等价于 “o*”。
      re{ n, m}匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式
      (re)匹配括号内的表达式,也表示一个组
      (?imx)正则表达式包含三种可选标志:i, m, 或 x 。只影响括号中的区域
      (?-imx)正则表达式关闭 i, m, 或 x 可选标志。只影响括号中的区域
      (?: re)类似 (…), 但是不表示一个组
      (?imx: re)在括号中使用i, m, 或 x 可选标志
      (?-imx: re)在括号中不使用i, m, 或 x 可选标志
      (?#…)注释.
      (?= re)前向肯定界定符
      (?! re)前向否定界定符
      (?> re)匹配的独立模式,省去回溯
      \w匹配字母数字及下划线
      \W匹配非字母数字及下划线
      \s匹配任意空白字符,等价于 [\t\n\r\f]
      \S匹配任意非空字符
      \d匹配任意数字,等价于 [0-9]
      \D匹配任意非数字
      \A匹配字符串开始
      \Z匹配字符串结束,如果是存在换行,只匹配到换行前的结束字符串
      \z匹配字符串结束
      \G匹配最后匹配完成的位置
      \b匹配一个单词边界,也就是指单词和空格间的位置。例如, ‘er\b’ 可以匹配"never" 中的 ‘er’,但不能匹配 “verb” 中的 ‘er’。
      \B匹配非单词边界。‘er\B’ 能匹配 “verb” 中的 ‘er’,但不能匹配 “never” 中的 ‘er’。
      \n, \t, 等.匹配一个换行符。匹配一个制表符。等
      \1…\9匹配第n个分组的内容
      \10匹配第n个分组的内容,如果它经匹配。否则指的是八进制字符码的表达式
    6. 贪婪与非贪婪模式
      (1)贪婪模式:在整个表达式匹配成功的前提下,尽可能多的匹配
      (2)非贪婪模式:在整个表达式匹配成功的前提下,尽可能少的匹配(多加个?
      (3)python里面数量词默认是贪婪模式

      代码实例:

      ##贪婪模式
      import re
      s = "abbbbbbc"
      pattern1 = re.compile("ab+")#贪婪
      pattern2 = re.compile("ab*?")#非贪婪
      m1 = pattern1.match(s)
      m2 = pattern2.match(s)
      s1 = m1.group()
      s2 = m2.group()
      print(s1) #贪婪模式,abbbbbb
      print(s2) #非贪婪模式,a,尽可能少匹配b所以没有b

      结果为:

      abbbbbb
      a
    7. 匹配中文
      (1)中文是Unicode编码(utf-8也是Unicode编码),范围:主要在[u4e00-u9fa5]
      (2)中文全角逗号一类的不在[u4e00-u9fa5]范围内

    3.2 爬虫实战

    实战要求:

    1. 结合requests、re两者的内容爬取豆瓣电影Top 250里的内容
    2. 要求抓取名次、影片名称、国家、导演等字段。

    思路分析:

    1. 由于豆瓣电影网页每一页默认只有25部电影信息,所以每翻一页,网页的URL就会变化。首页的URL为:https://movie.douban.com/top250?start=0&filter=,第二页的URL为:https://movie.douban.com/top250?start=25&filter=,第三页的URL为:https://movie.douban.com/top250?start=50&filter=,以此类推,所以可以使用一个while循环来遍历网页:
    while page <= 225:
     url = "https://movie.douban.com/top250?start=%s&filter=" % page
    1. 分析网页信息,鼠标右键点击网页选择查看网页源代码即可查看页面信息。

    2. 首先找到我们需要爬取的字段:名次,影片名称,国家,导演。
      名次:
      在这里插入图片描述
      影片名称:
      在这里插入图片描述
      国家和导演在一个栏目下:
      在这里插入图片描述
      相应的正则表达式为:

      名次:'<em class="">(.*?)</em>
      名称:'<span class="title">(.*?)</span>
      导演:'.*?<p class="">.*?导演:(.*?)&nbsp;'
      国家:'<br>.*?/&nbsp;(.*?)&nbsp;/.*?</p>'

    3. 最后在代码中加上请求头防止爬虫被阻止

    完整代码如下:

    import re, requests
    
    ##获取网站响应内容
    for i in range(10):# 一共有10页数据
        #请求头
        headers = {
            'User-Agent': "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36"
        }
    
        url = "https://movie.douban.com/top250?start=" + str(i * 25) + "&filter="
        movie_Num = movie_name = movie_count = movie_director = []  # 初始化数据,保证名称循环都是空的
        req = requests.get(url, headers=headers)
        html_reponse = req.content.decode("utf-8")  # 网页信息
    
        ## 从响应内容里正则化提取所需内容
        movie_Num = re.findall('<em class="">(.*?)</em>', html_reponse)  # 提取名次
        #print(movie_Num)
        movie_name = re.findall('<span class="title">([\u4e00-\u9fa5]+)</span>', html_reponse)  # 提取影片名称
        #print(movie_title)
        movie_director = re.findall('导演: (.*?)[&nbsp;|<br>]', html_reponse)  # 提取导演
        #print(movie_directors)
        movie_count = re.findall('&nbsp;/&nbsp;(.*?)&nbsp;/&nbsp;', html_reponse)  # 提取国家名
        #print(movie_countries)
        #每一页有25个电影
        for j in range(25):
            #电影信息拼接
            try:         #处理异常
                movie_info = "排名:" + movie_Num[j] + " 电影名:" + movie_name[j] + "导演:" + movie_director[j] + "国家:" + movie_count[j]
                print(movie_info)
            except:
                print('越界')
                continue

    结果为:
    在这里插入图片描述
    过程中遇到的问题:

    1. 过程中需要用到中文匹配的知识,使用[\u4e00-\u9fa5]代表中文字符
    2. 在抓取个别电影信息时会导致抓取错误,进而导致列表索引越界,需要加个异常处理,没有抓到的数据就将其释放。
    3. 还是需要优化正则条件,保证所有的信息能够匹配。

    4 参考

    1. HTTP 方法:GET 对比 POST
    2. requests与urllib库的区别
    3. Python中获取网页状态码的两个方法
    4. 状态码列表
    5. 常用的HTTP请求头与响应头
    6. http请求头和响应头
    7. 参考7
    8. 参考8
    9. Python 正则表达式
    10. 参考10
    展开全文
  • 本节是基础篇第二节,主要介绍jmeter的参数化,检查点,关联,正则表达式,其中参数化、检查点在实际压测中使用最多。 一、检查点 & 参数化 1、检查点 即是添加断言,这个在上篇文章中已提到,具体如下图...

    本节是基础篇第二节,主要介绍jmeter的参数化,检查点,关联,正则表达式,其中参数化、检查点在实际压测中使用最多。

     

    一、检查点 & 参数化

     

    1、检查点
          即是添加断言,这个在上篇文章中已提到,具体如下图

    2、参数化
     
    a、添加CSV Data Set Config配置组件,实现参数化。这里以注册为接口为例对注册用户名进行参数化。

     

    b、准备参数化文件,这里命名userName_data.txt,与脚本存在同级目录(下面用的),内容如下

     

    c、配置CSV Data Set Config,读取对应的数据文件

     

    d、将请求接口中的参数替换成变量

     

     

    强调下:至此参数化算是完成了,经常有人问我有必要参数化?其实参数化在性能压测过程中是必须的,抛开程序过程中可能存在缓存,数据库本身也会有缓存,如果使用一条数据反复跑的话,实际的压测结果就不符合业务场景。

    二、关联 & 正则表达式
          
    这里以接口https://api.midukanshu.com/logstash/userbehavior/report为例,请求方式POST,返回结果{"code":0,"message":"成功","currentTime":1568203926,"data":{}},通过正则表达式提取"currentTime"的值。


    1、调试post接口https://api.midukanshu.com/logstash/userbehavior/report(如下如图)

    2、请求一次,保证接口能正常返回,如下:

     

    3、关联 & 正则表达式

         后置处理器是JMeter的关联组件,可以从服务器响应数据中查找到需要的数据。常用的是正则表达式提取器(Regular Expression Extractor)

     

    a、创建正则表达式提取器,Regular Expression Extractor(如下图)

     

    b、提取接口返回参数"currentTime"

     

    c、添加后置处理组件中的BeanShell PostProcessor,并输出正则表达式提取的值到jmeter的log中

     

    d、执行脚本,查看jmeter的log是否有提取的字符

    到此本节基本算是完成,集合点在这里没有着重介绍,这里不做重点,工作中这块也用到较少,下面主要介绍正则表达的一些解释:

     

    (1)引用名称:下一个请求要引用的参数名称,如填写你nowtime,则可用${nowtime}引用它。

    (2)正则表达式

        ()括起来的部分就是要提取的。

        .匹配任何字符串。

        +:一次或多次。

        ?:不要太贪婪,在找到第一个匹配项后停止。

    注:(.+?)[.\n]+可以匹配换行符在内的所有字符。

    (3)模板:用$引用起来,如果在正则表达式中有多个正则表达式(多个括号括起来的东东),则可以是$2引用起来,如果在正则表达式中有多个正则表达式(多个括号括起来的东东),则可以是$2,$3等等,表示解析到的第几个值给title。如:$1$表示解析到的第1个值

    (4)匹配数字:0代表随机取值,1代表全部取值,通常情况下填0,如果在LR中,取出的值是一个数组,还得处理一下,LR11版本用一个随机的函数就可以不用写大段的代码来处理数组。

    (5)缺省值:如果参数没有取得到值,那默认给一个值让它取。

     

     

    作为一个对性能测试有情怀的人,希望过往的经验能够对新来人有一定的帮助,公众号"性能测试践行"中原创作者日常工作中典型案例和自己每时每刻对性能新的认知,希望喜欢!

     

    展开全文
  • 正则表达式

    千次阅读 2020-10-23 08:49:49
    但是在我们能完全理解这些工具提供的 所有功能之前,我们不得不先看看,经常与这些工具的高级使用相关联的一门技术——正则表达式。 我们已经浏览了许多由命令行提供的功能和工具,我们遇到了一些真正神秘的 shell ...

    接下来的几章中,我们将会看一下一些用来操作文本的工具。正如我们所见到的,在类 Unix 的 操作系统中,比如 Linux 中,文本数据起着举足轻重的作用。但是在我们能完全理解这些工具提供的 所有功能之前,我们不得不先看看,经常与这些工具的高级使用相关联的一门技术——正则表达式。

    我们已经浏览了许多由命令行提供的功能和工具,我们遇到了一些真正神秘的 shell 功能和命令, 比如 shell 展开和引用,键盘快捷键,和命令历史,更不用说 vi 编辑器了。正则表达式延续了 这种“传统”,而且有可能(备受争议地)是其中最神秘的功能。这并不是说花费时间来学习它们 是不值得的,而是恰恰相反。虽然它们的全部价值可能不能立即显现,但是较强理解这些功能 使我们能够表演令人惊奇的技艺。什么是正则表达式?

    简而言之,正则表达式是一种符号表示法,被用来识别文本模式。在某种程度上,它们与匹配 文件和路径名的 shell 通配符比较相似,但其规模更庞大。许多命令行工具和大多数的编程语言 都支持正则表达式,以此来帮助解决文本操作问题。然而,并不是所有的正则表达式都是一样的, 这就进一步混淆了事情;不同工具以及不同语言之间的正则表达式都略有差异。我们将会限定 POSIX 标准中描述的正则表达式(其包括了大多数的命令行工具),供我们讨论, 与许多编程语言(最著名的 Perl 语言)相反,它们使用了更多和更丰富的符号集。

    grep

    我们将使用的主要程序是我们的老朋友,grep 程序,它会用到正则表达式。实际上,“grep”这个名字 来自于短语“global regular expression print”,所以我们能看出 grep 程序和正则表达式有关联。 本质上,grep 程序会在文本文件中查找一个指定的正则表达式,并把匹配行输出到标准输出。

    到目前为止,我们已经使用 grep 程序查找了固定的字符串,就像这样:

    [me@linuxbox ~]$ ls /usr/bin | grep zip
    

    这个命令会列出,位于目录 /usr/bin 中,文件名中包含子字符串“zip”的所有文件。

    这个 grep 程序以这样的方式来接受选项和参数:

    grep [options] regex [file...]
    

    这里的 regx 是指一个正则表达式。

    这是一个常用的 grep 选项列表:

    表20-1: grep 选项

    选项描述
    -i忽略大小写。不会区分大小写字符。也可用--ignore-case 来指定。
    -v不匹配。通常,grep 程序会打印包含匹配项的文本行。这个选项导致 grep 程序 只会不包含匹配项的文本行。也可用--invert-match 来指定。
    -c打印匹配的数量(或者是不匹配的数目,若指定了-v 选项),而不是文本行本身。 也可用--count 选项来指定。
    -l打印包含匹配项的文件名,而不是文本行本身,也可用--files-with-matches 选项来指定。
    -L相似于-l 选项,但是只是打印不包含匹配项的文件名。也可用--files-without-match 来指定。
    -n在每个匹配行之前打印出其位于文件中的相应行号。也可用--line-number 选项来指定。
    -h应用于多文件搜索,不输出文件名。也可用--no-filename 选项来指定。

    为了更好的探究 grep 程序,让我们创建一些文本文件来搜寻:

    [me@linuxbox ~]$ ls /bin > dirlist-bin.txt
    [me@linuxbox ~]$ ls /usr/bin > dirlist-usr-bin.txt
    [me@linuxbox ~]$ ls /sbin > dirlist-sbin.txt
    [me@linuxbox ~]$ ls /usr/sbin > dirlist-usr-sbin.txt
    [me@linuxbox ~]$ ls dirlist*.txt
    dirlist-bin.txt     dirlist-sbin.txt    dirlist-usr-sbin.txt
    dirlist-usr-bin.txt
    

    我们能够对我们的文件列表执行简单的搜索,像这样:

    [me@linuxbox ~]$ grep bzip dirlist*.txt
    dirlist-bin.txt:bzip2
    dirlist-bin.txt:bzip2recover
    

    在这个例子里,grep 程序在所有列出的文件中搜索字符串 bzip,然后找到两个匹配项,其都在 文件 dirlist-bin.txt 中。如果我们只是对包含匹配项的文件列表,而不是对匹配项本身感兴趣 的话,我们可以指定-l 选项:

    [me@linuxbox ~]$ grep -l bzip dirlist*.txt
    dirlist-bin.txt
    

    相反地,如果我们只想查看不包含匹配项的文件列表,我们可以这样操作:

    [me@linuxbox ~]$ grep -L bzip dirlist*.txt
    dirlist-sbin.txt
    dirlist-usr-bin.txt
    dirlist-usr-sbin.txt
    

    元字符和文本

    它可能看起来不明显,但是我们的 grep 程序一直使用了正则表达式,虽然是非常简单的例子。 这个正则表达式“bzip”意味着,匹配项所在行至少包含4个字符,并且按照字符 “b”, “z”, “i”, 和 “p”的顺序 出现在匹配行的某处,字符之间没有其它的字符。字符串“bzip”中的所有字符都是原义字符,因为 它们匹配本身。除了原义字符之外,正则表达式也可能包含元字符,其被用来指定更复杂的匹配项。 正则表达式元字符由以下字符组成:

    ^ $ . [ ] { } - ? * + ( ) | \
    

    然后其它所有字符都被认为是原义字符,虽然在个别情况下,反斜杠会被用来创建元序列, 也允许元字符被转义为原义字符,而不是被解释为元字符。

    注意:正如我们所见到的,当 shell 执行展开的时候,许多正则表达式元字符,也是对 shell 有特殊 含义的字符。当我们在命令行中传递包含元字符的正则表达式的时候,把元字符用引号引起来至关重要, 这样可以阻止 shell 试图展开它们。

    任何字符

    我们将要查看的第一个元字符是圆点字符,其被用来匹配任意字符。如果我们在正则表达式中包含它, 它将会匹配在此位置的任意一个字符。这里有个例子:

    [me@linuxbox ~]$ grep -h '.zip' dirlist*.txt
    bunzip2
    bzip2
    bzip2recover
    gunzip
    gzip
    funzip
    gpg-zip
    preunzip
    prezip
    prezip-bin
    unzip
    unzipsfx
    

    我们在文件中查找包含正则表达式“.zip”的文本行。对于搜索结果,有几点需要注意一下。 注意没有找到这个 zip 程序。这是因为在我们的正则表达式中包含的圆点字符把所要求的匹配项的长度 增加到四个字符,并且字符串“zip”只包含三个字符,所以这个 zip 程序不匹配。另外,如果我们的文件列表 中有一些文件的扩展名是.zip,则它们也会成为匹配项,因为文件扩展名中的圆点符号也会被看作是 “任意字符”。

    锚点

    在正则表达式中,插入符号和美元符号被看作是锚点。这意味着正则表达式 只有在文本行的开头或末尾被找到时,才算发生一次匹配。

    [me@linuxbox ~]$ grep -h '^zip' dirlist*.txt
    zip
    zipcloak
    zipgrep
    zipinfo
    zipnote
    zipsplit
    [me@linuxbox ~]$ grep -h 'zip$' dirlist*.txt
    gunzip
    gzip
    funzip
    gpg-zip
    preunzip
    prezip
    unzip
    zip
    [me@linuxbox ~]$ grep -h '^zip$' dirlist*.txt
    zip
    

    这里我们分别在文件列表中搜索行首,行尾以及行首和行尾同时包含字符串“zip”(例如,zip 独占一行)的匹配行。 注意正则表达式‘^$’(行首和行尾之间没有字符)会匹配空行。

    字谜助手

    到目前为止,甚至凭借我们有限的正则表达式知识,我们已经能做些有意义的事情了。

    我妻子喜欢玩字谜游戏,有时候她会因为一个特殊的问题,而向我求助。类似这样的问题,“一个 有五个字母的单词,它的第三个字母是‘j’,最后一个字母是‘r’,是哪个单词?”这类问题会 让我动脑筋想想。

    你知道你的 Linux 系统中带有一本英文字典吗?千真万确。看一下 /usr/share/dict 目录,你就能找到一本, 或几本。存储在此目录下的字典文件,其内容仅仅是一个长长的单词列表,每行一个单词,按照字母顺序排列。在我的 系统中,这个文件仅包含98,000个单词。为了找到可能的上述字谜的答案,我们可以这样做:

    [me@linuxbox ~]$ grep -i '^..j.r$' /usr/share/dict/words
    Major
    major
    

    使用这个正则表达式,我们能在我们的字典文件中查找到包含五个字母,且第三个字母 是“j”,最后一个字母是“r”的所有单词。

    中括号表达式和字符类

    除了能够在正则表达式中的给定位置匹配任意字符之外,通过使用中括号表达式, 我们也能够从一个指定的字符集合中匹配一个单个的字符。通过中括号表达式,我们能够指定 一个字符集合(包含在不加中括号的情况下会被解释为元字符的字符)来被匹配。在这个例子里,使用了一个两个字符的集合:

    [me@linuxbox ~]$ grep -h '[bg]zip' dirlist*.txt
    bzip2
    bzip2recover
    gzip
    

    我们匹配包含字符串“bzip”或者“gzip”的任意行。

    一个字符集合可能包含任意多个字符,并且元字符被放置到中括号里面后会失去了它们的特殊含义。 然而,在两种情况下,会在中括号表达式中使用元字符,并且有着不同的含义。第一个元字符 是插入字符,其被用来表示否定;第二个是连字符字符,其被用来表示一个字符区域。

    否定

    如果在正则表示式中的第一个字符是一个插入字符,则剩余的字符被看作是不会在给定的字符位置出现的 字符集合。通过修改之前的例子,我们试验一下:

    [me@linuxbox ~]$ grep -h '[^bg]zip' dirlist*.txt
    bunzip2
    gunzip
    funzip
    gpg-zip
    preunzip
    prezip
    prezip-bin
    unzip
    unzipsfx
    

    通过激活否定操作,我们得到一个文件列表,它们的文件名都包含字符串“zip”,并且“zip”的前一个字符 是除了“b”和“g”之外的任意字符。注意文件 zip 没有被发现。一个否定的字符集仍然在给定位置要求一个字符, 但是这个字符必须不是否定字符集的成员。

    这个插入字符如果是中括号表达式中的第一个字符的时候,才会唤醒否定功能;否则,它会失去 它的特殊含义,变成字符集中的一个普通字符。

    传统的字符区域

    如果我们想要构建一个正则表达式,它可以在我们的列表中找到每个以大写字母开头的文件,我们 可以这样做:

    [me@linuxbox ~]$ grep -h '^[ABCDEFGHIJKLMNOPQRSTUVWXZY]' dirlist*.txt
    

    这只是一个在正则表达式中输入26个大写字母的问题。但是输入所有字母非常令人烦恼,所以有另外一种方式:

    [me@linuxbox ~]$ grep -h '^[A-Z]' dirlist*.txt
    MAKEDEV
    ControlPanel
    GET
    HEAD
    POST
    X
    X11
    Xorg
    MAKEFLOPPIES
    NetworkManager
    NetworkManagerDispatcher
    

    通过使用一个三字符区域,我们能够缩写26个字母。任意字符的区域都能按照这种方式表达,包括多个区域, 比如下面这个表达式就匹配了所有以字母和数字开头的文件名:

    [me@linuxbox ~]$ grep -h '^[A-Za-z0-9]' dirlist*.txt
    

    在字符区域中,我们看到这个连字符被特殊对待,所以我们怎样在一个正则表达式中包含一个连字符呢? 方法就是使连字符成为表达式中的第一个字符。考虑一下这两个例子:

    [me@linuxbox ~]$ grep -h '[A-Z]' dirlist*.txt
    

    这会匹配包含一个大写字母的文件名。然而:

    [me@linuxbox ~]$ grep -h '[-AZ]' dirlist*.txt
    

    上面的表达式会匹配包含一个连字符,或一个大写字母“A”,或一个大写字母“Z”的文件名。

    POSIX 字符集

    这个传统的字符区域在处理快速地指定字符集合的问题方面,是一个易于理解的和有效的方式。 不幸地是,它们不总是工作。到目前为止,虽然我们在使用 grep 程序的时候没有遇到任何问题, 但是我们可能在使用其它程序的时候会遭遇困难。

    回到第5章,我们看看通配符怎样被用来完成路径名展开操作。在那次讨论中,我们说过在 某种程度上,那个字符区域被使用的方式几乎与在正则表达式中的用法一样,但是有一个问题:

    [me@linuxbox ~]$ ls /usr/sbin/[ABCDEFGHIJKLMNOPQRSTUVWXYZ]*
    /usr/sbin/MAKEFLOPPIES
    /usr/sbin/NetworkManagerDispatcher
    /usr/sbin/NetworkManager
    

    (依赖于不同的 Linux 发行版,我们将得到不同的文件列表,有可能是一个空列表。这个例子来自于 Ubuntu) 这个命令产生了期望的结果——只有以大写字母开头的文件名,但是:

    [me@linuxbox ~]$ ls /usr/sbin/[A-Z]*
    /usr/sbin/biosdecode
    /usr/sbin/chat
    /usr/sbin/chgpasswd
    /usr/sbin/chpasswd
    /usr/sbin/chroot
    /usr/sbin/cleanup-info
    /usr/sbin/complain
    /usr/sbin/console-kit-daemon
    

    通过这个命令我们得到整个不同的结果(只显示了一部分结果列表)。为什么会是那样? 说来话长,但是这个版本比较简短:

    追溯到 Unix 刚刚开发的时候,它只知道 ASCII 字符,并且这个特性反映了事实。在 ASCII 中,前32个字符 (数字0-31)都是控制码(如 tabs,backspaces,和回车)。随后的32个字符(32-63)包含可打印的字符, 包括大多数的标点符号和数字0到9。再随后的32个字符(64-95)包含大写字符和一些更多的标点符号。 最后的31个字符(96-127)包含小写字母和更多的标点符号。基于这种安排方式,系统使用这种排序规则 的 ASCII:

    ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
    这个不同于正常的字典顺序,其像这样:

    aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ
    随着 Unix 系统的知名度在美国之外的国家传播开来,就需要支持不在 U.S.英语范围内的字符。 于是就扩展了这个 ASCII 字符表,使用了整个8位,添加了字符(数字128-255),这样就 容纳了更多的语言。

    为了支持这种能力,POSIX 标准介绍了一种叫做 locale 的概念,其可以被调整,来为某个特殊的区域, 选择所需的字符集。通过使用下面这个命令,我们能够查看到我们系统的语言设置:

    [me@linuxbox ~]$ echo $LANG
    en_US.UTF-8
    

    通过这个设置,POSIX 相容的应用程序将会使用字典排列顺序而不是 ASCII 顺序。这就解释了上述命令的行为。 当[A-Z]字符区域按照字典顺序解释的时候,包含除了小写字母“a”之外的所有字母,因此得到这样的结果。

    为了部分地解决这个问题,POSIX 标准包含了大量的字符集,其提供了有用的字符区域。 下表中描述了它们:

    表20-2: POSIX 字符集

    字符集说明
    [:alnum:]字母数字字符。在 ASCII 中,等价于:[A-Za-z0-9]
    [:word:]与[:alnum:]相同, 但增加了下划线字符。
    [:alpha:]字母字符。在 ASCII 中,等价于:[A-Za-z]
    [:blank:]包含空格和 tab 字符。
    [:cntrl:]ASCII 的控制码。包含了0到31,和127的 ASCII 字符。
    [:digit:]数字0到9
    [:graph:]可视字符。在 ASCII 中,它包含33到126的字符。
    [:lower:]小写字母。
    [:punct:]标点符号字符。在 ASCII 中,等价于:
    [:print:]可打印的字符。在[:graph:]中的所有字符,再加上空格字符。
    [:space:]空白字符,包括空格,tab,回车,换行,vertical tab, 和 form feed.在 ASCII 中, 等价于:[ \t\r\n\v\f]
    [:upper:]大写字母。
    [:xdigit:]用来表示十六进制数字的字符。在 ASCII 中,等价于:[0-9A-Fa-f]

    甚至通过字符集,仍然没有便捷的方法来表达部分区域,比如[A-M]。

    通过使用字符集,我们重做上述的例题,看到一个改进的结果:

    [me@linuxbox ~]$ ls /usr/sbin/[[:upper:]]*
    /usr/sbin/MAKEFLOPPIES
    /usr/sbin/NetworkManagerDispatcher
    /usr/sbin/NetworkManager
    

    记住,然而,这不是一个正则表达式的例子,而是 shell 正在执行路径名展开操作。我们在这里展示这个例子, 是因为 POSIX 规范的字符集适用于二者。

    恢复到传统的排列顺序

    通过改变环境变量 LANG 的值,你可以选择让你的系统使用传统的(ASCII)排列规则。如上所示,这个 LANG 变量包含了语种和字符集。这个值最初由你安装 Linux 系统时所选择的安装语言决定。

    使用 locale 命令,来查看 locale 的设置。

     [me@linuxbox ~]$ locale
    
     LANG=en_US.UTF-8
    
     LC_CTYPE="en_US.UTF-8"
    
     LC_NUMERIC="en_US.UTF-8"
    
     LC_TIME="en_US.UTF-8"
    
     LC_COLLATE="en_US.UTF-8"
    
     LC_MONETARY="en_US.UTF-8"
    
     LC_MESSAGES="en_US.UTF-8"
    
     LC_PAPER="en_US.UTF-8"
    
     LC_NAME="en_US.UTF-8"
    
     LC_ADDRESS="en_US.UTF-8"
    
     LC_TELEPHONE="en_US.UTF-8"
    
     LC_MEASUREMENT="en_US.UTF-8"
    
     LC_IDENTIFICATION="en_US.UTF-8"
    
     LC_ALL=
    

    把这个 LANG 变量设置为 POSIX,来更改 locale,使其使用传统的 Unix 行为。

    [me@linuxbox ~]$ export LANG=POSIX
    

    注意这个改动使系统为它的字符集使用 U.S.英语(更准确地说,ASCII),所以要确认一下这 是否是你真正想要的效果。通过把这条语句添加到你的.bashrc 文件中,你可以使这个更改永久有效。

    export LANG=POSIX
    

    POSIX 基本的 Vs.扩展的正则表达式

    就在我们认为这已经非常令人困惑了,我们却发现 POSIX 把正则表达式的实现分成了两类: 基本正则表达式(BRE)和扩展的正则表达式(ERE)。既服从 POSIX 规范又实现了 BRE 的任意应用程序,都支持我们目前研究的所有正则表达式特性。我们的 grep 程序就是其中一个。

    BRE 和 ERE 之间有什么区别呢?这是关于元字符的问题。BRE 可以辨别以下元字符:

    ^ $ . [ ] *
    

    其它的所有字符被认为是文本字符。ERE 添加了以下元字符(以及与其相关的功能):

    ( ) { } ? + |
    

    然而(这也是有趣的地方),在 BRE 中,字符“(”,“)”,“{”,和 “}”用反斜杠转义后,被看作是元字符, 相反在 ERE 中,在任意元字符之前加上反斜杠会导致其被看作是一个文本字符。在随后的讨论中将会涵盖 很多奇异的特性。

    因为我们将要讨论的下一个特性是 ERE 的一部分,我们将要使用一个不同的 grep 程序。照惯例, 一直由 egrep 程序来执行这项操作,但是 GUN 版本的 grep 程序也支持扩展的正则表达式,当使用了-E 选项之后。

    在 20 世纪 80 年代,Unix 成为一款非常流行的商业操作系统,但是到了1988年,Unix 世界 一片混乱。许多计算机制造商从 Unix 的创建者 AT&T 那里得到了许可的 Unix 源码,并且 供应各种版本的操作系统。然而,在他们努力创造产品差异化的同时,每个制造商都增加了 专用的更改和扩展。这就开始限制了软件的兼容性。

    专有软件供应商一如既往,每个供应商都试图玩嬴游戏“锁定”他们的客户。这个 Unix 历史上 的黑暗时代,就是今天众所周知的 “the Balkanization”。

    然后进入 IEEE( 电气与电子工程师协会 )时代。在上世纪 80 年代中叶,IEEE 开始制定一套标准, 其将会定义 Unix 系统( 以及类 Unix 的系统 )如何执行。这些标准,正式成为 IEEE 1003, 定义了应用程序编程接口( APIs ),shell 和一些实用程序,其将会在标准的类 Unix 操作系统中找到。“POSIX” 这个名字,象征着可移植的操作系统接口(为了额外的,添加末尾的 “X” ), 是由 Richard Stallman 建议的( 是的,的确是 Richard Stallman ),后来被 IEEE 采纳。

    Alternation

    我们将要讨论的扩展表达式的第一个特性叫做 alternation(交替),其是一款允许从一系列表达式 之间选择匹配项的实用程序。就像中括号表达式允许从一系列指定的字符之间匹配单个字符那样, alternation 允许从一系列字符串或者是其它的正则表达式中选择匹配项。为了说明问题, 我们将会结合 echo 程序来使用 grep 命令。首先,让我们试一个普通的字符串匹配:

    [me@linuxbox ~]$ echo "AAA" | grep AAA
    AAA
    [me@linuxbox ~]$ echo "BBB" | grep AAA
    [me@linuxbox ~]$
    

    一个相当直截了当的例子,我们把 echo 的输出管道给 grep,然后看到输出结果。当出现 一个匹配项时,我们看到它会打印出来;当没有匹配项时,我们看到没有输出结果。

    现在我们将添加 alternation,以竖杠线元字符为标记:

    [me@linuxbox ~]$ echo "AAA" | grep -E 'AAA|BBB'
    AAA
    [me@linuxbox ~]$ echo "BBB" | grep -E 'AAA|BBB'
    BBB
    [me@linuxbox ~]$ echo "CCC" | grep -E 'AAA|BBB'
    [me@linuxbox ~]$
    

    这里我们看到正则表达式’AAA|BBB’,这意味着“匹配字符串 AAA 或者是字符串 BBB”。注意因为这是 一个扩展的特性,我们给 grep 命令(虽然我们能以 egrep 程序来代替)添加了-E 选项,并且我们 把这个正则表达式用单引号引起来,为的是阻止 shell 把竖杠线元字符解释为一个 pipe 操作符。 Alternation 并不局限于两种选择:

    [me@linuxbox ~]$ echo "AAA" | grep -E 'AAA|BBB|CCC'
    AAA
    

    为了把 alternation 和其它正则表达式元素结合起来,我们可以使用()来分离 alternation。

    [me@linuxbox ~]$ grep -Eh '^(bz|gz|zip)' dirlist*.txt
    

    这个表达式将会在我们的列表中匹配以“bz”,或“gz”,或“zip”开头的文件名。如果我们删除了圆括号, 这个表达式的意思:

    [me@linuxbox ~]$ grep -Eh '^bz|gz|zip' dirlist*.txt
    

    会变成匹配任意以“bz”开头,或包含“gz”,或包含“zip”的文件名。

    限定符

    扩展的正则表达式支持几种方法,来指定一个元素被匹配的次数。

    ? - 匹配零个或一个元素

    这个限定符意味着,实际上,“使前面的元素可有可无。”比方说我们想要查看一个电话号码的真实性, 如果它匹配下面两种格式的任意一种,我们就认为这个电话号码是真实的:

    (nnn) nnn-nnnn
    
    nnn nnn-nnnn
    

    这里的“n”是一个数字。我们可以构建一个像这样的正则表达式:

    ^\(?[0-9][0-9][0-9]\)?  [0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]$
    

    在这个表达式中,我们在圆括号之后加上一个问号,来表示它们将被匹配零次或一次。再一次,因为 通常圆括号都是元字符(在 ERE 中),所以我们在圆括号之前加上了反斜杠,使它们成为文本字符。

    让我们试一下:

    [me@linuxbox ~]$ echo "(555) 123-4567" | grep -E '^\(?[0-9][0-9][0-9]
    \)? [0-9][0-9][0-9]$'
    (555) 123-4567
    [me@linuxbox ~]$ echo "555 123-4567" | grep -E '^\(?[0-9][0-9][0-9]\)
    ? [0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]$'
    555 123-4567
    [me@linuxbox ~]$ echo "AAA 123-4567" | grep -E '^\(?[0-9][0-9][0-9]\)
    ? [0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]$'
    [me@linuxbox ~]$
    

    这里我们看到这个表达式匹配这个电话号码的两种形式,但是不匹配包含非数字字符的号码。

    * - 匹配零个或多个元素

    像 ? 元字符一样,这个 * 被用来表示一个可选的字符;然而,又与 ? 不同,匹配的字符可以出现 任意多次,不仅是一次。比方说我们想要知道是否一个字符串是一句话;也就是说,字符串开始于 一个大写字母,然后包含任意多个大写和小写的字母和空格,最后以句号收尾。为了匹配这个(非常粗略的) 语句的定义,我们能够使用一个像这样的正则表达式:

    [[:upper:]][[:upper:][:lower:] ]*.
    

    这个表达式由三个元素组成:一个包含[:upper:]字符集的中括号表达式,一个包含[:upper:]和[:lower:] 两个字符集以及一个空格的中括号表达式,和一个被反斜杠字符转义过的圆点。第二个元素末尾带有一个 *元字符,所以在开头的大写字母之后,可能会跟随着任意数目的大写和小写字母和空格,并且匹配:

    [me@linuxbox ~]$ echo "This works." | grep -E '[[:upper:]][[:upper:][[:lower:]]*.'
    This works.
    [me@linuxbox ~]$ echo "This Works." | grep -E '[[:upper:]][[:upper:][[:lower:]]*.'
    This Works.
    [me@linuxbox ~]$ echo "this does not" | grep -E '[[:upper:]][[:upper: ][[:lower:]]*.'
    [me@linuxbox ~]$
    

    这个表达式匹配前两个测试语句,但不匹配第三个,因为第三个句子缺少开头的大写字母和末尾的句号。

    + - 匹配一个或多个元素

    这个 + 元字符的作用与 * 非常相似,除了它要求前面的元素至少出现一次匹配。这个正则表达式只匹配 那些由一个或多个字母字符组构成的文本行,字母字符之间由单个空格分开:

    ^([[:alpha:]]+ ?)+$
    [me@linuxbox ~]$ echo "This that" | grep -E '^([[:alpha:]]+ ?)+$'
    This that
    [me@linuxbox ~]$ echo "a b c" | grep -E '^([[:alpha:]]+ ?)+$'
    a b c
    [me@linuxbox ~]$ echo "a b 9" | grep -E '^([[:alpha:]]+ ?)+$'
    [me@linuxbox ~]$ echo "abc  d" | grep -E '^([[:alpha:]]+ ?)+$'
    [me@linuxbox ~]$
    

    我们看到这个正则表达式不匹配“a b 9”这一行,因为它包含了一个非字母的字符;它也不匹配 “abc d” ,因为在字符“c”和“d”之间不止一个空格。

    { } - 匹配特定个数的元素

    这个 { 和 } 元字符都被用来表达要求匹配的最小和最大数目。它们可以通过四种方法来指定:

    表20-3: 指定匹配的数目

    限定符意思
    {n}匹配前面的元素,如果它确切地出现了 n 次。
    {n,m}匹配前面的元素,如果它至少出现了 n 次,但是不多于 m 次。
    {n,}匹配前面的元素,如果它出现了 n 次或多于 n 次。
    {,m}匹配前面的元素,如果它出现的次数不多于 m 次。

    回到之前处理电话号码的例子,我们能够使用这种指定重复次数的方法来简化我们最初的正则表达式:

    ^\(?[0-9][0-9][0-9]\)?  [0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]$
    

    简化为:

    ^\(?[0-9]{3}\)?  [0-9]{3}-[0-9]{4}$
    

    让我们试一下:

    [me@linuxbox ~]$ echo "(555) 123-4567" | grep -E '^\(?[0-9]{3}\)? [0-9]{3}-[0-9]{4}$'
    (555) 123-4567
    [me@linuxbox ~]$ echo "555 123-4567" | grep -E '^\(?[0-9]{3}\)? [0-9]{3}-[0-9]{4}$'
    555 123-4567
    [me@linuxbox ~]$ echo "5555 123-4567" | grep -E '^\(?[0-9]{3}\)? [0-9]{3}-[0-9]{4}$'
    [me@linuxbox ~]$
    

    我们可以看到,我们修订的表达式能成功地验证带有和不带有圆括号的数字,而拒绝那些格式 不正确的数字。

    让正则表达式工作起来

    让我们看看一些我们已经知道的命令,然后看一下它们怎样使用正则表达式。

    通过 grep 命令来验证一个电话簿

    在我们先前的例子中,我们查看过单个电话号码,并且检查了它们的格式。一个更现实的 情形是检查一个数字列表,所以我们先创建一个列表。我们将背诵一个神奇的咒语到命令行中。 它会很神奇,因为我们还没有涵盖所涉及的大部分命令,但是不要担心。我们将在后面的章节里面 讨论那些命令。这里是这个咒语:

    [me@linuxbox ~]$ for i in {1..10}; do echo "(${RANDOM:0:3}) ${RANDO
    M:0:3}-${RANDOM:0:4}" >> phonelist.txt; done
    

    这个命令会创建一个包含10个电话号码的名为 phonelist.txt 的文件。每次重复这个命令的时候, 另外10个号码会被添加到这个列表中。我们也能够更改命令开头附近的数值10,来生成或多或少的 电话号码。如果我们查看这个文件的内容,然而我们会发现一个问题:

    [me@linuxbox ~]$ cat phonelist.txt
    (232) 298-2265
    (624) 381-1078
    (540) 126-1980
    (874) 163-2885
    (286) 254-2860
    (292) 108-518
    (129) 44-1379
    (458) 273-1642
    (686) 299-8268
    (198) 307-2440
    

    一些号码是残缺不全的,但是它们很适合我们的需求,因为我们将使用 grep 命令来验证它们。

    一个有用的验证方法是扫描这个文件,查找无效的号码,并把搜索结果显示到屏幕上:

    [me@linuxbox ~]$ grep -Ev '^\([0-9]{3}\) [0-9]{3}-[0-9]{4}$'
    phonelist.txt
    (292) 108-518
    (129) 44-1379
    [me@linuxbox ~]$
    

    这里我们使用-v 选项来产生相反的匹配,因此我们将只输出不匹配指定表达式的文本行。这个 表达式自身的两端都包含定位点(锚)元字符,是为了确保这个号码的两端没有多余的字符。 这个表达式也要求圆括号出现在一个有效的号码中,不同于我们先前电话号码的实例。

    用 find 查找丑陋的文件名

    这个 find 命令支持一个基于正则表达式的测试。当在使用正则表达式方面比较 find 和 grep 命令的时候, 还有一个重要问题要牢记在心。当某一行包含的字符串匹配上了一个表达式的时候,grep 命令会打印出这一行, 然而 find 命令要求路径名精确地匹配这个正则表达式。在下面的例子里面,我们将使用带有一个正则 表达式的 find 命令,来查找每个路径名,其包含的任意字符都不是以下字符集中的一员。

    [-\_./0-9a-zA-Z]
    

    这样一种扫描会发现包含空格和其它潜在不规范字符的路径名:

    [me@linuxbox ~]$ find . -regex '.*[^-\_./0-9a-zA-Z].*'
    

    由于要精确地匹配整个路径名,所以我们在表达式的两端使用了.*,来匹配零个或多个字符。 在表达式中间,我们使用了否定的中括号表达式,其包含了我们一系列可接受的路径名字符。

    用 locate 查找文件

    这个 locate 程序支持基本的(--regexp 选项)和扩展的(--regex 选项)正则表达式。通过 locate 命令,我们能够执行许多与先前操作 dirlist 文件时相同的操作:

    [me@linuxbox ~]$ locate --regex 'bin/(bz|gz|zip)'
    /bin/bzcat
    /bin/bzcmp
    /bin/bzdiff
    /bin/bzegrep
    /bin/bzexe
    /bin/bzfgrep
    /bin/bzgrep
    /bin/bzip2
    /bin/bzip2recover
    /bin/bzless
    /bin/bzmore
    /bin/gzexe
    /bin/gzip
    /usr/bin/zip
    /usr/bin/zipcloak
    /usr/bin/zipgrep
    /usr/bin/zipinfo
    /usr/bin/zipnote
    /usr/bin/zipsplit
    

    通过使用 alternation,我们搜索包含 bin/bz,bin/gz,或/bin/zip 字符串的路径名。

    在 less 和 vim 中查找文本

    less 和 vim 两者享有相同的文本查找方法。按下/按键,然后输入正则表达式,来执行搜索任务。 如果我们使用 less 程序来浏览我们的 phonelist.txt 文件:

    [me@linuxbox ~]$ less phonelist.txt
    

    然后查找我们有效的表达式:

    (232) 298-2265
    (624) 381-1078
    (540) 126-1980
    (874) 163-2885
    (286) 254-2860
    (292) 108-518
    (129) 44-1379
    (458) 273-1642
    (686) 299-8268
    (198) 307-2440
    ~
    /^\([0-9]{3}\) [0-9]{3}-[0-9]{4}$
    

    less 将会高亮匹配到的字符串,这样就很容易看到无效的电话号码:

    (232) 298-2265
    (624) 381-1078
    (540) 126-1980
    (874) 163-2885
    (286) 254-2860
    (292) 108-518
    (129) 44-1379
    (458) 273-1642
    (686) 299-8268
    (198) 307-2440
    ~
    (END)
    

    另一方面,vim 支持基本的正则表达式,所以我们用于搜索的表达式看起来像这样:

    /([0-9]\{3\}) [0-9]\{3\}-[0-9]\{4\}
    

    我们看到表达式几乎一样;然而,在扩展表达式中,许多被认为是元字符的字符在基本的表达式 中被看作是文本字符。只有用反斜杠把它们转义之后,它们才被看作是元字符。

    依赖于系统中 vim 的特殊配置,匹配项将会被高亮。如若不是,试试这个命令模式:

    :hlsearch
    

    来激活搜索高亮功能。

    注意:依赖于你的发行版,vim 有可能支持或不支持文本搜索高亮功能。尤其是 Ubuntu 自带了 一款非常简化的 vim 版本。在这样的系统中,你可能要使用你的软件包管理器来安装一个功能 更完备的 vim 版本。

    总结归纳

    在这章中,我们已经看到几个使用正则表达式例子。如果我们使用正则表达式来搜索那些使用正则表达式的应用程序, 我们可以找到更多的使用实例。通过查找手册页,我们就能找到:

    [me@linuxbox ~]$ cd /usr/share/man/man1
    [me@linuxbox man1]$ zgrep -El 'regex|regular expression' *.gz
    

    这个 zgrep 程序是 grep 的前端,允许 grep 来读取压缩文件。在我们的例子中,我们在手册文件所在的 目录中,搜索压缩文件中的内容。这个命令的结果是一个包含字符串“regex”或者“regular expression”的文件列表。正如我们所看到的,正则表达式会出现在大量程序中。

    基本正则表达式中有一个特性,我们没有涵盖。叫做反引用,这个特性在下一章中会被讨论到。

    拓展阅读

    有许多在线学习正则表达式的资源,包括各种各样的教材和速记表。

    另外,关于下面的背景话题,Wikipedia 有不错的文章。

    展开全文
  • 课程笔记地址:https://mp.csdn.net/postlist 课程代码地址:... 欢迎大家fork及star!(-^O^-) 改善深层神经网络:超参数调试、正则化以及优化 —深度学习的实践方面 1. 训练、验证、测试集 ...

    课程笔记地址:https://blog.csdn.net/column/details/26931.html
    课程代码地址:https://github.com/duboya/DeepLearning.ai-pragramming-code/tree/master
    欢迎大家forkstar!(-O-)

    改善深层神经网络:超参数调试、正则化以及优化 —深度学习的实践方面

    1. 训练、验证、测试集

    对于一个需要解决的问题的样本数据,在建立模型的过程中,我们会将问题的data划分为以下几个部分:

    • 训练集(train set):用训练集对算法或模型进行训练过程;
    • 验证集(development set):利用验证集或者又称为简单交叉验证集(hold-out cross validation set)进行交叉验证,选择出最好的模型;
    • 测试集(test set):最后利用测试集对模型进行测试,获取模型运行的无偏估计。

    小数据时代

    在小数据量的时代,如:100、1000、10000的数据量大小,可以将data做以下划分:

    • 无验证集的情况:70% / 30%;
    • 有验证集的情况:60% / 20% / 20%;
      通常在小数据量时代,以上比例的划分是非常合理的。

    大数据时代

    但是在如今的大数据时代,对于一个问题,我们拥有的data的数量可能是百万级别的,所以验证集和测试集所占的比重会趋向于变得更小。

    验证集的目的是为了验证不同的算法哪种更加有效,所以验证集只要足够大能够验证大约2-10种算法哪种更好就足够了,不需要使用20%的数据作为验证集。如百万数据中抽取1万的数据作为验证集就可以了。

    测试集的主要目的是评估模型的效果,如在单个分类器中,往往在百万级别的数据中,我们选择其中1000条数据足以评估单个模型的效果。

    • 100万数据量:98% / 1% / 1%;
    • 超百万数据量:99.5% / 0.25% / 0.25%(或者99.5% / 0.4% / 0.1%)

    Notation

    • 建议验证集要和训练集来自于同一个分布,可以使得机器学习算法变得更快;
    • 如果不需要用无偏估计来评估模型的性能,则可以不需要测试集。
    1. 训练集与测试集必须来自同一数据分布,不然测试集性能无法衡量模型偏差,后续Ng会介绍当
    2. 对开发集和测试集上的数据进行检查,确保他们来自于相同的分布。使得我们以开发集为目标方向,更正确地将算法应用到测试集上。
    3. “在深度学习的时代,因为需求的数据量非常大,现在很多的团队,使用的训练数据都是和开发集和测试集来自不同的分布。”
    4. 后面课程中Ng也提到,如做一个鉴定猫狗图片的分类器,训练数据往往是高清图片,但用户上传的照片质量则普遍偏低,这时候,train set、dev set来自高清图片,测试集来自用户上传照片,势必造成分类效果很差。
    5. 此时的做法通常有两种:一种是将收集到的少量实际数据如10000张与实际高清训练数据200000张照片打乱再依次分配到train set, dev set, test set。
      虽然这种方式实现了数据的平均分布,dev set 与 test set也来自同一分布。但由于实际数据集占总数据集比例很小,此时,训练出的模型更倾向于高清照片分类,所以这种做法效果并不好。(不推荐)
    6. 另一种做法是采用训练集全部采用高清照片,dev set与test set采用实际数据集,或者从实际数据集中分出5000张加入到训练数据集中。
      此时,好处是:开发集全部来自手机图片,瞄准目标; 坏处则是:训练集和开发、测试集来自不同的分布。
      从长期来看,这样的分布能够给我们带来更好的系统性能。(推荐)
    7. 通过估计学习算法的偏差和方差,可以帮助我们确定接下来应该优先努力的方向。但是当我们的训练集和开发、测试集来自不同的分布时,分析偏差和方差的方式就有一定的不同。

    2. 偏差、方差

    对于下图中两个类别分类边界的分割:

    在这里插入图片描述

    从图中我们可以看出,在欠拟合(underfitting)的情况下,出现高偏差(high bias)的情况;在过拟合(overfitting)的情况下,出现高方差(high variance)的情况。

    在bias-variance tradeoff 的角度来讲,我们利用训练集对模型进行训练就是为了使得模型在train集上使 bias 最小化,避免出现underfitting的情况;

    但是如果模型设置的太复杂,虽然在train集上 bias 的值非常小,模型甚至可以将所有的数据点正确分类,但是当将训练好的模型应用在dev 集上的时候,却出现了较高的错误率。这是因为模型设置的太复杂则没有排除一些train集数据中的噪声,使得模型出现overfitting的情况,在dev 集上出现高 variance 的现象。

    所以对于bias和variance的权衡问题,对于模型来说是一个十分重要的问题。

    例子:

    几种不同的情况:

    在这里插入图片描述

    以上为在人眼判别误差在0%的情况下,该最优误差通常也称为“贝叶斯误差”,如果“贝叶斯误差”大约为15%,那么图中第二种情况就是一种比较好的情况。

    1. 上图中optimal (Bayes) error约为0,Bayes error是理论极限达到的最小错误,由于人非常擅长处理图像、音频之类的非结构化数据处理,其处理性能已逼近理论极限,故而常用人在这类事务上处理的error当做是理论上能达到的最小error,这也往往是train set训练模型力求达到的目标。
    2. 如果训练集距离Bayes error差距较大,则证明模型没有训练好,存在high bias,如果train set error ≈ \approx Bayes error,则证明不存在high bias,此时,再分析dev set error,若dev set error ≈ \approx train set error,则证明不存在high variance, 若dev set error >> train set error,则证明存在过拟合。

    High bias and high variance的情况

    上图中第三种bias和variance的情况出现的可能如下:
    在这里插入图片描述

    即训练的模型既存在高偏差(high bias),又存在高方差(high variance)。这种情况在高维空间更常见:在高维空间中更容易存在部分空间过拟合,部分空间欠拟合现象。

    3. 机器学习的基本方法

    在训练机器学习模型的过程中,解决High bias 和High variance 的过程:

    在这里插入图片描述

    • 1.是否存在High bias ?
      • 增加网络结构,如增加隐藏层数目;
      • 训练更长时间;
      • 寻找合适的网络架构,使用更大的NN结构;
    • 2.是否存在High variance?
      • 获取更多的数据;
      • 正则化( regularization);
      • 寻找合适的网络结构;

    在大数据时代,深度学习对监督式学习大有裨益,使得我们不用像以前一样太过关注如何平衡偏差和方差的权衡问题,通过以上方法可以使得在不增加另一方的情况下减少一方的值。

    1. 机器学习中variance 与 bias 往往存在一个权衡取舍的问题,要么增大bias,来减少variance(如logistic regression减少输入变量),要么增大variance,来减少bias(如random forest增大tree的数量)。
    2. 而neural networks往往不需这样的权衡,可在不增大bias的情况下减少variance,同理也可在不增大variance的情况下减少bias。

    4. 正则化(regularization)

    Logistic regression

    加入正则化项的代价函数:
    J ( w , b ) = 1 m ∑ i = 1 m l ( y ^ ( i ) , y ( i ) ) + λ 2 m ∥ w ∥ 2 2 J(w, b)=\frac{1}{m} \sum_{i=1}^{m} l\left(\hat{y}^{(i)}, y^{(i)}\right)+\frac{\lambda}{2 m}\|w\|_{2}^{2} J(w,b)=m1i=1ml(y^(i),y(i))+2mλw22
    上式为逻辑回归的L2正则化。

    • L2正则化: λ 2 m ∥ w ∥ 2 2 = λ 2 m ∑ j = 1 n x w j 2 = λ 2 m w T w \frac{\lambda}{2 m}\|w\|_{2}^{2}=\frac{\lambda}{2 m} \sum_{j=1}^{n_{x}} w_{j}^{2}=\frac{\lambda}{2 m} w^{T} w 2mλw22=2mλj=1nxwj2=2mλwTw
    • L1正则化: λ 2 m ∥ w ∥ 1 = λ 2 m ∑ j = 1 n x ∣ w j ∣ \frac{\lambda}{2 m}\|w\|_{1}=\frac{\lambda}{2 m} \sum_{j=1}^{n_{x}}\left|w_{j}\right| 2mλw1=2mλj=1nxwj

    其中 λ \lambda λ 为正则化因子。

    注意: lambda 在 Python 中属于保留字,所以在编程的时候, 用“lambd”代表这里的正则化因子 λ \lambda λ

    Neural network

    加入正则化项的代价函数:
    J ( w [ 1 ] , b [ 1 ] , ⋯   , w [ L ] , b [ L ] ) = 1 m ∑ i = 1 m l ( y ^ ( i ) , y ( i ) ) + λ 2 m ∑ l = 1 L ∥ w [ l ] ∥ F 2 J\left(w^{[1]}, b^{[1]}, \cdots, w^{[L]}, b^{[L]}\right)=\frac{1}{m} \sum_{i=1}^{m} l\left(\hat{y}^{(i)}, y^{(i)}\right)+\frac{\lambda}{2 m} \sum_{l=1}^{L}\left\|w^{[l]}\right\|_{F}^{2} J(w[1],b[1],,w[L],b[L])=m1i=1ml(y^(i),y(i))+2mλl=1Lw[l]F2
    其中 ∥ w [ l ] ∥ F 2 = ∑ i = 1 n [ l − 1 ] ∑ j = 1 n [ l ] ( w i j [ l ] ) 2 , \left\|w^{[l]}\right\|_{F}^{2}=\sum_{i=1}^{n^{[l-1]}} \sum_{j=1}^{n^{[l]}}\left(w_{i j}^{[l]}\right)^{2}, w[l]F2=i=1n[l1]j=1n[l](wij[l])2, 因为 w w w 的大小为 ( n [ l − 1 ] , n [ l ] ) , \left(n^{[l-1]}, n^{[l]}\right), (n[l1],n[l]), 该矩阵范数被称为 ”Frobenius norm“

    Weight decay

    在加入正则化项后,梯度变为:

    d W [ l ] = ( f o r m _ b a c k p r o p ) + λ m W [ l ] dW^{[l]}=( form\_backprop)+\frac{\lambda}{m} W^{[l]} dW[l]=(form_backprop)+mλW[l]

    则梯度更新公式变为:
    W [ l ] : = W [ l ] − α d W [ l ] W^{[l]}:=W^{[l]}-\alpha d W^{[l]} W[l]:=W[l]αdW[l]
    代入可得:

    W [ l ] : = W [ l ] − α [ ( f o r m _ b a c k p r o p ) + λ m W [ l ] ] = W [ l ] − α λ m W [ l ] − α (   f o r m _ b a c k p r o p ) = ( 1 − α λ m ) W [ l ] − α ( f o r m _ b a c k p r o p ) W^{[l]} :=W^{[l]}-\alpha\left[(form\_backprop)+\frac{\lambda}{m} W^{[l]}\right] \\ = W^{[l]}-\alpha \frac{\lambda}{m} W^{[l]}-\alpha(\ form\_backprop ) \\ \quad =\left(1-\frac{\alpha \lambda}{m}\right) W^{[l]}-\alpha(form\_backprop) W[l]:=W[l]α[(form_backprop)+mλW[l]]=W[l]αmλW[l]α( form_backprop)=(1mαλ)W[l]α(form_backprop)

    其中, ( 1 − α λ m ) \left(1-\frac{\alpha \lambda}{m}\right) (1mαλ) 为一个 < 1 <1 <1 的项, 会给原来的 W [ l ] W^{[l]} W[l] 一个衰减的参数, 所以 L2 范数正则化也被称为“权重衰减 (Weight decay)"

    正则化只用在模型训练过程中,在dev set与test set上是关闭的。
    “正则化机制,如 Dropout 和 L1/L2 权重正则化,在测试时是关闭的”。

    5. 为什么正则化可以减小过拟合

    假设下图的神经网络结构属于过拟合状态:

    在这里插入图片描述

    对于神经网络的Cost function:

    J ( w [ 1 ] , b [ l ] , ⋯   , w [ L ] , b [ L ] ) = 1 m ∑ i = 1 m l ( y ^ ( i ) , y ( i ) ) + λ 2 m ∑ l = 1 L ∣ ∣ w [ l ] ∣ ∣ F 2 J(w^{[1]}, b^{[l]}, \cdots , w^{[L]}, b^{[L]}) = \frac{1}{m}\sum_{i=1}^{m}l(\hat{y}^{(i)},y^{(i)}) + \frac{\lambda}{2m}\sum_{l=1}^{L} || w^{[l]}||_{F}^{2} J(w[1],b[l],,w[L],b[L])=m1i=1ml(y^(i),y(i))+2mλl=1Lw[l]F2

    加入正则化项,直观上理解,正则化因子 λ \lambda λ设置的足够大的情况下,为了使代价函数最小化,权重矩阵W就会被设置为接近于0的值。则相当于消除了很多神经元的影响,那么图中的大的神经网络就会变成一个较小的网络。

    当然上面这种解释是一种直观上的理解,但是实际上隐藏层的神经元依然存在,但是他们的影响变小了,便不会导致过拟合。

    数学解释:

    假设神经元中使用的激活函数为 g ( z ) = t a n h ⁡ ( z ) g(z) = tanh⁡(z) g(z)=tanh(z),在加入正则化项后:

    在这里插入图片描述

    λ \lambda λ增大,导致 W [ l ] W^{[l]} W[l]减小, Z [ l ] = W [ l ] a [ l − 1 ] + b [ l ] Z^{[l]} = W^{[l]}a^{[l−1]}+b^{[l]} Z[l]=W[l]a[l1]+b[l]便会减小,由上图可知,在z较小的区域里, t a n h ⁡ ( z ) tanh⁡(z) tanh(z)函数近似线性,所以每层的函数就近似线性函数,整个网络就成为一个简单的近似线性的网络,从而不会发生过拟合。

    注:由以上分析也应当得知,lambda应该设定合理,不然lambda过大的话,整个neural network变成了线型函数的叠加,依旧是线型函数,模型表达能力大大降低。

    6. Dropout 正则化

    Dropout(随机失活)就是在神经网络的Dropout层,为每个神经元结点设置一个随机消除的概率,对于保留下来的神经元,我们得到一个节点较少,规模较小的网络进行训练。

    在这里插入图片描述

    实现Dropout的方法:反向随机失活(Inverted dropout)

    首先假设对 layer 3 进行dropout:

    keep_prob = 0.8  # 设置神经元保留概率
    d3 = np.random.rand(a3.shape[0], a3.shape[1]) < keep_prob
    a3 = np.multiply(a3, d3)
    a3 /= keep_prob
    

    这里解释下为什么要有最后一步:a3 /= keep_prob

    依照例子中的keep_prob = 0.8 ,那么就有大约20%的神经元被删除了,也就是说 a [ 3 ] ​ a^{[3]}​ a[3]中有20%的元素被归零了,在下一层的计算中有 Z [ 4 ] = W [ 4 ] a [ 3 ] + b [ 4 ] ​ Z^{[4]} = W^{[4]}a^{[3]} + b^{[4]}​ Z[4]=W[4]a[3]+b[4],所以为了不影响 Z [ 4 ] ​ Z^{[4]}​ Z[4]的期望值,所以需要 W [ 4 ] ⋅ a [ 3 ] ​ W^{[4]}⋅a^{[3]}​ W[4]a[3]的部分除以一个keep_prob。

    Inverted dropout通过对“a3 /= keep_prob”,则保证无论keep_prob设置为多少,都不会对 Z [ 4 ] Z^{[4]} Z[4]的期望值产生影响。

    Notation:在测试阶段不要用dropout,因为那样会使得预测结果变得随机。

    1. dropout主要用于CV方向,由于CV方向input size很大,输入了太多像素,以至于没有足够多的数据,所以一直存在过拟合,故而常用到dropout,几乎成了默认设置!
    2. 但dropout是一种正则化手段,除非算法表现出过拟合,不然不用使用dropout,故而dropout在其他方向应用很少。因为即便是模型表现出了过拟合,也有很多方法可以用来对抗过拟合(比如使用L2正则式(很常用),使用L1正则式,加入更多数据,更改网络机构,提前结束训练)等方法。
    1. 一般输入层很少用到dropout,即对于输入层常设置keep_prob =1;
    2. drop out实施时候,可采用不同方式,一种方式是针对不同层设置不同的keep_prob,对应层神经元数目过多的时候,设置keep_prob较小(如0.5-0.8),对应层神经元数目过少的时候,设置keep_porb较大,如设置0.8,0.9等,这时候每层设置的keep_prob也是一个超参数,需要使用交叉验证寻找超参,增加了训练难度;
      另一种方法只针对神经元数目较多的层设置相同的drop_prob,这时候,只增加了一个keep_prob超参。
    3. 因为引入dropout之后程序,cost function难以明确定义,程序变得难以调试,故而Ng的通常做法是先关闭dropout,设置keep_prob=1,运行代码,保证损失函数J单调递减,然后再打开dropout函数,希望在dropout过程中,代码并未引入bug。

    7. 理解 Dropout

    另外一种对于Dropout的理解。

    这里我们以单个神经元入手,单个神经元的工作就是接收输入,并产生一些有意义的输出,但是加入了Dropout以后,输入的特征都是有可能会被随机清除的,所以该神经元不会再特别依赖于任何一个输入特征,也就是说不会给任何一个输入设置太大的权重。

    所以通过传播过程,dropout将产生和L2范数相同的收缩权重的效果。

    对于不同的层,设置的keep_prob也不同,一般来说神经元较少的层,会设keep_prob =1.0,神经元多的层,则会将keep_prob设置的较小。

    缺点:

    dropout的一大缺点就是其使得 Cost function不能再被明确的定义,以为每次迭代都会随机消除一些神经元结点,所以我们无法绘制出每次迭代 J ( W , b ) J(W,b) J(W,b)下降的图,如下:

    在这里插入图片描述

    使用Dropout:

    • 关闭dropout功能,即设置 keep_prob = 1.0;
    • 运行代码,确保 J ( W , b ) J(W,b) J(Wb)函数单调递减;
    • 再打开dropout函数。

    8. 其他正则化方法

    • 数据扩增(Data augmentation):通过图片的一些变换,得到更多的训练集和验证集;

    Data augmentation是一种常用方法,会在第三课中详细讲述,大致有对称变换等方式。

    在这里插入图片描述

    • Early stopping:在交叉验证集的误差上升之前的点停止迭代,避免过拟合。这种方法的缺点是无法同时解决bias和variance之间的最优。

    这种方法Ng并不推荐用,因为按照Ng在第三课中讲到的正交性原则,设计、训练模型的时候应该使调整bias与调整variance的方法分开,互不影响,这样在模型出现hig bias or high variance的时候就可以针对问题进行单独处理而不会影响另一方。

    在这里插入图片描述

    9. 归一化输入

    对数据集特征 x 1 x_1 x1, x 2 x_2 x2归一化的过程:

    在这里插入图片描述

    • 计算每个特征所有样本数据的均值: μ = 1 m ∑ i = 1 m x ( i ) \mu = \frac{1}{m}\sum_{i=1}^{m}x^{(i)} μ=m1i=1mx(i)
    • 减去均值得到对称的分布: x : = x − μ x := x − \mu x:=xμ
    • 归一化方差: σ 2 = 1 m ∑ i = 1 m x ( i ) 2 \sigma^2 = \frac{1}{m}\sum_{i=1}^{m}x^{(i)^2} σ2=m1i=1mx(i)2, x = x / σ 2 x = x / \sigma^2 x=x/σ2

    这是一种高斯归一化方法。

    使用归一化的原因:

    在这里插入图片描述

    由图可以看出不使用归一化和使用归一化前后Cost function 的函数形状会有很大的区别。

    在不使用归一化的代价函数中,如果我们设置一个较小的学习率,那么很可能我们需要很多次迭代才能到达代价函数全局最优解;如果使用了归一化,那么无论从哪个位置开始迭代,我们都能以相对很少的迭代次数找到全局最优解。

    10. 梯度消失与梯度爆炸

    如下图所示的神经网络结构,以两个输入为例:

    在这里插入图片描述

    上面的情况对于导数也是同样的道理,所以在计算梯度时,根据情况的不同,梯度函数会以指数级递增或者递减,导致训练导数难度上升,梯度下降算法的步长会变得非常非常小,需要训练的时间将会非常长。

    在梯度函数上出现的以指数级递增或者递减的情况就分别称为梯度爆炸或者梯度消失。

    1. 梯度消失带来的问题是梯度无法有效回传,当从最后一层算出loss后,最后几层还能进行梯度下降,但越往前回传,梯度改变量越小,还没到中间就接近于0,造成前面的层无法得到训练,其最终结果就是虽然层数很多,但是前面层得不到训练,模型最终依旧是表现为浅层模型(只有最后几层起到作用)。
    2. 而梯度爆炸则直接使得前面层变化太大,导致参数数值溢出,最终前面层参数表现为Nan。
    3. 其实梯度爆炸和梯度消失问题都是因为网络太深,网络权值更新不稳定造成的,本质上是因为梯度反向传播中的连乘效应。对于更普遍的梯度消失问题,可以考虑用Relu激活函数取代sigmoid函数。另外,LSTM的结构设计也可以改善RNN的梯度消失问题。

    在这里插入图片描述
    注:上面图片最早是从知乎某答案上截图过来,其原因解释比较简答,但不影响管中窥豹,从梯度更新反向传播的公式来看:
    ∂ C ∂ b 1 = ∂ C ∂ y 4 ∂ y 4 ∂ z 4 ∂ z 4 ∂ x 4 ∂ x 4 ∂ z 3 ∂ z 3 ∂ x 3 ∂ x 3 ∂ x 2 ∂ x 2 ∂ z 1 ∂ z 1 ∂ b 1 = ∂ C ∂ y 4 σ ′ ( z 4 ) w 4 σ ′ ( z 3 ) w 3 σ ′ ( z 2 ) w 2 σ ′ ( z 1 ) \begin{array}{l}{\frac{\partial C}{\partial b_{1}}=\frac{\partial C}{\partial y_{4}} \frac{\partial y_{4}}{\partial z_{4}} \frac{\partial z_{4}}{\partial x_{4}} \frac{\partial x_{4}}{\partial z_{3}} \frac{\partial z_{3}}{\partial x_{3}} \frac{\partial x_{3}}{\partial x_{2}} \frac{\partial x_{2}}{\partial z_{1}} \frac{\partial z_{1}}{\partial b_{1}}} \\ {=\frac{\partial C}{\partial y_{4}} \sigma^{\prime}\left(z_{4}\right) w_{4} \sigma^{\prime}\left(z_{3}\right) w_{3} \sigma^{\prime}\left(z_{2}\right) w_{2} \sigma^{\prime}\left(z_{1}\right)}\end{array} b1C=y4Cz4y4x4z4z3x4x3z3x2x3z1x2b1z1=y4Cσ(z4)w4σ(z3)w3σ(z2)w2σ(z1)
    其反向传播表达式虽然看起来很长,其实质上就 3 块内容:

    a. 损失函数导数: ∂ C ∂ y 4 \frac{\partial C}{\partial y_{4}} y4C

    b. 激活函数导数: ∂ y i ∂ z i \frac{\partial y_{i}}{\partial z_{i}} ziyi

    c. 权重参数: ∂ z i ∂ x i \frac{\partial z_{i}}{\partial x_{i}} xizi

    因此,从这三者组合不难理解,若要避免梯度消失或者梯度爆炸,从权重角度考虑可以加入 Batch normalization,另外联调损失函数与权重激活函数也是一种很好的方式,这也是为什么损失函数的选择要配合激活函数一起选择会有更好的表现原因,下面再简单进一步阐述一下。

    当激活函数采用 sigmoid 函数,损失函数采用 MSE 时会出现梯度消失。原因如下:

    (1) MSE对参数的偏导
    ∂ c ∂ w = ( a − y ) σ ( z ) ′ x \frac{\partial c}{\partial w}=(a-y) \sigma(z)^{\prime} x wc=(ay)σ(z)x
    ∂ c ∂ b = ( a − y ) σ ( z ) ′ \frac{\partial c}{\partial b}=(a-y) \sigma(z)^{\prime} bc=(ay)σ(z)
    (2) corss-entropy对参数的偏导
    ∂ c ∂ w = 1 n ∑ x ( σ ( z ) − y ) \frac{\partial c}{\partial w}=\frac{1}{n} \sum x(\sigma(z)-y) wc=n1x(σ(z)y)
    ∂ c ∂ b = 1 n ∑ ( σ ( z ) − y ) \frac{\partial c}{\partial b}=\frac{1}{n} \sum(\sigma(z)-y) bc=n1(σ(z)y)

    由上述公式可以看出,在使用MSE时,w、b的梯度均与sigmoid函数对z的偏导有关系,而sigmoid函数的偏导在自变量非常大或者非常小时,偏导数的值接近于零,这将导致w、b的梯度将不会变化,也就是出现所谓的梯度消失现象。

    而使用 cross-entropy 时,w、b 的梯度就不会出现上述的情况,而且使用交叉熵损失函数配合 sigmoid 函数带来一个附加优势,即反向回传时候的梯度与误差(预测值与实际值之差)成正比,这样更有利于加速模型的训练。

    具体可参考: https://www.cnblogs.com/pinard/p/6437495.html

    常用的用于解决梯度消失和梯度爆炸的方法如下所示:

    1. 使用 ReLU、LReLU、ELU、maxout 等激活函数
      sigmoid函数的梯度随着x的增大或减小和消失,而ReLU不会。
    2. 使用批规范化
      通过规范化操作将输出信号 x 规范化到均值为 0,方差为 1 保证网络的稳定性.从上述分析分可以看到,反向传播式子中有 w 的存在,所以 w 的大小影响了梯度的消失和爆炸,Batch Normalization 就是通过对每一层的输出规范为均值和方差一致的方法,消除了 w 带来的放大缩小的影响,进而解决梯度消失和爆炸的问题。
    1. 梯度爆炸比梯度消失更容易解决,也更容易判定,如果出现梯度爆炸,其经过BP参数更新,w会出现指数级增长(如上分析),导致最终w数值溢出,会造成前层神经网络出现很多Nan,这时候便可以判定是否出现梯度爆炸。
    2. 解决办法就是利用gradient clipping,对w设置一个上线,当达到这个上限之后就对其进行缩放,保证w不至于太大。
    3. 做gradient clipping有很多方法,在RNN编程实践的时候提到一种简单的方法,即设置上下线[-N, +N],当达到这个上下线的时候就用上下线阈值替代w。对于梯度消失问题 ,在RNN结构中是我们首要关心的问题,也更难解决。
    4. 对于梯度消失问题,在RNN的结构中是我们首要关心的问题,也更难解决;虽然梯度爆炸在RNN中也会出现,但对于梯度爆炸问题,因为参数会指数级上升,会让我们的网络参数变得很大,得到很多的Nan或者数值,所以梯度爆炸是很容易发现的,我们的解决方法就是用梯度修剪,也就是观察梯度变量,如果其大于某个阈值,则对其进行缩放,保证它不会太大。

    11. 利用初始化缓解梯度消失和爆炸问题

    以一个单个神经元为例子:

    在这里插入图片描述

    由上图可知,当输入的数量n较大时,我们希望每个 w i w_i wi的值都小一些,这样它们的和得到的z也较小。

    这里为了得到较小的 w i w_i wi,设置 V a r ( w i ) = 1 n Var(w_i) = \frac{1}{n} Var(wi)=n1,这里称为Xavier initialization。
    对参数进行初始化:

    WL = np.random.randn(WL.shape[0],WL.shape[1])* np.sqrt(1/n)
    

    这么做是因为,如果激活函数的输入x近似设置成均值为0,标准方差1的情况,输出z也会调整到相似的范围内。虽然没有解决梯度消失和爆炸的问题,但其在一定程度上确实减缓了梯度消失和爆炸的速度。

    不同激活函数的 Xavier initialization:

    • 激活函数使用Relu: V a r ( w i ) = 2 n Var(w_i) = \frac{2}{n} Var(wi)=n2
    • 激活函数使用tanh: V a r ( w i ) = 1 n Var(w_i) = \frac{1}{n} Var(wi)=n1
      其中n是输入的神经元个数,也就是 n [ l − 1 ] n^{[l−1]} n[l1]

    12. 梯度的数值逼近

    使用双边误差的方法去逼近导数:

    在这里插入图片描述
    由图可以看出,双边误差逼近的误差是0.0001,先比单边逼近的误差0.03,其精度要高了很多。

    涉及的公式:

    • 双边导数:

    f ′ ( θ ) = lim ⁡ ϵ → 0 = f ( θ + ϵ ) − f ( θ − ϵ ) 2 ϵ f^{'}(\theta) = \lim_{\epsilon \rightarrow 0} = \frac{f(\theta + \epsilon) - f(\theta - \epsilon)}{2\epsilon} f(θ)=ϵ0lim=2ϵf(θ+ϵ)f(θϵ)

    误差: O ( ϵ 2 ) O(\epsilon^2) O(ϵ2)

    • 单边导数:

    f ′ ( θ ) = lim ⁡ ϵ → 0 = f ( θ ) − f ( θ − ϵ ) ϵ f^{'}(\theta) = \lim_{\epsilon \rightarrow 0} = \frac{f(\theta) - f(\theta - \epsilon)}{\epsilon} f(θ)=ϵ0lim=ϵf(θ)f(θϵ)

    误差: O ( ϵ ) O(\epsilon) O(ϵ)

    13. 梯度检验

    下面用前面一节的方法来进行梯度检验。

    连接参数

    因为我们的神经网络中含有大量的参数: W [ 1 ] W^{[1]} W[1], b [ 1 ] b^{[1]} b[1], ⋯ \cdots , W [ L ] W^{[L]} W[L], b [ L ] b^{[L]} b[L],为了做梯度检验,需要将这些参数全部连接起来,reshape成一个大的向量 θ \theta θ

    同时对 d W [ 1 ] dW^{[1]} dW[1], d b [ 1 ] db^{[1]} db[1], ⋯ \cdots , d W [ L ] dW^{[L]} dW[L], d b [ L ] db^{[L]} db[L]执行同样的操作。

    在这里插入图片描述

    进行梯度检验

    进行如下图的梯度检验:

    判断 d θ a p p r o x ≈ d θ d\theta_{approx} \approx d\theta dθapproxdθ是否接近。

    判断公式:
    ∥ d θ a p p r o x − d θ ∥ 2 ∥ d θ a p p r o x ∥ 2 + ∥ d θ ∥ 2 \frac{\parallel d\theta_{approx} - d\theta \parallel_2}{\parallel d\theta_{approx}\parallel_2 + \parallel d\theta \parallel_2} dθapprox2+dθ2dθapproxdθ2

    其中," ∥ ⋅ ∥ 2 \parallel \cdot \parallel_2 2"表示欧几里得范数,它是误差平方之和,然后求平方根,得到的欧氏距离。

    14. 实现梯度检验 Notes

    • 不要在训练过程中使用梯度检验,只在debug的时候使用,使用完毕关闭梯度检验的功能;
    • 如果算法的梯度检验出现了错误,要检查每一项,找出错误,也就是说要找出哪个 d θ a p p r o x [ i ] d\theta_{approx}[i] dθapprox[i] d θ dθ dθ的值相差比较大;
    • 不要忘记了正则化项;
    • 梯度检验不能与dropout同时使用。因为每次迭代的过程中,dropout会随机消除隐层单元的不同神经元,这时是难以计算dropout在梯度下降上的代价函数J;
    • 在随机初始化的时候运行梯度检验,或许在训练几次后再进行。

    注:补充参考自:
    https://blog.csdn.net/koala_tree/article/details/78125697

    展开全文
  • get_ticket在响应头中要获取的部分: post 请求中可以将url中的参数,加到变量中访问: 2.4 请求业务系统: http请求具体设置: get_jsessionid的正则表达式的提取器: get_jsessionid表达式:jsessionid=(.*) get_...
  • 改善深层神经网络:超参数调试、正则化以及优化 W1.深度学习的实践层面 作业1:初始化 好的初始化: 加快梯度下降的收敛速度 增加梯度下降收敛到较低的训练(和泛化)误差的几率 导入数据 import numpy as np ...
  • 性能测试_Day_06JMETER 参数化应用JMETER 数据量预估值JMETER 后置处理器-正则表达式提取器应用范围(Apply to)检查当前的响应字段(Field to Check)正则表达式填写参数规范引用名称(Reference Name)正则表达式...
  • 深入理解吴恩达深度学习(02 改善深层神经网络:超参数调试、正则化以及优化 第一周深度学习的实践层面)1.1 训练,验证,测试集(Train / Dev / Test sets)1.2 偏差,方差(Bias /Variance)1.3 机器学习基础...
  • Jmeter的研究一直在跟进,今天讲一下如何使用正则表达式提取器来获取关联参数,还是以我最熟悉的BBS为例吧。 1、录制BBS登陆、发帖、回帖脚本后,进行筛选,整理后的脚本如下图所示: 2、forum.jsp页面是...
  • 正则参数(这也是一个超级参数), ∥ w ∥ 2 2 = ∑ j = 1 n x w j 2 = w T w \left \| w \right \|^{2}_{2} = \sum_{j=1}^{n_{x}} w_{j}^{2}=w^{T}w ∥ w ∥ 2 2 ​ = ∑ j = 1 n x ​ ​ w j 2 ​ = w T w ,...
  • #成功时返回 TRUE, 或者在失败时返回 FALSE。 bool filter_has_var( int $type, ...当然,也可以使用 isset($_GET["name"]) 、isset($_POST["name"])、isset($_SERVER["name"])等方式来判断全局变量里的参数是否存在
  • JMeter 正则表达式关联

    2018-10-22 15:15:44
    话说LoadRunner有的一些功能,比如:参数化、检查点、集合点、关联,Jmeter也都有这些功能,只是功能可能稍弱一些,今天就关联来讲解一下。 JMeter的关联方法有两种:后置处理器-正则表达式提取器与XPath ...
  • 正则表达式描述了一种字符串的匹配模式,可以用来检查一个串是否含有某种子串,将匹配的子串替换或者从某个串中取出符合某个条件的子串等。 正则表达式字符说明: 百讲不如一练,看例题 例如利用正则表达式来对这...
  • 正则表达式不要背

    2021-01-10 15:39:55
    原文链接:https://juejin.im/post/5cdcd42551882568651554e6 正则表达式到底是什么 正则表达式(Regular Expression)其实就是一门工具,目的是为了字符串模式匹配,从而实现搜索和替换功能。它起源于上个20世纪50...
  • MySQL使用正则表达式

    千次阅读 2017-12-26 17:18:46
    以前我要查找数据都是使用like后来发现mysql中也有正则表达式了并且感觉性能要好于like,下面我来给大家分享一下mysql REGEXP正则表达式使用详解,希望此方法对大家有帮助。 一、正则与LIKE的区别  Mysql...
  • python3正则表达式

    2018-04-10 09:11:53
    正则表达式是一个很强大的字符串处理工具,它能帮助我们方便的检查一个字符串是否与某种模式匹配。一、python中正则表达式的基本语法规则 序号 语法 解释 表达式 成功匹配对象 1 一般字符 匹配自身相对应的...
  • JS正则表达式

    2016-11-08 14:10:19
    19 空白行的正则表达式:\n\s*\r (可以用来删除空白行) 20 HTML标记的正则表达式: < (\S*?)[^ > ] *>.* ? \1 > | < .*? /> 21 首尾空白字符的正则表达式:^\s *|\s* $或(^\s *)|(\s* $) (可以用来...
  • 正则表达式大全

    千次阅读 2018-12-28 16:39:26
    正则表达式是一个考验每个程序员记忆力的功能,大家都经历过忘了再记,记了再忘的痛苦,在这里试图先通过一个简单的表格方式来呈现它,然后再慢慢品味,消化它。 1.符号表 名称 符号 读音 功能 定界符 ...
  • 经典正则表达式

    2008-08-21 09:41:00
    正则表达式用于字符串处理,表单验证等场合,实用高效,但用到时总是不太把握,以致往往要上网查一番。我将一些常用的表达式收藏在这里,作备忘之用。本贴随时会更新。匹配中文字符的正则表达式: [/u4e00-/u9fa5]...
  • 正则表达式学习

    千次阅读 2008-02-15 17:16:00
    正则表达式学习 引用:http://www.wangqi.com/n9250c53.aspxhttp://www.cnblogs.com/zhzkl/archive/2006/10/01/519868.html 基本语法 在正则表达式中拥有一套自己的语法规则,常见语法包括;字符匹配、重复匹配...
  • 正则表达式科学计数法As data scientists, diving headlong into huge heaps of data is part of the mission. Sometimes, this includes massive corpuses of text. For instance, suppose we were asked to figure...
  • jmeter 正则表达式用法

    2019-08-05 12:29:53
    所谓关联,就是把应用中动态变化返回的数据获取到,把它保存为一个参数,提供给后面需要用到的地方进行使用。 Jmeter中关联是通过“添加—后置处理器—正则表达式提取器”来获取数据,且注意正则表达式提取器附在...
  • Jmeter-详解正则表达式

    2020-11-17 13:25:37
    在一个线程组中,B请求需要使用A请求返回的数据,也就是常说的关联,将上一个请求的响应结果作为下一个请求的参数,则需要对A请求的响应报文使用后置处理器,其中最方便最常用的就是正则表达式提取器了。 1、实例一 ...
  • 正则表达式 - 基础篇

    2018-10-18 12:33:10
    正则简介  - 1.使用特殊的符号来做校验,目标是操作字符串。例如:手机号码、邮箱、身份证的校验等。 使用正则表达式的优缺点  - 1.使用特殊的符号来做校验,目标是操作字符串。例如:手机号码、邮箱、身份证的...
  • js正则表达式

    千次阅读 2012-08-15 20:02:44
    例如,/jim {2,6}/ 上述正则表达式规定字符m可以在匹配对象中连续出现2-6次,因此,上述正则表达式可以同jimmy或jimmmmmy等字符串相匹配。 在对如何使用正则表达式有了初步了解之后,我们来看一下其它几个重要的元...
  • js 正则表达式

    千次阅读 2012-07-30 17:16:58
    例如,/jim {2,6}/ 上述正则表达式规定字符m可以在匹配对象中连续出现2-6次,因此,上述正则表达式可以同jimmy或jimmmmmy等字符串相匹配。  在对如何使用正则表达式有了初步了解之后,我们来看一下其它几个重要的元...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 28,723
精华内容 11,489
关键字:

post参数检查正则