对着爬虫网页HTML学习Python正则表达式re
-
1.正則表達(dá)式初探
-
2.用正則表達(dá)式匹配更多模式
-
2.1.利用括號(hào)()進(jìn)行分組
-
2.2.利用管道|匹配多個(gè)分組
-
2.3.用問(wèn)號(hào)?實(shí)現(xiàn)可選匹配
-
2.4.用星號(hào)*實(shí)現(xiàn)0次或多次
-
2.5.用加號(hào)+實(shí)現(xiàn)1次或多次
-
2.6.用花括號(hào){}匹配特定次數(shù)
-
-
3.貪心和非貪心匹配
-
4.字符類型
-
5.split()函數(shù)
1.正則表達(dá)式初探
用比較經(jīng)典的例子,查找一段文本中的手機(jī)號(hào)碼。比如對(duì)于文本“我現(xiàn)在用的電話是188-8888-8888,之前那個(gè)186-6666-6666已經(jīng)不用了”,我們想獲取其中的手機(jī)號(hào)碼信息,用正則表達(dá)式可以這么做呢?
正則表達(dá)式,簡(jiǎn)稱為 regex,是文本模式的描述方法。例如,\d 是一個(gè)正則表達(dá)式,表示一位數(shù)字字符,即任何一位 0 到 9 的數(shù)字。Python 使用正則表達(dá)式\d\d\d-\d\d\d\d-\d\d\d\d,來(lái)匹配3 個(gè)數(shù)字、一個(gè)短橫線、4 個(gè)數(shù)字、一個(gè)短橫線、4 個(gè)數(shù)字。所有其他字符串都不能匹配\d\d\d-\d\d\d\d-\d\d\d\d 正則表達(dá)式。
在一個(gè)表達(dá)式后加上花括號(hào)包圍的 3({3}),就是說(shuō),“匹配這個(gè)模式 3 次”。所以較短的正則表達(dá)式\d{3}-\d{4}-\d{4},也可以匹配正確的手機(jī)號(hào)碼格式。
引入正則表達(dá)式庫(kù)?re,該庫(kù)是python自帶的哈。
In?[1]:?import?re...:?#?創(chuàng)建一個(gè)regex模式對(duì)象...:?phoneNum?=?re.compile(r'\d\d\d-\d\d\d\d-\d\d\d\d')...:?#?匹配regex對(duì)象...:?mo?=?phoneNum.search('我現(xiàn)在用的電話是188-8888-8888,之前那個(gè)186-6666-6666已經(jīng)不用了')In?[2]:?mo.group() Out[2]:?'188-8888-8888'其實(shí),以下是等價(jià)的
#?創(chuàng)建一個(gè)regex模式對(duì)象,pattern指待匹配的正則表達(dá)式 phoneNum?=?re.compile(pattern) #?匹配regex對(duì)象,string指代匹配的文本內(nèi)容 mo?=?phoneNum.search(string)等價(jià)于
mo?=?phoneNum.search(pattern,?string)如果需要多次使用這個(gè)正則表達(dá)式的話,使用?re.compile()?和保存這個(gè)正則對(duì)象以便復(fù)用,可以讓程序更加高效。
不過(guò),我們發(fā)現(xiàn)其實(shí)在待匹配的文本內(nèi)容中出現(xiàn)了2個(gè)手機(jī)號(hào)碼,但是re.search()只返回了第一個(gè)匹配成功的文本。如何可以獲取全部匹配成功的項(xiàng)呢,咱們可以使用re.findall()來(lái)進(jìn)行操作,其返回的結(jié)果是由所有匹配組成的列表。
In?[3]:?re.findall(r'\d{3}-\d{4}-\d{4}',?'我現(xiàn)在用的電話是188-8888-8888,之前那個(gè)186-6666-6666已經(jīng)不用了') Out[3]:?['188-8888-8888',?'186-6666-6666']2.用正則表達(dá)式匹配更多模式
在實(shí)際解析網(wǎng)頁(yè)HTML文本的時(shí)候,我們可能需要取匹配中某個(gè)部分分組文本、或者需要選擇性匹配多個(gè)文本、又或者對(duì)某些字符或者分組需要匹配0/1次或者多次等等。
以下是待解析的某待租房間信息
info=?'''<h5?class="title?sign"><a?href="//www.ziroom.com/x/712447913.html"?target="_blank"?style="line-height:?0.9em;">合租·DBC加州小鎮(zhèn)C區(qū)4居室-南臥</a></h5><div?class="desc"><div>23.3㎡?|?5/15層</div><div?class="location">小區(qū)距高樓金站步行約178米????????????????????????????????</div></div><div?class="price?"><span?class="rmb">¥</span><span?class="num">188</span><span?class="unit">/天</span></div><div?class="tag"><span>可短租</span><span>離地鐵近</span><span>米蘇4.0</span></div>'''對(duì)于這種文本,由于存在很多空白字符類如換行、空格等等,我需要先用re.sub()進(jìn)行簡(jiǎn)單的清洗。
info?=?re.sub(r'\s','',info)?#?\s?匹配任意空白字符2.1.利用括號(hào)()進(jìn)行分組
比如,我需要匹配子字符中的房間租金信息,因租金為數(shù)字但是還有別的一些信息也是數(shù)字(如房間大小等),因此我們?cè)谄ヅ涞臅r(shí)候需要代入前后一些字符做唯一匹配,但是實(shí)際只需要對(duì)應(yīng)的數(shù)字文本內(nèi)容,因此需要進(jìn)行分組。
<spanclass="num">188</span>比如以上,我們想要獲得價(jià)格188,可以使用(\d{3})進(jìn)行匹配。
注意:這里是的匹配模式是4位數(shù)字的精確匹配,在實(shí)際的操作中價(jià)格可能存在不確定的位置甚至帶有小數(shù),我們需要用到更復(fù)雜的匹配模式,具體見(jiàn)后續(xù)講解。
In?[4]:?re.findall(r'<spanclass="num">(\d{3})</span>',?info) Out[4]:?['188']2.2.利用管道|匹配多個(gè)分組
以示例的info文本,在爬蟲(chóng)過(guò)程中其價(jià)格有時(shí)候類型是天或者月,我們匹配的可能就是諸多表達(dá)式中的一個(gè),此時(shí)可以使用 | 進(jìn)行操作。正則表達(dá)式r“天|月”即可匹配 天 或者 月。
<spanclass="unit">/天</span> #?或者 <spanclass="unit">/月</span>我們采用正則表達(dá)式 r“天|月” 可實(shí)現(xiàn)匹配。
In?[5]:?re.findall(r'<spanclass="unit">/(月|天)</span>',?info) Out[5]:?['天']In?[6]:?s?=?'<spanclass="unit">/月</span>' In?[7]:?re.findall(r'<spanclass="unit">/(月|天)</span>',?s) Out[7]:?['月']2.3.用問(wèn)號(hào)?實(shí)現(xiàn)可選匹配
對(duì)于房間的面積,有的可能是整數(shù)有的可能是小數(shù),因此小數(shù)點(diǎn)及小數(shù)點(diǎn)后的數(shù)字其實(shí)是可選項(xiàng),為了更好的匹配這個(gè)面積文本,我們需要用到問(wèn)號(hào)?。字符?表示它前面的分組在這個(gè)模式中是可選的。
<div>23.3㎡|5/15層</div> #?或者 <div>23㎡|5/15層</div>我們可以用 r'(\d{2}.?\d?)'來(lái)進(jìn)行匹配,如果為了在整個(gè)html里找且怕存在重復(fù),可以用r'(\d{2}.?\d?)|5/15層'。這里需要注意我們?cè)?| 前面加了 轉(zhuǎn)義字符 \,區(qū)別于 | 本身,否則可能無(wú)法得出正確結(jié)果。
In?[8]:?re.findall(r'<div>(\d{2}\.?\d?)㎡\|5/15層</div>',info) Out[8]:?['23.3']In?[9]:?re.findall(r'<div>(\d{2}\.?\d?)㎡\|5/15層</div>','<div>23㎡|5/15層</div>') Out[9]:?['23']2.4.用星號(hào)*實(shí)現(xiàn)0次或多次
對(duì)于樓層信息來(lái)說(shuō),我們要獲取其樓層和樓高,有的可能有樓層信息但是有的可能沒(méi)有,樓層和樓高可能是個(gè)位數(shù)或者十位數(shù)。這種情況下,我們可以使用星號(hào)進(jìn)行匹配。字符*表示它前面的分組在這個(gè)模式中是出現(xiàn)0次或者多次。
<div>23.3㎡|5/15層</div> #?或者 <div>23㎡|9層</div>由于樓高是一定存在的,而樓層不一定存在,因?yàn)槲覀兛梢杂胷'(\d*)/*(\d+)'來(lái)進(jìn)行匹配,注意字符+代表至少一次,詳見(jiàn)后續(xù)說(shuō)明。
In?[10]:?re.findall(r'<div>\d{2}\.?\d?㎡\|(\d*)/*(\d+)層</div>',?info) Out[10]:?[('5',?'15')]In?[11]:?re.findall(r'<div>\d{2}\.?\d?㎡\|(\d*)/*(\d+)層</div>',?'<div>23㎡|9層</div>') Out[11]:?[('',?'9')]2.5.用加號(hào)+實(shí)現(xiàn)1次或多次
我們?cè)?.4中其實(shí)看到了?字符 +?的使用場(chǎng)景,其代表的就是?它前面的分組在這個(gè)模式中是出現(xiàn)1次或者多次。
<spanclass="num">188</span> #?或者 <spanclass="num">1888</span>我們回到 2.1.中 匹配租金的案例,其實(shí)對(duì)于租金來(lái)說(shuō)除了3位數(shù)之外,租金金額其實(shí)是一個(gè)大于0的值,也就是至少出現(xiàn)1次數(shù)字,因此我們可以用 r'(\d+)' 來(lái)匹配。
In?[12]:?re.findall(r'<spanclass="num">(\d+)</span>',?info) Out[12]:?['188']In?[13]:?re.findall(r'<spanclass="num">(\d+)</span>',?'<spanclass="num">1888</span>') Out[13]:?['1888']2.6.用花括號(hào){}匹配特定次數(shù)
再以2.3.中的房間面積為例,我們認(rèn)為房間面積不可能超過(guò)3位數(shù)、最低1位數(shù) 為正常值。如果想要一個(gè)分組重復(fù)特定次數(shù),就在正則表達(dá)式中該分組的后面,跟上花括號(hào)包圍的數(shù)字。例如,正則表達(dá)式(Ha){3}將匹配字符串'HaHaHa',但不會(huì)匹配'HaHa',因?yàn)楹笳咧恢貜?fù)了(Ha)分組兩次。
除了一個(gè)數(shù)字,還可以指定一個(gè)范圍,即在花括號(hào)中寫(xiě)下一個(gè)最小值、一個(gè)逗號(hào)和一個(gè)最大值。例如,正則表達(dá)式(Ha){3,5}將匹配'HaHaHa'、 'HaHaHaHa'和'HaHaHaHaHa'。?
也可以不寫(xiě)花括號(hào)中的第一個(gè)或第二個(gè)數(shù)字, 不限定最小值或最大值。例如,(Ha){3,}將匹配 3 次或更多次實(shí)例, (Ha){,5}將匹配 0 到 5 次實(shí)例。?
不過(guò),在使用過(guò)程中一定要慎重,同樣的分組在不同的匹配模式可能帶來(lái)不同的結(jié)果。
In?[14]:?re.findall(r'(\d{2,3})㎡','<div>3456㎡|5/15層</div>') Out[14]:?['456']In?[15]:?re.findall(r'<div>(\d{2,3})㎡','<div>3456㎡|5/15層</div>') Out[15]:?[]3.貪心和非貪心匹配
Python 的正則表達(dá)式默認(rèn)是“貪心” 的,這表示在有二義的情況下,它們會(huì)盡可能匹配最長(zhǎng)的字符串。?
在表達(dá)式后面加上符號(hào)?,即為非貪心匹配。
In?[16]:?greedyHaRegex?=?re.compile(r'(Ha){3,5}')In?[17]:?mo1?=?greedyHaRegex.search('HaHaHaHaHa')In?[18]:?mo1.group() Out[18]:?'HaHaHaHaHa'In?[19]:?greedyHaRegex?=?re.compile(r'(Ha){3,5}?')In?[20]:?mo2?=?greedyHaRegex.search('HaHaHaHaHa')In?[21]:?mo2.group() Out[21]:?'HaHaHa'In?[22]:?re.findall(r'(Ha){3,5}?','HaHaHaHaHa') Out[22]:?['Ha']In?[23]:?re.findall(r'(Ha){3,5}','HaHaHaHaHa') Out[23]:?['Ha']In?[24]:?re.findall(r'((Ha){3,5})','HaHaHaHaHa') Out[24]:?[('HaHaHaHaHa',?'Ha')]In?[25]:?re.findall(r'((Ha){3,5}?)','HaHaHaHaHa') Out[25]:?[('HaHaHa',?'Ha')]4.字符類型
| ^ | 匹配字符串的開(kāi)頭 |
| $ | 匹配字符串的末尾。 |
| . | 匹配任意字符,除了換行符,當(dāng).DOTALL標(biāo)記被指定時(shí),則可以匹配包括換行符的任意字符。 |
| [...] | 用來(lái)表示一組字符,單獨(dú)列出:[amk] 匹配 'a','m'或'k' |
| [^...] | 不在[]中的字符:[^abc] 匹配除了a,b,c之外的字符。 |
| * | 匹配0個(gè)或多個(gè)的表達(dá)式。 |
| + | 匹配1個(gè)或多個(gè)的表達(dá)式。 |
| ? | 匹配0個(gè)或1個(gè)由前面的正則表達(dá)式定義的片段,非貪婪方式 |
| { n} | 精確匹配 n 個(gè)前面表達(dá)式。例如, o{2} 不能匹配 "Bob" 中的 "o",但是能匹配 "food" 中的兩個(gè) o。 |
| { n,} | 匹配 n 個(gè)前面表達(dá)式。例如, o{2,} 不能匹配"Bob"中的"o",但能匹配 "foooood"中的所有 o。"o{1,}" 等價(jià)于 "o+"。"o{0,}" 則等價(jià)于 "o*"。 |
| { n, m} | 匹配 n 到 m 次由前面的正則表達(dá)式定義的片段,貪婪方式 |
| a|b | 匹配a或b |
| () | 對(duì)正則表達(dá)式分組并記住匹配的文本 |
| (?imx) | 正則表達(dá)式包含三種可選標(biāo)志:i, m, 或 x 。只影響括號(hào)中的區(qū)域。 |
| (?-imx) | 正則表達(dá)式關(guān)閉 i, m, 或 x 可選標(biāo)志。只影響括號(hào)中的區(qū)域。 |
| (?: ) | 類似 (...), 但是不表示一個(gè)組 |
| (?imx: ) | 在括號(hào)中使用i, m, 或 x 可選標(biāo)志 |
| (?-imx: ) | 在括號(hào)中不使用i, m, 或 x 可選標(biāo)志 |
| (?#...) | 注釋. |
| (?= ) | 前向肯定界定符。如果所含正則表達(dá)式,以 ... 表示,在當(dāng)前位置成功匹配時(shí)成功,否則失敗。但一旦所含表達(dá)式已經(jīng)嘗試,匹配引擎根本沒(méi)有提高;模式的剩余部分還要嘗試界定符的右邊。 |
| (?! ) | 前向否定界定符。與肯定界定符相反;當(dāng)所含表達(dá)式不能在字符串當(dāng)前位置匹配時(shí)成功 |
| (?> ) | 匹配的獨(dú)立模式,省去回溯。 |
| \w | 匹配字母數(shù)字及下劃線 |
| \W | 匹配非字母數(shù)字及下劃線 |
| \s | 匹配任意空白字符,等價(jià)于 [ \t\n\r\f]。 |
| \S | 匹配任意非空字符 |
| \d | 匹配任意數(shù)字,等價(jià)于 [0-9]. |
| \D | 匹配任意非數(shù)字 |
| \A | 匹配字符串開(kāi)始 |
| \Z | 匹配字符串結(jié)束,如果是存在換行,只匹配到換行前的結(jié)束字符串。 |
| \z | 匹配字符串結(jié)束 |
| \G | 匹配最后匹配完成的位置。 |
| \b | 匹配一個(gè)單詞邊界,也就是指單詞和空格間的位置。例如, 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。 |
| \B | 匹配非單詞邊界。'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。 |
| \n, \t, | 匹配一個(gè)換行符,匹配一個(gè)制表符 |
| \1...\9 | 匹配第n個(gè)分組的內(nèi)容。 |
| \10 | 匹配第n個(gè)分組的內(nèi)容,如果它經(jīng)匹配。否則指的是八進(jìn)制字符碼的表達(dá)式。 |
| [Pp]ython | 匹配 "Python" 或 "python" |
| rub[ye] | 匹配 "ruby" 或 "rube" |
| [aeiou] | 匹配中括號(hào)內(nèi)的任意一個(gè)字母 |
| [0-9] | 匹配任何數(shù)字。類似于 [0123456789] |
| [a-z] | 匹配任何小寫(xiě)字母 |
| [A-Z] | 匹配任何大寫(xiě)字母 |
| [a-zA-Z0-9] | 匹配任何字母及數(shù)字 |
| [^aeiou] | 除了aeiou字母以外的所有字符 |
| [^0-9] | 匹配除了數(shù)字外的字符 |
正則匹配模式表
| re.S(DOTALL) | 使.匹配包括換行在內(nèi)的所有字符 |
| re.I(IGNORECASE) | 使匹配對(duì)大小寫(xiě)不敏感 |
| re.L(LOCALE) | 做本地化識(shí)別(locale-aware)匹配,法語(yǔ)等 |
| re.M(MULTILINE) | 多行匹配,影響^和$ |
| re.X(VERBOSE) | 該標(biāo)志通過(guò)給予更靈活的格式以便將正則表達(dá)式寫(xiě)得更易于理解 |
| re.U | 根據(jù)Unicode字符集解析字符,這個(gè)標(biāo)志影響\w,\W,\b,\B |
5.split()函數(shù)
根據(jù)正則匹配分割字符串,返回分割后的一個(gè)列表split(pattern, string, maxsplit=0, flags=0)
pattern:正則模型
string :要匹配的字符串
maxsplit:指定分割個(gè)數(shù)
flags ?:匹配模式
當(dāng)我們獲取了全部房源信息后,需要對(duì)一些信息進(jìn)行二次解析,比如房屋信息的解析。
In?[26]:?#?房屋信息解析...:?s1?=?'合租·李村東里3居室-北臥'...:?s2?=?'合租·強(qiáng)佑·府學(xué)上院4居室-北臥'...:?s3?=?'整租·鐵二區(qū)1室1廳-北'...:?s4?=?'整租·廠甸11號(hào)院1室1廳-東'...:?s5?=?'整租·牛街182室1廳-西'In?[27]:?re.split(r'(\S*?)·(.*)(\d居*室.*)-(.*)',s1) Out[27]:?['',?'合租',?'李村東里',?'3居室',?'北臥',?'']In?[28]:?re.split(r'(\S*?)·(.*)(\d居*室.*)-(.*)',s2) Out[28]:?['',?'合租',?'強(qiáng)佑·府學(xué)上院',?'4居室',?'北臥',?'']In?[29]:?re.split(r'(\S*?)·(.*)(\d居*室.*)-(.*)',s3) Out[29]:?['',?'整租',?'鐵二區(qū)',?'1室1廳',?'北',?'']In?[30]:?re.split(r'(\S*?)·(.*)(\d居*室.*)-(.*)',s4) Out[30]:?['',?'整租',?'廠甸11號(hào)院',?'1室1廳',?'東',?'']In?[31]:?re.split(r'(\S*?)·(.*)(\d居*室.*)-(.*)',s5) Out[31]:?['',?'整租',?'牛街18',?'2室1廳',?'西',?'']大家可以嘗試更多種正則表達(dá)式匹配規(guī)則,比如能把前后的空字符串去掉的等等。
如果我們要解析出 房間面積、樓層和樓高信息,觀測(cè)數(shù)據(jù)發(fā)現(xiàn)存在以下3種情況,大家覺(jué)得怎么寫(xiě)正則表達(dá)式能實(shí)現(xiàn)呢?
#?房間信息解析 #?我們?cè)跀?shù)據(jù)處理中發(fā)現(xiàn)存在異常數(shù)據(jù)(樓層如?7層?或?-1/5層) s1?=?'87.26㎡|11/29層' s2?=?'87㎡|7層' s3?=?'8.6㎡|-1/5層'總結(jié)
以上是生活随笔為你收集整理的对着爬虫网页HTML学习Python正则表达式re的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Python办公自动化,对文件进行自由操
- 下一篇: 如何用Python读取Excel中图片?