ホームページ  >  記事  >  バックエンド開発  >  クローラーの解析方法 3: 正規表現

クローラーの解析方法 3: 正規表現

爱喝马黛茶的安东尼
爱喝马黛茶的安东尼転載
2019-06-05 14:06:043271ブラウズ

众多语言都能进行爬虫,但基于python的爬虫显得更加简洁,方便。爬虫也成了python语言中必不可少的一部分。爬虫的解析方式也是多种多样。

上一篇给大家讲解的是爬虫的解析方式二:Beautifulsoup,今天给带给大家的是正则表达式。

クローラーの解析方法 3: 正規表現

正则表达式

正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。就是 事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符”,这个“规则字符” 来表达对字符的一种过滤逻辑。

正则并不是python独有的,其他语言也都有正则。

python中的正则,封装了re模块

Python中常用的正则表达式处理函数

re.match函数

re.match 尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。

函数语法:

re.match(pattern, string, flags=0)

函数参数说明:

参数                                                         描述

pattern                                           匹配的正则表达式

string                                              要匹配的字符串。

flags                        标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。

匹配成功re.match方法返回一个匹配的对象,否则返回None。

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

匹配对象方法                                                              描述

group(num=0)                                      匹配的整个表达式的字符串,group() 可以一次输入多个组号,

                                                              在这种情况下它将返回一个包含那些组所对应值的元组。

groups()                                                返回一个包含所有小组字符串的元组,从 1 到 所含的小组号。

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

以上实例运行输出结果为:

(0, 3)
None
import re
content = "Cats are smarter than dogs"
result = re.match( r'(.*) are (.*?) .*', content)
print(result.group())
print(result.group(1))
print(result.group(2))

以上实例执行结果如下:

Cats are smarter than dogs
Cats
smarter
result.group()获取匹配的结果
result.span()获去匹配字符串的长度范围

泛匹配

其实相对来说上面的方式并不是非常方便,其实可以将上述的正则规则进行更改

import re
content = "Cats are smarter than dogs"
result = re.match( r'Cats.*dogs$', content)
print(result)
print(result.group())
print(result.span())

匹配目标

如果为了匹配字符串中具体的目标,则需要通过()括起来,例子如下:

import re
content = "Cats are 1234567 smarter than dogs"
result = re.match( r'(.*)\sare\s(\d+)\s(.*?)\s.*', content) #\s匹配空格符 \d+匹配数字
print(result.group())
print(result.group(1))
print(result.group(2))

以下为执行结果:

Cats are smarter than dogs

Cats

1234567

贪婪匹配

先看下面代码:

import re
content = "Cats are 1234567 smarter than dogs"
result = re.match( r'Cats.*(\d+).*dogs', content) 
print(result.group())
print(result.group(1))

 

从结果中可以看出只匹配到了7,并没有匹配到1234567,出现这种情况的原因是前面的.* 给匹配掉了, .*在这里会尽可能的匹配多的内容,也就是我们所说的贪婪匹配,

如果我们想要匹配到1234567则需要将正则表达式改为:

result = re.match( r'Cats.*?(\d+).*dogs', content) 

这样结果就可以匹配到1234567

匹配模式

很多时候匹配的内容是存在换行的问题的,这个时候的就需要用到匹配模式re.S来匹配换行的内容

import re
content = """Cats are 1234567 smarter than dogs
dogs are wangwangwang"""
result = re.match( r'Cats.*(\d+).*wangwangwang', content,re.S) 
print(result.group())
print(result.group(1))

转义

当我们要匹配的内容中存在特殊字符的时候,就需要用到转移符号\,例子如下:

import re
content= "price is $5.00"
result = re.match('price is \$5\.00',content)
print(result.group())

注意:

对上面的一个小结:

尽量使用泛匹配,使用括号得到匹配目标,尽量使用非贪婪模式,有换行符就用re.S

强调re.match是从字符串的起始位置匹配一个模式

re.search方法

re.search 扫描整个字符串并返回第一个成功的匹配。

函数语法:

re.search(pattern, string, flags=0)

函数参数说明:

参数                                           描述

pattern                            匹配的正则表达式

