zoukankan      html  css  js  c++  java
  • python爬虫05 | 年轻人,不会正则表达式你睡得着觉?有点出息没有?

    现在

     

    你已经会使用 python 模拟浏览器

     

    进行一些 Http 的请求了

     

    那么请求完之后

     

    服务器返回给我们一堆源代码

     

    我们可不是啥都要的啊

     

    我们是有原则的

     

     

    我们想要的东西

     

    怎么能一股脑的啥都往自己兜里塞呢?

     

    使不得

     

    使不得

     

    所以

     

    在服务器返回给我们的源码之中

     

    我们要过滤

     

    拿到我们想要的就好

     

    其它就丢一旁

     

    那么

     

    我们就需要学会怎么使用

     

    正则表达式

     

    通过它

     

    我们才能过滤出我们想要的内容

     

     

    ...

     

     

    接下来就是

     

    学习 python 的正确姿势

     

     

     

    真香警告

     

    这篇文章不适合急性子的人看,要不然会把手机砸了的!但是,如果你能看完,那么正则表达式对你来说,算个 p 的难度啊?

     

     

     

    其实

     

    正则表达式不仅仅适用于 python

     

    很多编程语言

     

    很多地方都会使用到正则

     

    试想一下

     

    如何从下面这段字符串中快速检索所有的数字出来呢?

     

    zui12shu234ai45der6en7sh88ixia7898os0huaib

     

     

     

     

    简单来说

     

    正则表达式就是定义一些特殊的符号

     

    来匹配不同的字符

     

    比如

     

    d

     

    就可以代表

     

    一个数字,等价于 0-9 的任意一个

     

    那么你肯定想知道

     

    其它的特殊符号表示的啥意思吧?

     

     

    就不告诉你

     

     

    本篇完

     

    再见

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    这是各种符号的解释

     

     

    字符描述
    将下一个字符标记为一个特殊字符(File Format Escape,清单见本表)、或一个原义字符(Identity Escape,有^$()*+?.[{|共计12个)、或一个向后引用(backreferences)、或一个八进制转义符。例如,“n”匹配字符“n”。“ ”匹配一个换行符。序列“\”匹配“”而“(”则匹配“(”。
    ^ 匹配输入字符串的开始位置。如果设置了RegExp对象的Multiline属性,^也匹配“ ”或“ ”之后的位置。
    $ 匹配输入字符串的结束位置。如果设置了RegExp对象的Multiline属性,$也匹配“ ”或“ ”之前的位置。
    * 匹配前面的子表达式零次或多次。例如,zo*能匹配“z”、“zo”以及“zoo”。*等价于{0,}。
    + 匹配前面的子表达式一次或多次。例如,“zo+”能匹配“zo”以及“zoo”,但不能匹配“z”。+等价于{1,}。
    ? 匹配前面的子表达式零次或一次。例如,“do(es)?”可以匹配“do”或“does”中的“do”。?等价于{0,1}。
    {n} n是一个非负整数。匹配确定的n次。例如,“o{2}”不能匹配“Bob”中的“o”,但是能匹配“food”中的两个o。
    {n,} n是一个非负整数。至少匹配n次。例如,“o{2,}”不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o。“o{1,}”等价于“o+”。“o{0,}”则等价于“o*”。
    {n,m} mn均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,“o{1,3}”将匹配“fooooood”中的前三个o。“o{0,1}”等价于“o?”。请注意在逗号和两个数之间不能有空格。
    ? 非贪心量化(Non-greedy quantifiers):当该字符紧跟在任何一个其他重复修饰符(*,+,?,{n},{n,},{n,m})后面时,匹配模式是贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串“oooo”,“o+?”将匹配单个“o”,而“o+”将匹配所有“o”。
    . 匹配除“ ”“ ”之外的任何单个字符。要匹配包括“ ”“ ”在内的任何字符,请使用像“(.| | )”的模式。
    (pattern) 匹配pattern并获取这一匹配的子字符串。该子字符串用于向后引用。所获取的匹配可以从产生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中则使用$0…$9属性。要匹配圆括号字符,请使用“(”或“)”。可带数量后缀。
    (?:pattern) 匹配pattern但不获取匹配的子字符串(shy groups),也就是说这是一个非获取匹配,不存储匹配的子字符串用于向后引用。这在使用或字符“(|)”来组合一个模式的各个部分是很有用。例如“industr(?:y|ies)”就是一个比“industry|industries”更简略的表达式。
    (?=pattern) 正向肯定预查(look ahead positive assert),在任何匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,“Windows(?=95|98|NT|2000)”能匹配“Windows2000”中的“Windows”,但不能匹配“Windows3.1”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
    (?!pattern) 正向否定预查(negative assert),在任何不匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如“Windows(?!95|98|NT|2000)”能匹配“Windows3.1”中的“Windows”,但不能匹配“Windows2000”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始
    (?<=pattern) 反向(look behind)肯定预查,与正向肯定预查类似,只是方向相反。例如,“(?<=95|98|NT|2000)Windows”能匹配“2000Windows”中的“Windows”,但不能匹配“3.1Windows”中的“Windows”。
    (?<!pattern) 反向否定预查,与正向否定预查类似,只是方向相反。例如“(?<!95|98|NT|2000)Windows”能匹配“3.1Windows”中的“Windows”,但不能匹配“2000Windows”中的“Windows”。
    x|y 没有包围在()里,其范围是整个正则表达式。例如,“z|food”能匹配“z”或“food”。“(?:z|f)ood”则匹配“zood”或“food”。
    [xyz] 字符集合(character class)。匹配所包含的任意一个字符。例如,“[abc]”可以匹配“plain”中的“a”。特殊字符仅有反斜线保持特殊含义,用于转义字符。其它特殊字符如星号、加号、各种括号等均作为普通字符。脱字符^如果出现在首位则表示负值字符集合;如果出现在字符串中间就仅作为普通字符。连字符 - 如果出现在字符串中间表示字符范围描述;如果如果出现在首位(或末尾)则仅作为普通字符。右方括号应转义出现,也可以作为首位字符出现。
    [^xyz] 排除型字符集合(negated character classes)。匹配未列出的任意字符。例如,“[^abc]”可以匹配“plain”中的“plain”。
    [a-z] 字符范围。匹配指定范围内的任意字符。例如,“[a-z]”可以匹配“a”到“z”范围内的任意小写字母字符。
    [^a-z] 排除型的字符范围。匹配任何不在指定范围内的任意字符。例如,“[^a-z]”可以匹配任何不在“a”到“z”范围内的任意字符。
    [:name:] 增加命名字符类(named character class[注 1]中的字符到表达式。只能用于方括号表达式
    [=elt=] 增加当前locale下排序(collate)等价于字符“elt”的元素。例如,[=a=]可能会增加ä、á、à、ă、ắ、ằ、ẵ、ẳ、â、ấ、ầ、ẫ、ẩ、ǎ、å、ǻ、ä、ǟ、ã、ȧ、ǡ、ą、ā、ả、ȁ、ȃ、ạ、ặ、ậ、ḁ、ⱥ、ᶏ、ɐ、ɑ 。只能用于方括号表达式。
    [.elt.] 增加排序元素(collation elementelt到表达式中。这是因为某些排序元素由多个字符组成。例如,29个字母表的西班牙语, "CH"作为单个字母排在字母C之后,因此会产生如此排序“cinco, credo, chispa”。只能用于方括号表达式。
     匹配一个单词边界,也就是指单词和空格间的位置。例如,“er”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”。
    B 匹配非单词边界。“erB”能匹配“verb”中的“er”,但不能匹配“never”中的“er”。
    cx 匹配由x指明的控制字符。x的值必须为A-Za-z之一。否则,将c视为一个原义的“c”字符。控制字符的值等于x的值最低5比特(即对3210进制的余数)。例如,cM匹配一个Control-M或回车符。ca等效于u0001, cb等效于u0002, 等等...
    d 匹配一个数字字符。等价于[0-9]。注意Unicode正则表达式会匹配全角数字字符。
    D 匹配一个非数字字符。等价于[^0-9]。
    f 匹配一个换页符。等价于x0c和cL。
    匹配一个换行符。等价于x0a和cJ。
    匹配一个回车符。等价于x0d和cM。
    s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于[ f v]。注意Unicode正则表达式会匹配全角空格符。
    S 匹配任何非空白字符。等价于[^ f v]。
    匹配一个制表符。等价于x09和cI。
    v 匹配一个垂直制表符。等价于x0b和cK。
    w 匹配包括下划线的任何单词字符。等价于“[A-Za-z0-9_]”。注意Unicode正则表达式会匹配中文字符。
    W 匹配任何非单词字符。等价于“[^A-Za-z0-9_]”。
    xnn 十六进制转义字符序列。匹配两个十六进制数字nn表示的字符。例如,“x41”匹配“A”。“x041”则等价于“x04&1”。正则表达式中可以使用ASCII编码。.
    num 向后引用(back-reference)一个子字符串(substring),该子字符串与正则表达式的第num个用括号围起来的捕捉群(capture group)子表达式(subexpression)匹配。其中num是从1开始的十进制正整数,其上限可能是9[注 2]、31[注 3]、99甚至无限[注 4]。例如:“(.)1”匹配两个连续的相同字符。
    n 标识一个八进制转义值或一个向后引用。如果n之前至少n个获取的子表达式,则n为向后引用。否则,如果n为八进制数字(0-7),则n为一个八进制转义值。
    nm 3位八进制数字,标识一个八进制转义值或一个向后引用。如果nm之前至少有nm个获得子表达式,则nm为向后引用。如果nm之前至少有n个获取,则n为一个后跟文字m的向后引用。如果前面的条件都不满足,若nm均为八进制数字(0-7),则nm将匹配八进制转义值nm
    nml 如果n为八进制数字(0-3),且m和l均为八进制数字(0-7),则匹配八进制转义值nml。
    un Unicode转义字符序列。其中n是一个用四个十六进制数字表示的Unicode字符。例如,u00A9匹配版权符号(©)。

    (来自维基百科)

     

     

     

    你能看到这里

     

    也是

     

     

     

    不知道你看懵逼了没?

     

    反正我是不想看了

     

    接下来

     

    才是干货

     

     

    小帅b就给你精简一下

     

    通俗的把最常用的匹配告诉你

     

    字符描述
    d 代表任意数字,就是阿拉伯数字 0-9 这些玩意。
    D 大写的就是和小写的唱反调,d 你代表的是任意数字是吧?那么我 D 就代表不是数字的。
    w 代表字母,数字,下划线。也就是 a-z、A-Z、0-9、_。
    W 跟 w 唱反调,代表不是字母,不是数字,不是下划线的。
    代表一个换行。
    代表一个回车。
    f 代表换页。
    代表一个 Tab 。
    s 代表所有的空白字符,也就是上面这个: 、 、 、f。
    S

    跟 s 唱反调,代表所有不是空白的字符。

    A 代表字符串的开始。
     代表字符串的结束。
    ^ 匹配字符串开始的位置。
    $ 匹配字符创结束的位置。
    . 代表所有的单个字符,除了
    [...] 代表在 [] 范围内的字符,比如 [a-z] 就代表 a到z的字母
    [^...] 跟 [...] 唱反调,代表不在 [] 范围内的字符
    {n} 匹配在 {n} 前面的东西,比如: o{2} 不能匹配 Bob 中的 o ,但是能匹配 food 中的两个o。
    {n,m} 匹配在 {n,m} 前面的东西,比如:o{1,3} 将匹配“fooooood”中的前三个o。
    {n,} 匹配在 {n,} 前面的东西,比如:o{2,} 不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o。
    * 和 {0,} 一个样,匹配 * 前面的 0 次或多次。 比如 zo* 能匹配“z”、“zo”以及“zoo”。
    + 和{1,} 一个样,匹配 + 前面 1 次或多次。 比如 zo+”能匹配“zo”以及“zoo”,但不能匹配“z”。
    和{0,1} 一个样,匹配 ?前面 0 次或 1 次。
    a|b 匹配 a 或者 b。
    () 匹配括号里面的内容。

     

     

    ok

     

    知道了这些之后

     

    我们怎么用 python 来进行判断呢?

     

    那就要使用到 python 的库了

     

    它就是

     

    re

     

    接下来我们就来使用 re 模块

     

    对其常用的方法

     

    来使用正则表达式

     

    re.match

     

    使用这个方法

     

    主要传入两个参数

     

    第一个就是我们的匹配规则

     

    第二个就是需要被过滤的内容

     

    例如

     

    我们想要从这

     

    Xiaoshuaib has 100 bananas

     

    拿到一个数字

     

    那么我们就可以这样

     

    import re

    content = 'Xiaoshuaib has 100 bananas'
    res = re.match('^Xi.*(d+)s.*s$',content)
    print(res.group(1))

     

    通过我们刚刚说的匹配符号

     

    可以定义出相应的匹配规则

     

    在这里我们将我们需要的目标内容用 () 括起来

     

    此刻我们获得结果是

     

    0

     

    那么如果我们想要 100 这个数字呢?

     

    可以这样

     

    import re

    content = 'Xiaoshuaib has 100 bananas'
    res = re.match('^Xi.*?(d+)s.*s$',content)
    print(res.group(1))

     

    看出区别了么

     

    第二段代码我们多了一个 ?符号

     

    在这里呢

     

    涉及到两个概念

     

    一个是

     

    贪婪匹配

     

    另一个是

     

    非贪婪匹配

     

    所谓贪婪匹配

     

    就是我们的第一段代码

     

    一个数一个数都要去匹配

     

    而非贪婪呢

     

    我们是直接把 100 给匹配出来了

     

     

     

    刚刚我们用到的

     

    .*?

     

    是我们在匹配过程中最常使用到的

     

    表示的就是匹配任意字符

     

    但是

     

    .*?的 . 代表所有的单个字符,除了

     

    如果我们的字符串有换行了

     

    怎么办呢?

     

    比如这样

     

    content = """Xiaoshuaib has 100 
    bananas"""

     

    那么我们就需要用到 re 的匹配模式了

     

    说来也简单

     

    直接用 re.S 就可以了

     

    import re

    content = """Xiaoshuaib has 100
    bananas"""
    res = re.match('^Xi.*?(d+)s.*s$',content,re.S)
    print(res.group(1))

     

    可能有些朋友会觉得

     

    匹配一个东西还要写开头结尾

     

    有点麻烦

     

    那么就可以使用 re 的另一个方法了

     

     

    re.search

     

     

    它会直接去扫描字符串

     

    然后把匹配成功的第一个结果的返回给你

     

    import re

    content = """Xiaoshuaib has 100
    bananas"""
    res = re.search('Xi.*?(d+)s.*s',content,re.S)
    print(res.group(1))

     

    这样子也是可以获取 100 的

     

    但是如果我们的内容是这样的

     

    content = """Xiaoshuaib has 100 bananas;
    Xiaoshuaib has 100 bananas;
    Xiaoshuaib has 100 bananas;
    Xiaoshuaib has 100 bananas;"""

     

    想要获取所有的 100 呢?

     

    这时候就要用到 re 的另一个方法了

     

     

    re.findall

     

    通过它我们就能轻松的获取所有匹配的内容了

     

    import re

    content = """Xiaoshuaib has 100 bananas;
    Xiaoshuaib has 100 bananas;
    Xiaoshuaib has 100 bananas;
    Xiaoshuaib has 100 bananas;"""
    res = re.findall('Xi.*?(d+)s.*?s;',content,re.S)
    print(res)

     

    这里的结果是

     

    ['100', '100', '100', '100']

     

     

    又有朋友觉得

     

    如果我们想直接替换匹配的内容呢

     

    就比如刚刚的字符串

     

    可不可以把 100 直接替换成 250 呢?

     

     

    那就要用到 re 的另一个方法了

     

     

    re.sub

     

     

    可以这样

     

    import re

    content = """Xiaoshuaib has 100 bananas;
    Xiaoshuaib has 100 bananas;
    Xiaoshuaib has 100 bananas;
    Xiaoshuaib has 100 bananas;"""
    content = re.sub('d+','250',content)
    print(content)

     

    那么结果就变成了

     

    Xiaoshuaib has 250 bananas;

    Xiaoshuaib has 250 bananas;

    Xiaoshuaib has 250 bananas;

    Xiaoshuaib has 250 bananas;

     

    250 个香蕉

     

    吃....得完么??

     

     

     

    再来说说 re 的另一个常用到的方法吧

     

     

    re.compile

     

     

    这个主要就是把我们的匹配符封装一下

     

    import re

    content = "Xiaoshuaib has 100 bananas"
    pattern = re.compile('Xi.*?(d+)s.*s',re.S)
    res = re.match(pattern,content)

    print(res.group(1))

     

     

    其实和我们之前写的一样的

     

    res = re.match('^Xi.*?(d+)s.*s$',content,re.S)

     

    只不过 compile 一下

     

    便于以后复用

     

    好了 

     

    关于 re 模块和正则表达式就介绍完啦

     

     

    知道了怎么请求数据

     

    也知道了将返回的数据如何正则过滤

     

    那么

     

    爬虫对我们来说还难么?

     

     

     

    这次本篇真的完啦

     

     

    再见

     

     

    扫一扫

    学习 Python 没烦恼

     

     

     

    近期文章

     

    python爬虫03:那个叫做 Urllib 的库让我们的 python 假装是浏览器

     

    python爬虫04|长江后浪推前浪,Requests 库把 urllib 库拍在沙滩上

     

    对不起,我不应该出轨的!

     

     

     

     

     

     

     

    也不知道为什么

    你点了好看之后

    你变得更好看了

  • 相关阅读:
    函数
    字符编码转换
    文件读写与修改
    Java期末项目——校园商铺平台(三)
    Java期末项目——校园商铺平台(二)
    Java期末项目——校园商铺平台(一)
    LDAP & Implementation
    RESTful Levels HATEOAS
    隔离级别
    Servlet CDI Analysis
  • 原文地址:https://www.cnblogs.com/fxxkpython/p/10832017.html
Copyright © 2011-2022 走看看