正則與爬蟲(1)
阿新 • • 發佈:2018-11-09
正則是什麼
正則是一門小型的程式語言,在python中正則被封裝成re模組。自己對正則的理解就是用來匹配字串中一些字元,正則使得匹配字串的匹配更加多樣。
三種匹配方法
re模組中匹配了三種方法(findall,match,search)
s = "/home/kioskday25/PycharmProjects/python_stack/day25/正則表示式的正常使用符那方法.py" pattern = r'day' pattern1 = r'/home' print(re.findall(pattern,s)) #注意:findall會匹配子符串中所有的字元,並以列表的形式把他們列出來 print(re.match(pattern1,s).group()) #注意:match方法只匹配字串的開頭: #當在開頭匹配到字串時,返回一個物件,如果要檢視匹配的結果,需要借用group方法,gruop方法返回的是一個字串 #當沒有匹配到結果時返回一個空 print(re.search(pattern,s).group()) #注意:search方法匹配的是整個字串,當字串中有多個滿足匹配結果時,只匹配第一個,並通過gruop方法獲取到匹配的結果。
結果如下:
正則特殊字元類
#.:匹配除\n之外的任意字元,並將每個匹配到的字元通過列表儲存 print(re.findall(r'.','westos\n')) #\d:匹配一個數字,等加於[0-9] #\D:匹配除數字外的任意一個字元 print(re.findall('\d','python12345linux')) print(re.findall('\D','python12345linux')) #\s:匹配單個任何空白的字元 #\S:匹配除單個任何空白字元的任何字元 print(re.findall('\s',"python\r123\tlinux")) print(re.findall('\S',"python\r123\tlinux")) #\w:匹配字母數字或下劃線 #\W:匹配除字母數字下劃線的字元 print(re.findall('\w',"python_123_linxu%%%%@@@")) print(re.findall('\W',"python_123_linxu%%%%@@@")) print(re.findall('[1-5][0-9]','123456')) pattern = r'[ABC][]'
注意:在findall方法中,匹配返回的結果是一個列表。
結果:
在上面的匹配中,如果我們要匹配一個數字,那我們就必須寫多次\d,顯得非常麻煩。在正則中,我們可以利用一個語法實現實現\d多次,免去多次書寫的麻煩。
#*代表前面的字元出現0次或者無限多次
print(re.findall("\d*",'132233'))
#+代表前面的字元出現一次或者無限次
print(re.findall("\d+",'122245'))
#?代表一個字元出現1次或者0次
print(re.findall("\d?","123455"))
#{m}:前一個字元出現m次
#{m,}:前一個字元至少出現m次
#{m,n}:前一個字元出現m次到n次
pattern = r"[a-zA-z][\w]{5,11}@qq\.com"
print(re.findall(pattern,' [email protected]'))
結果:
轉義字元
|:匹配左右任意一個表示式即可
print(re.findall(r"(westos|hello)\d+",'westoshello446'))
(mn):將括號裡面的內容作為一個分組
s = '<span class="red">31</span>'
print(re.findall(pattern,s))
pattern1 = r'(<span class="red">(\d+)</span>)'
s = '<span class="red">31</span>'
print(re.findall(pattern1,s))
print(re.findall(r'((westos|hello)\d+)',"westos1hello2"))
#把匹配到的內容分成組,每組以元組的形式儲存
\num:引用分組第num個字串
(?P):分組起別名
爬去貼吧中的qq郵箱
分析:實現這個效果要利用正則表示式,對得到的網頁原始碼進行匹配操作。
程式碼可分為以下幾個模組:
- 獲取原始碼:對網頁的內容進行爬取,並進行解碼
- 獲取網頁的頁數:寫一個正則表示式,從獲取到的原始碼中獲取到貼吧的總頁數。
- 獲取郵箱:寫一個郵箱的正則表示式,從獲取到的的原始碼中獲取到郵箱,具體為先獲取到頁數,然後利用迴圈對每一頁的原始碼進行獲取,然後對原始碼進行一個郵箱的提取操作,。
- 主函式:呼叫上面幾個函式,然後把函式返回的郵箱地址,寫入檔案中。
from itertools import chain
from urllib.request import urlopen
def getPageHtml(url):
#獲取網頁的原始碼檔案
obj = urlopen(url)
return obj.read().decode('utf-8')
#print(getPageHtml("http://tieba.baidu.com/p/3600458679"))
'''<li class="l_reply_num" style="margin-left:8px" ><span class="red" style="margin-right:3px">1193</span>回覆貼,共<span class="red">26</span>頁</li>'''
def getPagenum(text):
#從原始碼檔案中獲取,總頁數
pattern = r'<span class="red">(\d{0,3})</span>'
return re.findall(pattern,text)[0]
#text = getPageHtml("http://tieba.baidu.com/p/3600458679")
#print(getPagenum(text))
'''http://tieba.baidu.com/p/3600458679?pn=2'''
def getPageEMail(count):
#對所有頁數的檔案挨個進行爬取,並利用正則表示式從原始碼中,匹配到資訊
mails = []
for i in range(int(count)):
url = "http://tieba.baidu.com/p/2314539885?pn=%d" %(i+1)
text = getPageHtml(url)
#<li class="d_name" data-field='{"user_id":1159023837}'>
#[email protected]
pattern1 = r'\d{5,12}@qq\.com'
print("正在爬取http://tieba.baidu.com/p/3600458679?pn=%d的內容" %(i+1))
print(re.findall(pattern1,text))
mails.append(re.findall(pattern1,text))
return mails
def main():
text = getPageHtml("http://tieba.baidu.com/p/2314539885")
count = getPagenum(text)
email = getPageEMail(count)
#chain 方法是對不同集合中的元素進行操作時,將不同的列表連線起來
with open("mails.txt",'w') as f:
for i in chain(*email):
f.write(i+"\n")
main()
結果:
爬取圖片
from urllib.request import urlopen
def getPageHtml(url): #獲取網頁的原始碼
obj = urlopen(url)
return obj.read()
def getPagepic(text):
#從網頁的原始碼中獲取到圖片的網址,返回的是一個列表,列表裡面儲存的是當前網頁的所有圖片的地址
pattern ='<img class="BDE_Image" .*?src="(http://.*?\.jpg)".*?>'
return re.findall(pattern,text.decode('utf-8'))
url = "http://tieba.baidu.com/p/5904388543"
text = getPageHtml(url)
picurl = getPagepic(text)
#對存有圖片地址的列表進行一個遍歷,依次對圖片的網址開啟進行一個獲取圖片。
for i,v in enumerate(picurl):
with open("img/img%d.jpg" %(i+1),"wb") as f:
content = getPageHtml(v)
print("正在爬取第%d張圖片" %(i+1))
f.write(content)
結果: