写爬虫,不会正则怎么行?
1、正則基礎1.1、基礎語法1.2、修飾符1.3、貪婪與懶惰2、正則進階2.1、捕獲分組2.2、零寬斷言2.3、條件匹配2.4、findall結語
導讀:正則在各語言中的使用是有差異的,本文以 Python 3 為基礎。本文主要講述的是正則的語法,對于 re 模塊不做過多描述,只會對一些特殊地方做提示。
很多人覺得正則很難,在我看來,這些人一定是沒有用心。其實正則很簡單,根據二八原則,我們只需要懂 20% 的內容就可以解決 80% 的問題了。我曾經有幾年幾乎每天都跟正則打交道,剛接手項目的時候我對正則也是一無所知,花半小時百度了一下,然后寫了幾個 demo,就開始正式接手了。三年多時間,我用到的正則鮮有超出我最初半小時百度到的知識的。
1、正則基礎
1.1、基礎語法
(1)常用元字符
| \b | 匹配單詞的開始或結束 |
| \d | 匹配數字 |
| \s | 匹配任意不可見字符(空格、換行符、制表符等),等價于[ \f\n\r\t\v]。 |
| \w | 匹配任意 Unicode 字符集,包括字母、數字、下劃線、漢字等 |
| . | 匹配除換行符(\n)以外的任意字符 |
| ^ 或 \A | 匹配字符串或行的起始位置 |
| $ 或 \Z | 匹配字符串或行的結束位置 |
(2)限定詞(又叫量詞)
| * | 重復零次或更多次 |
| + | 重復一次或更多次 |
| ? | 重復零次或一次 |
| {n} | 重復 n 次 |
| {n,} | 重復 n 次或更多次 |
| {n,m} | 重復 n 到 m 次 |
(3)常用反義詞
| \B | 匹配非單詞的開始或結束 |
| \D | 匹配非數字 |
| \S | 匹配任意可見字符, [^ \f\n\r\t\v] |
| \W | 匹配任意非 Unicode 字符集 |
| [^abc] | 除 a、b、c 以外的任意字符 |
(4)字符族
| [abc] | a、b 或 c |
| [^abc] | 除 a、b、c 以外的任意字符 |
| [a-zA-Z] | a 到 z 或 A 到 Z |
| [a-d[m-p]] | a 到 d 或 m 到 p,即 [a-dm-p](并集) |
| [a-z&&[def]] | d、e 或 f(交集) |
| [a-z&&[^bc]] | a 到 z,除了 b 和 c:[ad-z](減去) |
| [a-z&&[^m-p]] | a 到 z,減去 m 到 p:[a-lq-z](減去) |
以上便是正則的基礎內容,下面來寫兩個例子看下:
s?=?'123abc你好're.search('\d+',?s).group()
re.search('\w+',?s).group()
結果:
123123abc你好
是不是很簡單?
1.2、修飾符
修飾符在各語言中也是有差異的。
Python 中的修飾符:
| re.A | 匹配 ASCII字符類,影響 \w, \W, \b, \B, \d, \D |
| re.I | 忽略大小寫 |
| re.L | 做本地化識別匹配(這個極少極少使用) |
| re.M | 多行匹配,影響 和 |
| re.S | 使 . 匹配包括換行符(\n)在內的所有字符 |
| re.U | 匹配 Unicode 字符集。與 re.A 相對,這是默認設置 |
| re.X | 忽略空格和 # 后面的注釋以獲得看起來更易懂的正則。 |
(1)re.A
修飾符 A 使 \w 只匹配 ASCII 字符,\W 匹配非 ASCII 字符。
s?=?'123abc你好're.search('\w+',?s,?re.A).group()
re.search('\W+',?s,?re.A).group()
結果:
123abc你好
但是描述中還有 \d 和 \D,數字不都是 ASCII 字符嗎?這是什么意思?別忘了,還有 全角和半角!
s?=?'0123456789'????re.search('\d+',?s,?re.U).group()
結果:
0123456789(2)re.M
多行匹配的模式其實也不常用,很少有一行行規整的數據。
re.findall('^[\s\w]*?$',?s)
re.findall('^[\s\w]*?$',?s,?re.M)
結果:
['aaa\r\nbbb\r\nccc']????????#?單行模式['aaa\r',?'bbb\r',?'ccc']????#?多行模式
(3)re.S
這個簡單,直接看個例子。
re.findall('^.*',?s)
re.findall('^.*',?s,?re.S)
結果:
['aaa\r']['aaa\r\nbbb\r\nccc']
(4)re.X
用法如下:
\d+?#?匹配數字
#?和字母
[a-zA-Z]+
""",?re.X)
rc.search('123abc').group()
結果:
123abc注意,用了 X 修飾符后,正則中的所有空格會被忽略,包括正則里面的原本有用的空格。如果正則中有需要使用空格,只能用 \s 代替。
(5)(?aiLmsux)
修飾符不僅可以代碼中指定,也可以在正則中指定。(?aiLmsux) 表示了以上所有的修飾符,具體用的時候需要哪個就在 ? 后面加上對應的字母,示例如下,(?a) 和 re.A 效果是一樣的:
re.search('(?a)\w+',?s).group()
re.search('\w+',?s,?re.A).group()
結果是一樣的:
123abc123abc
1.3、貪婪與懶惰
當正則表達式中包含能接受重復的限定符時,通常的行為是(在使整個表達式能得到匹配的前提下)匹配盡可能多的字符。
s?=?'aabab're.search('a.*b',?s).group()????
re.search('a.*?b',?s).group()???
結果:
aababaab
簡單來說:
- 所謂貪婪,就是盡可能 多 的匹配;
- 所謂懶惰,就是盡可能 少 的匹配。
- *、+、{n,} 這些表達式屬于貪婪;
- *?、+?、{n,}? 這些表達式就是懶惰(在貪婪的基礎上加上 ?)。
2、正則進階
2.1、捕獲分組
| (exp) | 匹配exp,并捕獲文本到自動命名的組里 |
| (?Pexp) | 匹配exp,并捕獲文本到名稱為 name 的組里 |
| (?:exp) | 匹配exp,不捕獲匹配的文本,也不給此分組分配組號 |
| (?P=name) | 匹配之前由名為 name 的組匹配的文本 |
注意:在其他語言或者網上的一些正則工具中,分組命名的語法是 (?<name>exp) 或 (?'name'exp) ,但在 Python 里,這樣寫會報錯:This named group syntax is not supported in this regex dialect。Python 中正確的寫法是:(?P<name>exp)
示例一:
分組可以讓我們用一條正則提取出多個信息,例如:
s?=?'姓名:張三;性別:男;電話:138123456789'm?=?re.search('姓名[::](\w+).*?電話[::](\d{11})',?s)
if?m:
????name?=?m.group(1)
????phone?=?m.group(2)
????print(f'name:{name},?phone:{phone}')
結果:
name:張三,?phone:13812345678示例二:
(?P<name>exp) 有時還是會用到的, (?P=name) 則很少情況下會用到。我想了一個 (?P=name) 的使用示例,給大家看下效果:
s?=?'''<name>張三</name>
<age>30</age>
<phone>138123456789</phone>
'''
pattern?=?r'<(?P<name>.*?)>(.*?)</(?P=name)>'
It?=?re.findall(pattern,?s)
結果:
[('name',?'張三'),?('age',?'30'),?('phone',?'138123456789')]2.2、零寬斷言
| (?=exp) | 匹配exp前面的位置 |
| (?<=exp) | 匹配exp后面的位置 |
| (?!exp) | 匹配后面跟的不是exp的位置 |
| (?<!exp) | 匹配前面不是exp的位置 |
注意:正則中常用的前項界定 (?<=exp) 和前項否定界定 (?<!exp) 在 Python 中可能會報錯:look-behind requires fixed-width pattern,原因是 python 中 前項界定的表達式必須是定長的,看如下示例:
(?<=aaa)????????#?正確(?<=aaa|bbb)????#?正確
(?<=aaa|bb)????????#?錯誤
(?<=\d+)????????#?錯誤
(?<=\d{3})????????#?正確
2.3、條件匹配
這大概是最復雜的正則表達式了。語法如下:
| (?(id/name)yes|no) | 如果指定分組存在,則匹配 yes 模式,否則匹配 no 模式 |
此語法極少用到,印象中只用過一次。
以下示例的要求是:如果以 _ 開頭,則以字母結尾,否則以數字結尾。
s1?=?'_abcd's2?=?'abc1'
pattern?=?'(_)?[a-zA-Z]+(?(1)[a-zA-Z]|\d)'
re.search(pattern,?s1).group()
re.search(pattern,?s2).group()
結果:
_abcdabc1
2.4、findall
Python 中的 re.findall 是個比較特別的方法(之所以說它特別,是跟我常用的 C# 做比較,在沒看注釋之前我想當然的掉坑里去了)。我們看這個方法的官方注釋:
Return?a?list?of?all?non-overlapping?matches?in?the?string.If?one?or?more?capturing?groups?are?present?in?the?pattern,?return?
a?list?of?groups;?this?will?be?a?list?of?tuples?if?the?pattern?
has?more?than?one?group.
Empty?matches?are?included?in?the?result.
簡單來說,就是
- 如果沒有分組,則返回整條正則匹配結果的列表;
- 如果有 1 個分組,則返回分組匹配到的結果的列表;
- 如果有多個分組,則返回分組匹配到的結果的元組的列表。
看下面的例子:
s?=?'aaa123bbb456ccc're.findall('[a-z]+\d+',?s)??????????#?不包含分組
re.findall('[a-z]+(\d+)',?s)????????#?包含一個分組
re.findall('([a-z]+(\d+))',?s)??????#?包含多個分組
re.findall('(?:[a-z]+(\d+))',?s)????#??:?不捕獲分組匹配結果
結果:
['aaa123',?'bbb456']['123',?'456']
[('aaa123',?'123'),?('bbb456',?'456')]
['123',?'456']
零寬斷言中講到 Python 中前項界定必須是定長的,這很不方便,但是配合 findall 有分組時只取分組結果的特性,就可以模擬出非定長前項界定的效果了。
結語
其實正則就像是一個數學公式,會背公式不一定會做題。但其實這公式一點也不難,至少比學校里學的數學簡單多了,多練習幾次也就會了。
轉載于:https://www.cnblogs.com/gl1573/p/11363079.html
總結
以上是生活随笔為你收集整理的写爬虫,不会正则怎么行?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 解决socket粘包的两种low版模式
- 下一篇: 使用canvas实现360水球波动