PYTHON爬虫基础(二)-正则

发布于 2023-06-18  340 次阅读


  • 正则就是一组由字母和符号组成的特殊文本,它可以用来从文本中找出满足你想要的格式的句子,在爬虫中,我们需要使用正则在requests得到的响应中提取出网页源码中有用的内容.
  • 感觉beautifulsoup库的作用和正则差不多,相当于以函数的形式包装了正则语句吧

正则函数

  • 'stri\ng'和r'stri\ng'的区别是后者加了r去除了转义字符,前者相当于"stri换行g",后者是"stri\ng".正则中会使用\,为了防止转义,正则匹配表达式(pattern)都要加r前缀

  • text:需要得到处理的字符串

  • pattern:正则匹配表达式,即后面的匹配模式

  • flags:标志位,结合正则常量用于控制正则表达式的匹配方式

单次匹配

  • re.search(pattern,text,flag): 查找任意位置的匹配项,在第一次找到之后就停止匹配,在单次匹配中,匹配结果会返回对象,如果没有匹配到会返回None对象,如果匹配到则可以使用group()函数来获取得到的匹配字符串.
import re

text="miaowumiao"
pattern=r"miao"

result=re.search(pattern,text)

if result==None:
    print("匹配失败")
else:
    print("search:"+result.group())

import re

text="miaowumiao"
pattern=r"miao1"

result=re.search(pattern,text)

if result==None:
    print("匹配失败")
else:
    print("search:"+result.group())

  • 注意,search没有groups()函数,使用会报错,获取整体匹配结果只能使用group(),如果匹配模式中使用了括号来拆分了匹配部分,那么可以使用group(n)来单独匹配每个部分
  • group()或者group(0)表示匹配结果整体,group(n)表示pattern中第n个括号中被匹配到的内容,在后面的匹配模式板块也会提到.
import re

text="miaowumiao"
pattern=r"(mi)(ao)"

#匹配字符串miao,但是mi作为第一部分,ao作为第二部分
#所以group()和group(0)得到miao,group(1)得到mi,group(2)得到ao

result=re.search(pattern,text,re.DOTALL)

if result==None:
    print("匹配失败")
else:
    print("search:"+result.group(2))

多次匹配

  • re.findall(pattern,text,flag): 从字符串任意位置查找,返回一个列表,所有匹配结果都将存于列表中
import re

text="miaowumiao"
pattern=r"miao"

results=re.findall(pattern,text)

if results==None:
    print("匹配失败")
else:
    i=0
    for result in results:
        i=i+1
        print("第%d次匹配:"%i,result)

正则替换

  • re.sub(pattern, repl, text, flag) :repl替换掉text中被pattern匹配的字符,flag表示正则常量。
import re

repl="wang"
text="miaowumiao"
pattern=r"miao"

text=re.sub(pattern,repl,text)#注意转换后要赋值回去
print(text)

  • 和正则匹配不同,在正则替换中即使匹配不到也不用做None的检查了
import re

repl="wang"
text="miaowumiao"
pattern=r"miao1"

text=re.sub(pattern,repl,text)
print(text)

正则分割

  • re.split(pattern, string,flag)函数:用 pattern 分开 string ,maxsplit表示最多进行分割次数,flag表示后面的正则常量,返回一个数组,匹配结果不包括pattern
import re

text="miaowumiao"
pattern=r"i"

texts=re.split(pattern,text)
print(texts)

正则常量

  • 正则常量使用于正则函数的flag的位置,用于完善正则匹配模式(虽然正则表达式也可以实现相同功能,但是正则常量会简单一些),比如在MIAOWUmiao中同时匹配出miao和MIAO,我们可以使用正则常量re.IGNORECASE来忽略大小写进行匹配

  • 正则常量在所有正则函数中的使用方法是一样的,下面只使用findall函数来举例

IGNORECASE

  • 正则中pattern和text是大小写敏感的,这个常量用于忽略text内容的大小写
  • 使用前
import re

text="miaowuMIAO"
pattern=r"miao"

results=re.findall(pattern,text)

if results==None:
    print("匹配失败")
else:
    i=0
    for result in results:
        i=i+1
        print("第%d次匹配:"%i,result)

(只能匹配到第一个miao)

  • 使用后
import re

text="miaowuMIAO"
pattern=r"miao"

results=re.findall(pattern,text,re.IGNORECASE)

if results==None:
    print("匹配失败")
else:
    i=0
    for result in results:
        i=i+1
        print("第%d次匹配:"%i,result)

(大小写都匹配成功)

ASCII

  • re.ASCII常量让 \w, \W, \b, \B, \d, \D, \s 和\S只匹配ASCII,而不是整个字符串(比如汉字),后面会提到这些匹配的意思
  • 比如r"\w+"是匹配连续的长字符串,那么r"\w+"匹配"miaowumiao666"啊就会得到"miaowumiao"啊,使用re.ASCII仅能匹配到"miaowumiao"
import re

