abc)d(?P=id)abcdabc(?:…)分組的不捕獲模式,計(jì)算索引時(shí)會(huì)跳過這個(gè)分組(?:a)b(c)d\1abcdc(?iLmsux)分組中可以設(shè)置模式,iLmsux之中的每個(gè)字符代表一個(gè)模式(?i)abcAbc(?#…)注釋,#后面的內(nèi)容會(huì)被忽略ab(?#注釋)123ab123(?=…)順序肯定環(huán)視,表示所在位置右側(cè)能夠匹配括號(hào)內(nèi)正則a(?=\d)a1最后的結(jié)果得到a(?!…)順序否定環(huán)視,表示所在位置右側(cè)不能匹配括號(hào)內(nèi)正則a(?!\w)a c最后的結(jié)果得到a(?<><>上面表格中(?iLmsux)這里的”i”, “L”, “m”, “s”, “u”, “x”,它們不匹配任何字串,而對應(yīng)re模塊中(re.S|re.S):
I:re.I# 忽略大小寫L:re.L# 字符集本地化,為了支持多語言版本的字符集使用環(huán)境U :re.U# 使用\w,\W,\b,\B這些元字符時(shí)將按照UNICODE定義的屬性M:re.M # 多行模式,改變 ^ 和 $ 的行為S:re.S # '.' 的匹配不受限制,包括換行符X:re.X # 冗余模式,可以忽略正則表達(dá)式中的空白和#號(hào)的注釋
對于一個(gè)特殊字符在正則表達(dá)式中是不能正常識(shí)別的,如果接觸過其他語言我們就這到有一個(gè)叫做轉(zhuǎn)移字符的東西的存在,在特殊字符前加用反斜杠接口。比如\n換行\(zhòng)\為反斜杠,在這不再累述。下面來介紹一下re這個(gè)模塊。
1.2.re模塊
此模塊主要方法如下
re.match()#嘗試從字符串的起始位置匹配一個(gè)模式(pattern),如果不是起始位置匹配成功的話,match()就返回Nonere.search()#函數(shù)會(huì)在字符串內(nèi)查找模式匹配,只要找到第一個(gè)匹配然后返回,如果字符串沒有匹配,則返回None。re.findall()#遍歷匹配,可以獲取字符串中所有匹配的字符串,返回一個(gè)列表。re.compile()#編譯正則表達(dá)式模式,返回一個(gè)對象的模式。(可以把那些常用的正則表達(dá)式編譯成正則表達(dá)式對象,這樣可以提高一點(diǎn)效率。)re.sub()#使用re替換string中每一個(gè)匹配的子串后返回替換后的字符串。re.subn()#返回替換次數(shù)re.split()#按照能夠匹配的子串將string分割后返回列表。
1.2.1.re.match()
方法: re.match(pattern, string, flags=0) # pattern:正則表達(dá)式(或者正則表達(dá)式對象)string:要匹配的字符串flags:修飾符
先看一個(gè)最簡單的用法
import recontent ='Hello 123 4567 wangyanling REDome'print(len(content))result = re.match('^Hello\s\d\d\d\s\d{4}\s\w{10}.*Dome$', content)print(result)print(result.group())print(result.span())
結(jié)果:
匹配規(guī)則就不在累述,以上需要注意的是
(1) .group() 表示的是返回正則匹配的結(jié)果
(2) .span() 表示返回正則匹配的范圍
使用:
以上我們已經(jīng)知道re.matcha()的具體方法,那么接下我來看一下具體使用,對此我們要理解以下幾種匹配的感念。
1.泛匹配(.*):匹配所有字符
import recontent ='Hello 123 4567 wangyanling REDome'result = re.match('^Hello.*Dome$', content)print(result)print(result.group())print(result.span())
它的結(jié)果是和上面的輸出結(jié)果完全一樣的。
2.目標(biāo)匹配(()):將需要的字符匹配出來
import recontent ='Hello 123 4567 wangyanling REDome'result = re.match('^Hello\s\d\d(\d)\s\d{4}\s\w{10}.*Dome$', content)print(result)print(result.group(1))import recontent ='Hello 123 4567 wangyanling REDome'result = re.match('^Hello\s(\d+)\s\d{4}\s\w{10}.*Dome$', content)print(result)print(result.group(1))
結(jié)果
以上可以看出:
(1) () 匹配括號(hào)內(nèi)的表達(dá)式,也表示一個(gè)組
(2) + 匹配1個(gè)或多個(gè)的表達(dá)式
*匹配0個(gè)或多個(gè)的表達(dá)式
(3) .group(1) —輸出第一個(gè)帶有()的目標(biāo)
3.貪婪匹配(.*()):匹配盡可能少的的結(jié)果
import recontent ='Hello 123 4567 wangyanling REDome'result = re.match('^H.*(\d+).*Dome$', content)print(result)print(result.group(1))
結(jié)果
4.貪婪匹配(.*?()):匹配盡可能多的結(jié)果
import recontent ='Hello 123 4567 wangyanling REDome'result = re.match('^H.*?(\d+).*?Dome$', content)print(result)print(result.group(1))
結(jié)果
以上3,4兩個(gè)匹配方式請盡量采用非貪婪匹配
5.其他
換行:
import recontent ='''Hello 123 4567 wangyanling REDome'''result = re.match('^H.*?(\d+).*?Dome$', content,re.S)#re.Sprint(result.group(1))result = re.match('^H.*?(\d+).*?Dome$', content)print(result.group(1))
結(jié)果:
轉(zhuǎn)義字符:
import recontent = 'price is $5.00'result = re.match('price is $5.00', content)print(result)result = re.match('price is \$5\.00', content)print(result)
結(jié)果:
其中re.I使匹配對大小不敏感,re.S匹配包括換行符在內(nèi)的所有字符,\進(jìn)行處理轉(zhuǎn)義字符。匹配規(guī)則中有詳細(xì)介紹。
1.2.2.re.search()
方法:
re.search(pattern, string, flags=0)#pattern:正則表達(dá)式(或者正則表達(dá)式對象)string:要匹配的字符串flags:修飾符 #re.match()和re.search()用法類似唯一的區(qū)別在于re.match()從字符串頭開始匹配,若頭匹配不成功,則返回None
對比一下與match()
import recontent ='Hello 123 4567 wangyanling REDome'result = re.match('(\d+)\s\d{4}\s\w{10}.*Dome$', content)print(result)#從開頭開始查找,不能匹配返回Noneresult = re.search('(\d+)\s\d{4}\s\w{10}.*Dome$', content)print(result)print(result.group())
結(jié)果:
可以看出兩個(gè)使用基本一致,search從頭開始匹配,如果匹配不到就返回none.
1.2.3.re.findall()
方法: re.finditer(pattern, string, flags=0) # pattern:正則表達(dá)式(或者正則表達(dá)式對象)string:要匹配的字符串flags:修飾符
與re.search()類似區(qū)別在于re.findall()搜索string,返回一個(gè)順序訪問每一個(gè)匹配結(jié)果(Match對象)的迭代器。找到 RE 匹配的所有子串,并把它們作為一個(gè)迭代器返回。
import rehtml = '''
'''regex_4='
(.*?)'results=re.findall(regex_4,html,re.S)print(results)for result in results: print(result)結(jié)果:
1.2.4.re.compile()
編譯正則表達(dá)式模式,返回一個(gè)對象的模式。
方法: re.compile(pattern,flags=0) # pattern:正則表達(dá)式(或者正則表達(dá)式對象);flags:修飾符
看一個(gè)demo
import recontent ='Hello 123 4567 wangyanling REDome wangyanling 那小子很帥'rr = re.compile(r'\w*wang\w*')result =rr.findall(content)print(result)
結(jié)果:
我們可以看出compile 我們可以把它理解為封裝了一個(gè)公用的正則,類似于方法,然后功用。
1.2.5.其他
re.sub 替換字符
方法: re.sub(pattern, repl, string, count=0, flags=0) # pattern:正則表達(dá)式(或者正則表達(dá)式對象)repl:替換的字符串string:要匹配的字符串count:要替換的個(gè)數(shù)flags:修飾符
re.subn 替換次數(shù)
方法: re.subn(pattern, repl, string, count=0, flags=0) # pattern:正則表達(dá)式(或者正則表達(dá)式對象)repl:替換的字符串string:要匹配的字符串count:要替換的個(gè)數(shù)flags:修飾符
re.split()分隔字符
方法
re.split(pattern, string,[maxsplit])#正則表達(dá)式(或者正則表達(dá)式對象)string:要匹配的字符串;maxsplit:用于指定最大分割次數(shù),不指定將全部分割
2.案例:爬取貓眼信息,寫入txt,csv,下載圖片
2.1.獲取單頁面信息
def get_one_page(html): pattern= re.compile('
.*?board-index.*?>(\d+).*?data-src='(.*?)'.*?name'>(.*?).*?star'>(.*?).*?releasetime' + '.*?>(.*?)
.*?score.*?integer'>(.*?).*?>(.*?).*?',re.S)#這里就用到了我們上述提到的一些知識(shí)點(diǎn),非貪婪匹配,對象匹配,修飾符 items = re.findall(pattern,html) for item in items: yield { 'rank' :item[0], 'img': item[1], 'title':item[2], 'actor':item[3].strip()[3:] if len(item[3])>3 else '', 'time' :item[4].strip()[5:] if len(item[4])>5 else '', 'score':item[5] + item[6] }
對于上面的信息我們可以看出是存到一個(gè)對象中那么接下來我們應(yīng)該把它們存到文件當(dāng)中去。
2.2.保存文件
我寫了兩種方式保存到txt和csv這些在python都有涉及,不懂得可以去翻看一下。
2.2.1.保存到txt
def write_txtfile(content): with open('Maoyan.txt','a',encoding='utf-8') as f: #要引入json,利用json.dumps()方法將字典序列化,存入中文要把ensure_ascii編碼方式關(guān)掉 f.write(json.dumps(content,ensure_ascii=False) + '\n') f.close()
結(jié)果:
以上看到并非按順序排列因?yàn)槲矣玫氖嵌嗑€程。
2.2.2.保存到csv
def write_csvRows(content,fieldnames): '''寫入csv文件內(nèi)容''' with open('Maoyao.csv','a',encoding='gb18030',newline='') as f: #將字段名傳給Dictwriter來初始化一個(gè)字典寫入對象 writer = csv.DictWriter(f,fieldnames=fieldnames) #調(diào)用writeheader方法寫入字段名 writer.writerows(content) f.close()
結(jié)果:
那么還有一部就是我們要把圖片下載下來。
2.2.3.下載圖片
def download_img(title,url): r=requests.get(url) with open(title+'.jpg','wb') as f: f.write(r.content)
2.3.整體代碼
這里面又到了多線程在這不在敘述后面會(huì)有相關(guān)介紹。這個(gè)demo僅做一案例,主要是對正則能有個(gè)認(rèn)知。上面寫的知識(shí)點(diǎn)有不足的地方望大家多多指教。
#抓取貓眼電影TOP100榜from multiprocessing import Poolfrom requests.exceptions import RequestExceptionimport requestsimport jsonimport timeimport csvimport redef get_one_page(url): '''獲取單頁源碼''' try: headers = { 'User-Agent':'Mozilla/5.0(WindowsNT6.3;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/68.0.3440.106Safari/537.36' } res = requests.get(url, headers=headers) # 判斷響應(yīng)是否成功,若成功打印響應(yīng)內(nèi)容,否則返回None if res.status_code == 200: return res.text return None except RequestException: return Nonedef parse_one_page(html): '''解析單頁源碼''' pattern = re.compile('
.*?board-index.*?>(\d+).*?data-src='(.*?)'.*?name'>(.*?).*?star'>(.*?).*?releasetime' + '.*?>(.*?)
.*?score.*?integer'>(.*?).*?>(.*?).*?',re.S) items = re.findall(pattern,html) #采用遍歷的方式提取信息 for item in items: yield { 'rank' :item[0], 'img': item[1], 'title':item[2], 'actor':item[3].strip()[3:] if len(item[3])>3 else '', #判斷是否大于3個(gè)字符 'time' :item[4].strip()[5:] if len(item[4])>5 else '', 'score':item[5] + item[6] }def write_txtfile(content): with open('Maoyan.txt','a',encoding='utf-8') as f: #要引入json,利用json.dumps()方法將字典序列化,存入中文要把ensure_ascii編碼方式關(guān)掉 f.write(json.dumps(content,ensure_ascii=False) + '\n') f.close()def write_csvRows(content,fieldnames): '''寫入csv文件內(nèi)容''' with open('Maoyao.csv','a',encoding='gb18030',newline='') as f: #將字段名傳給Dictwriter來初始化一個(gè)字典寫入對象 writer = csv.DictWriter(f,fieldnames=fieldnames) #調(diào)用writeheader方法寫入字段名 #writer.writeheader() ###這里寫入字段的話會(huì)造成在抓取多個(gè)時(shí)重復(fù). writer.writerows(content) f.close()def download_img(title,url): r=requests.get(url) with open(title+'.jpg','wb') as f: f.write(r.content)def main(offset): fieldnames = ['rank','img', 'title', 'actor', 'time', 'score'] url = 'http://maoyan.com/board/4?offset={0}'.format(offset) html = get_one_page(url) rows = [] for item in parse_one_page(html): #download_img(item['rank']+item['title'],item['img']) write_txtfile(item) rows.append(item) write_csvRows(rows,fieldnames)if __name__ == '__main__': pool = Pool() #map方法會(huì)把每個(gè)元素當(dāng)做函數(shù)的參數(shù),創(chuàng)建一個(gè)個(gè)進(jìn)程,在進(jìn)程池中運(yùn)行. pool.map(main,[i*10 for i in range(10)])