string                              要匹配的字符串。

flags                                标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。

匹配成功re.search方法返回一个匹配的对象,否则返回None。

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

匹配对象方法                                                               描述

group(num=0)                          匹配的整个表达式的字符串,group() 可以一次输入多个组号,

                                                  在这种情况下它将返回一个包含那些组所对应值的元组。

groups()                                     返回一个包含所有小组字符串的元组,从 1 到 所含的小组号。

import re
content = "extra things hello 123455 world_this is a Re Extra things"
result = re.search("hello.*?(\d+).*?Re",content)
print(result.group())
print(result.group(1)

其实这个时候我们就不需要在写^以及$,因为search是扫描整个字符串

注意:所以为了匹配方便,我们会更多的用search,不用match,match必须匹配头部,所以很多时候不是特别方

re.match与re.search的区别

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

html = &#39;&#39;&#39;<div id="songs-list">
    <h2 class="title">经典老歌</h2>
    <p class="introduction">
        经典老歌列表
    </p>
    <ul id="list" class="list-group">
        <li data-view="2">一路上有你</li>
        <li data-view="7">
            <a href="/2.mp3" singer="任贤齐">沧海一声笑</a>
        </li>
        <li data-view="4" class="active">
            <a href="/3.mp3" singer="齐秦">往事随风</a>
        </li>
        <li data-view="6"><a href="/4.mp3" singer="beyond">光辉岁月</a></li>
        <li data-view="5"><a href="/5.mp3" singer="陈慧琳">记事本</a></li>
        <li data-view="5">
            <a href="/6.mp3" singer="邓丽君">但愿人长久</a>
        </li>
    </ul>
</div>&#39;&#39;&#39;
import re
 
result = re.search(&#39;<li.*?active.*?singer="(.*?)">(.*?)</a>&#39;,html,re.S)
print(result.group(1), result.group(2))

观察到

    节点里面有许多
  • 节点,其中
  • 节点有的包含节点,有的不包含节点,节点还有一些相应的属性,超链接和歌手名。

    首先我们尝试提取class为active的

  • 节点内部的超链接包含的歌手名和歌名。

    所以我们需要提取第三个

  • 节点下的节点的singer属性和文本。

    所以正则表达式可以以

  • 开头,然后接下来寻找一个标志符active,中间的部分可以用.*?来匹配,然后接下来我们要提取singer这个属性值,所以还需要写入singer="(.*?)",我们需要提取的部分用小括号括起来,以便于用group()方法提取出来,它的两侧边界是双引号,然后接下来还需要匹配节点的文本,那么它的左边界是>,右边界是,所以我们指定一下左右边界,然后目标内容依然用(.*?)来匹配,所以最后的正则表达式就变成了
  • (.*?)',然后我们再调用search()方法,它便会搜索整个HTML文本,找到符合正则表达式的第一个内容返回。

    另外由于代码有换行,所以这里第三个参数需要传入re.S

    注意:在上面两次匹配中,search()方法的第三个参数我们都加了re.S,使得.*?可以匹配换行,所以含有换行的

  • 节点被匹配到了,如果我们将其去掉,只会匹配到不换行的的内容

    re.findall

    搜索整个字符串然后返回匹配正则表达式的所有内容

    html = &#39;&#39;&#39;<div id="songs-list">
        <h2 class="title">经典老歌</h2>
        <p class="introduction">
            经典老歌列表
        </p>
        <ul id="list" class="list-group">
            <li data-view="2">一路上有你</li>
            <li data-view="7">
                <a href="/2.mp3" singer="任贤齐">沧海一声笑</a>
            </li>
            <li data-view="4" class="active">
                <a href="/3.mp3" singer="齐秦">往事随风</a>
            </li>
            <li data-view="6"><a href="/4.mp3" singer="beyond">光辉岁月</a></li>
            <li data-view="5"><a href="/5.mp3" singer="陈慧琳">记事本</a></li>
            <li data-view="5">
                <a href="/6.mp3" singer="邓丽君">但愿人长久</a>
            </li>
        </ul>
    </div>&#39;&#39;&#39;
    import re
    results = re.findall(&#39;<li.*?href="/(.*?)".*?singer="(.*?)">(.*?)</a>&#39;, html, re.S)
    for result in results:
        print(result)
        print(result[0], result[1], result[2])

    运行结果:

    ('2.mp3', '任贤齐', '沧海一声笑')

    2.mp3 任贤齐 沧海一声笑

    ('3.mp3', '齐秦', '往事随风')

    3.mp3 齐秦 往事随风

    ('4.mp3', 'beyond', '光辉岁月')

    4.mp3 beyond 光辉岁月

    ('5.mp3', '陈慧琳', '记事本')

    5.mp3 陈慧琳 记事本

    ('6.mp3', '邓丽君', '但愿人长久')

    6.mp3 邓丽君 但愿人长久

    results = re.findall(&#39;<li.*?>\s*?(<a.*?>)?(\w+)(</a>)?\s*?</li>&#39;,html,re.S)
    for result in results:
        #print(result)
        print(result[0], result[1], result[2])

    运行结果:

     一路上有你 

    沧海一声笑

    往事随风

    光辉岁月

    记事本

    但愿人长久

    \s*? 这种用法其实就是为了解决有的有换行,有的没有换行的问题

    ()? 这种用法是因为html中有的有a标签,有的没有的,?表示匹配一个或0个,正好可以用于匹配

    检索和替换

    Python 的re模块提供了re.sub用于替换字符串中的匹配项。

    语法:

    re.sub(pattern, repl, string, count=0)

    参数:

    pattern : 正则中的模式字符串。

    repl : 替换的字符串,也可为一个函数。

    string : 要被查找替换的原始字符串。

    count : 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配。

    import re
    phone = "2004-959-559 # 这是一个电话号码"
    # 删除注释
    num = re.sub(r&#39;#.*$&#39;, "", phone)
    print ("电话号码 : ", num)
     
    # 移除非数字的内容
    num = re.sub(r&#39;\D&#39;, "", phone)
    print ("电话号码 : ", num)

    在这里我们只需要在第一个参数传入\D来匹配所有的数字,然后第二个参数“”是替换成的字符串,要去掉的话就可以赋值为空,第三个参数phone就是原字符串。


    re.compile

    将正则表达式编译成正则表达式对象,方便复用该正则表达式

    import re
    content= "hello world fan"
     
    pattern =re.compile("hello.*fan",re.S)
     
    result1 = re.match(pattern,content)
    result2 = re.search(pattern,content)
    result3 = re.sub(pattern, &#39;&#39;, content)
    print(result1, result2, result3)

    compile()还可以传入修饰符,例如re.S等修饰符,这样在search()、findall()等方法中就不需要额外传了。所以compile()方法可以说是给正则表达式做了一层封装,以便于我们更好地复用。

    正则表达式修饰符 - 可选标志

    正则表达式可以包含一些可选标志修饰符来控制匹配的模式。修饰符被指定为一个可选的标志。多个标志可以通过按位 OR(|) 它们来指定。如 re.I | re.M 被设置成 I 和 M 标志:

    修饰符                                               描述

    re.I                                     使匹配对大小写不敏感

    re.L                                    做本地化识别(locale-aware)匹配

    re.M                                   多行匹配,影响 ^ 和 $

    re.S                                    使 . 匹配包括换行在内的所有字符

    re.U                                    根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B.

    re.X                                    该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。


    正则表达式模式

    模式字符串使用特殊的语法来表示一个正则表达式:

    字母和数字表示他们自身。一个正则表达式模式中的字母和数字匹配同样的字符串。

    多数字母和数字前加一个反斜杠时会拥有不同的含义。

    标点符号只有被转义时才匹配自身,否则它们表示特殊的含义。

    反斜杠本身需要使用反斜杠转义。

    由于正则表达式通常都包含反斜杠,所以你最好使用原始字符串来表示它们。模式元素(如 r'\t',等价于 \\t )匹配相应的特殊字符。

    下表列出了正则表达式模式语法中的特殊元素。如果你使用模式的同时提供了可选的标志参数,某些模式元素的含义会改变。

    模式                                                                描述

    ^                                                          匹配字符串的开头

    $ は文字列の末尾に一致します。

    」re.dotallフラグが指定されている場合、Newline文字を除くすべての文字に一致します。NewLine文字を含む任意のキャラクターと一致させることができます。

    [...] []:[^ABC]に文字がありません。A、B、cを除く文字と一致します。

    re* 0 個以上の式と一致します。

    これ1 つ以上の式と一致します。

    これ? ‐ ‐ ‐ ′ ‐ ‐ ‐ 前の正規表現で定義された 0 または 1 つのフラグメントと一致します。貪欲でない方法で

    re{ n} n 個前の式と完全に一致します。

    # re {n、m}#aまたはb

    #(re)g””””””””””””””””””””””” ” ” ” ” ” 正規表現には、i、m、または x の 3 つのオプションのフラグが含まれます。括弧内の領域にのみ影響します。

    (? -IMX) i、m、または X のオプション記号をオフにします。括弧内の領域にのみ影響します。

    #(?:re)括弧内のi、m、またはxのオプションフラグを使用

    (? - imx:re)コメント。

    (?= re) 正面方向が肯定されます。 ... で表される含まれる正規表現が成功し、現在の位置が正常に一致した場合、

    ’ using using ‐ use ‐ ‐ ‐ ‐‐ ‐‐‐ 現在位置が一致すれば成功。しかし、含まれている式が試行されると、マッチング エンジンはまったく改善されません。

    モードの残りの部分でも、定義の右側の定義を試行する必要があります。

    (?!再)定義を定義します。肯定的な定義文字とは反対に、文字列に含まれる式が一致しない場合に、

    (? & GT; Rely) の独立したマッチング モードをマッチングし、振り返りを保存します。

    \w一致する英数字

    \英数字以外の

    \s と一致します [\t\n\r\f] に相当する任意の空白文字と一致します。

    \S ‐ ‐ ‐ ‐ ‐ ‐ 空でない文字と一致します

    \d [0-9].

    \D に相当します。数値以外の任意の文字列に一致します。

    \ 一致する文字列の開始

    # \ Z 一致する文字列から終了まで 行の変更がある場合、変更前の終了文字列のみと一致します。 c

    \z 文字列

    \G 文字列の末尾と一致します。最後の一致が完了した位置と一致します。

    \b単語の境界、つまり単語とスペースの間の位置を一致させます。

    たとえば、「er \ b」は「never」の「er」と一致しますが、「VERB」の「er」とは一致しません。

    \B単語以外の境界と一致します。 「er\B」は「動詞」の「er」と一致しますが、「never」の「er」とは一致しません。

    \n、\t など 改行文字と一致します。タブ文字と一致します。待ってください

    \ 1 ... \ 9

    \ 10が一致している場合は、n番目のグループの内容に一致します。それ以外の場合は、8 進数の文字コードの表現を指します。


    #正規表現の例

    文字の一致

    #例

    #Character class##例 rubb[ye] 「ruby」または「rube」と一致します

    [aeiou]内の任意の文字と一致します。角括弧

    [0-9] 任意の数値と一致します。 [0123456789]
    # [a-z]小文字のレターに一致する

    # [a-z]小文字

    [a-z [A-z # [^AEIOU] AEIOU 文字以外のすべての文字

    [^0-9] 数字を除く一致する文字

    特殊文字クラス

    「\n」以外の単一文字と一致します。 「\n」を含む任意の文字と一致するには、「[.\n]」のようなパターンを使用します。

    \d数字と一致します。 [0-9]に相当します。

    \Dキャラクター。 [^0-9] に相当します。

    \s スペース、タブ、フォームフィードなどの空白文字と一致します。 [ \f\n\r\t\v] と同等。

    \S 空白以外の文字と一致します。 [^ \f\n\r\t\v] と同等。

    \w アンダースコアを含む任意の単語文字と一致します。 「[A-Za-z0-9_]」と同等。

    \W 任意の非単語文字と一致します。 「[^A-Za-z0-9_]」と同等。

以上がクローラーの解析方法 3: 正規表現の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はcsdn.netで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。