text="miaowumiao啊"
pattern=r"\w+"

results=re.findall(pattern,text)

if results==None:
    print("匹配失败")
else:
    i=0
    for result in results:
        i=i+1
        print("第%d次匹配:"%i,result)

(能匹配到unicode)

import re

text="miaowumiao啊"
pattern=r"\w+"

results=re.findall(pattern,text,re.ASCII)

if results==None:
    print("匹配失败")
else:
    i=0
    for result in results:
        i=i+1
        print("第%d次匹配:"%i,result)

(中文不是ascii,匹配不到)

DOTALL

  • 正则匹配默认不能匹配一些特殊符号,比如换行符\n我们可以使用正则常量DOTALL来各种特殊符号(实际测试下,我的环境默认是允许多行匹配和匹配所有的🤣)
import re

text="miao\nwu\nmiao"
pattern=r"\n"

results=re.findall(pattern,text,re.DOTALL)

if results==None:
    print("匹配失败")
else:
    i=0
    for result in results:
        i=i+1
        print("第%d次匹配:"%i,result)

MULTILINE

  • 正则匹配在使用^模式匹配(匹配文本开头)时是不能匹配多行文本的,当text有多行且我们需要匹配开头时,我们可以使用正则常量MULTILINE来匹配多行
import re

text="miao\nwu\nmiao"
pattern=r"^miao"

results=re.findall(pattern,text)

if results==None:
    print("匹配失败")
else:
    i=0
    for result in results:
        i=i+1
        print("第%d次匹配:"%i,result)

(只匹配到第一行的)

import re

text="miao\nwu\nmiao"
pattern=r"miao"

results=re.findall(pattern,text,re.MULTILINE)

if results==None:
    print("匹配失败")
else:
    i=0
    for result in results:
        i=i+1
        print("第%d次匹配:"%i,result)

(两行都匹配到了)

匹配模式

基础概念

  • 为了满足能够在任何复杂情况下,正则表达式都能随心所欲的匹配到任意想要的内容,整个匹配模式的知识点复杂繁多,这里只列举一些常用的模板,并在结尾贴出所有模式语法中的特殊元素.
  • 注意,针对某种特殊句子的正则表达式通常不是唯一的,比如想要匹配出一段文本中的全部ip地址(数字.数字.数字.数字)以下两种匹配模式都是可以的,显然后者会简单很多
(?<![\.\d])(([1-9]?\d|1\d\d|2[0-4]\d|25[0-5])\.){3}([1-9]?\d|1\d\d|2[0-4]\d|25[0-5])(?![\.\d])
\d+.\d+.\d+.\d+
  • 当想要验证正则表达式语法是否正确,或者能否成功匹配到目标,可以使用这个网站https://regex101.com/
  • 将正则表达式放在regular expression里面,将代匹配的文本放在test string里面,匹配信息会出现在右边栏的match information里面

  • 同时,如果正则表达式中使用了括号分组,匹配到的每组的信息也会以group(n)的形式呈现

常用语法

  • 单个字符即表示单个字符本身(特殊字符除外),比如可以用miao匹配miao

  • 字符类,可以单独使用,代替了一类字符

.   匹配除 "\n" 之外的任何单个字符。
\d  匹配一个数字字符。
\D  匹配一个非数字字符。
\s  匹配任何空白字符,包括空格、制表符、换页符等等。
\S  匹配任何非空白字符。
\w  匹配包括下划线的任何字符(数字也匹配)。
\W  匹配任何非单词字符(数字也不匹配)。
  • 模式类,不能单独使用,要配合字符类使用
^   匹配字符串的开头
$   匹配字符串的末尾。
*   重复*前面表达式0次或多次。
re+ 重复+前面表达式1次或多次。
re? 重复?前面表达式0次或1次。
(re)    对正则表达式分组并记住匹配的文本
  • 正则表达式多个部分之间连接不需要任何符号,比如从曜的连招"33a213a"中匹配出以数字字母开头,字符结尾的两位字符的匹配只需要\D\d,得到"a2"

  • 如果匹配成功后需要单独取出a和2才需要加括号变为(\D)(\d)

常用模板-固定格式

  • 如果是匹配固定格式内容,可以使用上面常用语法提到的常用字符类+常用模式类+正则常量的组合,比如\d表示数字,+表示重复一次或多次,所以\d+表示多个数字,\S+表示字符串.
  • 举个例子,从以下文本中匹配出所有的形如"miao:127.0.0.1"的字符串的正则表达式可以写成
\S+:\d+.\d+.\d+.\d+
 Automatically created by: scrapy startproject
登录用户miao:192.168.1.2
 For more information about the [deploy] section see:
登录用户wang:127.0.0.1
 https://scrapyd.readthedocs.io/en/latest/deploy.html

  • 但是这种情况下,会把不想要的登录用户字符也带进去,那么直接用前面提到的re.ASCII正则常量,即可实现忽略掉中文

常用模板-固定开头结尾

  • 有时,要匹配的内容不是固定的,但是它们有固定的开头结尾,就可以使用"开头标志(.)结尾标志"来匹配内容,开头标志和原文标志一致,结尾标志和原文一致,中间匹配到的内容用(.*)匹配,.表示单个任意字符,*表示重复0次或多次,(.\)就表示了开头结尾之间可能存在的所有内容
  • 举个例子,从以下文本中匹配出所有:之后的内容的正则表达式可以写成
r":(.*)\n"
这个错误提示 'gbk' codec can't decode byte 0xa6 in position 4: illegal multibyte sequence 表示在使用 'gbk' 编码解码时,遇到了非法的多字节序列。情感分析结果是:负向 把握99.213300% 负向程度99.646000%
情感分析结果是:中性
这个错误通常发生在尝试使用错误的编码方式解码字符串时。可能是因为字符串的实际编码与你指定的 'gbk' 编码方式不匹配导致的。情感分析结果是:负向 把握99.792700% 负向程度99.906700%
情感分析结果是:中性
要解决这个问题,可以尝试以下几种方法:情感分析结果是:负向 把握44.274400% 负向程度74.923500%
情感分析结果是:中性
确认使用正确的编码方式:请检查字符串的实际编码方式,确保与你指定的 'gbk' 编码方式相匹配。情感分析结果是:负向 把握53.611100% 负向程度79.125000%
情感分析结果是:中性
使用正确的编码方式进行解码:如果你确认字符串采用了其他编码方式,可以尝试使用正确的编码方式进行解码,例如 'utf-8'。情感分析结果是:中性
情感分析结果是:中性
python情感分析结果是:正向 把握92.343800% 正向程度96.554700%
string = b'\xa6'情感分析结果是:正向 把握78.493500% 正向程度90.322100%
decoded_string = string.decode('utf-8')情感分析结果是:正向 把握63.117700% 正向程度83.403000%
print(decoded_string)情感分析结果是:正向 把握73.309300% 正向程度87.989200%
输出结果会根据实际的编码方式而有所不同,这里使用 'utf-8' 编码仅作为示例。情感分析结果是:正向 把握59.112600% 正向程度81.600700%
情感分析结果是:中性
忽略错误的字节:如果不确定字符串的编码方式或无法更改编码方式,可以尝试在解码过程中忽略错误的字节。情感分析结果是:负向 把握95.517700% 负向程度97.983000%
情感分析结果是:中性
python情感分析结果是:正向 把握92.343800% 正向程度96.554700%
string = b'\xa6'情感分析结果是:正向 把握78.493500% 正向程度90.322100%
decoded_string = string.decode('gbk', errors='ignore')情感分析结果是:正向 把握38.152600% 正向程度72.168700%
print(decoded_string)情感分析结果是:正向 把握73.309300% 正向程度87.989200%
这样做会忽略无法解码的字节,但也可能导致输出结果不完整或含有乱码。情感分析结果是:负向 把握99.634300% 负向程度99.835400%
情感分析结果是:中性
请根据具体情况选择适合的方法来解决该错误。如果问题仍然存在,请提供更多的上下文信息,以便更好地帮助你解决问题。情感分析结果是:负向 把握67.160000% 负向程度85.222000%
import re

text=open("666.txt").read()
pattern=r":(.*)\n"

results=re.findall(pattern,text)

if results==None:
    print("匹配失败")
else:
    i=0
    for result in results:
        i=i+1
        print("第%d次匹配:"%i,result)

  • 本来到这里就结束了,但是在jwt脚本的编写时出了一点问题,导致贪婪匹配的问题暴露了出来
  • ==比如使用r"a(.*)b"在匹配以下内容时就会两种情况,匹配到以a开头,b结尾的"a66b",或者匹配到以a开头,b结尾的"a66b66b",因为(.*)是贪婪匹配,所以匹配到的是后者.==
  • 在这个模板的常见使用情况下,一般都是要匹配最短的,就得去除贪婪模式,所以应该使用==r"a(.*?)b"==

表达式大全

  • 这两种模板已经可以解决很多情况下的匹配了,在面对某些难以解决的情况还得加其他的表达式,这里贴一份
模式 描述
^ 匹配字符串的开头
$ 匹配字符串的末尾。
. 匹配任意字符,除了换行符,当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 次由前面的正则表达式定义的片段,贪婪方式
a| b 匹配a或b
(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个分组的内容,如果它经匹配。否则指的是八进制字符码的表达式。
字符 描述
python 匹配 "python".
字符类 描述
[Pp]ython 匹配 "Python" 或 "python"
rub[ye] 匹配 "ruby" 或 "rube"
[aeiou] 匹配中括号内的任意一个字母
[0-9] 匹配任何数字。类似于 [0123456789]
[a-z] 匹配任何小写字母
[A-Z] 匹配任何大写字母
[a-zA-Z0-9] 匹配任何字母及数字
[^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_]'。
届ける言葉を今は育ててる
最后更新于 2024-02-07