python爬蟲 正則表達(dá)式使用技巧及爬取個人博客的實例講解
這篇博客是自己《數(shù)據(jù)挖掘與分析》課程講到正則表達(dá)式爬蟲的相關(guān)內(nèi)容,主要簡單介紹Python正則表達(dá)式爬蟲,同時講述常見的正則表達(dá)式分析方法,最后通過實例爬取作者的個人博客網(wǎng)站。希望這篇基礎(chǔ)文章對您有所幫助,如果文章中存在錯誤或不足之處,還請海涵。真的太忙了,太長時間沒有寫博客了,抱歉~
一.正則表達(dá)式
正則表達(dá)式(Regular Expression,簡稱Regex或RE)又稱為正規(guī)表示法或常規(guī)表示法,常常用來檢索、替換那些符合某個模式的文本,它首先設(shè)定好了一些特殊的字及字符組合,通過組合的“規(guī)則字符串”來對表達(dá)式進(jìn)行過濾,從而獲取或匹配我們想要的特定內(nèi)容。它具有靈活、邏輯性和功能性非常的強,能迅速地通過表達(dá)式從字符串中找到所需信息的優(yōu)點,但對于剛接觸的人來說,比較晦澀難懂。
1.re模塊
Python通過re模塊提供對正則表達(dá)式的支持,使用正則表達(dá)式之前需要導(dǎo)入該庫。
import re
import re其基本步驟是先將正則表達(dá)式的字符串形式編譯為Pattern實例,然后使用Pattern實例處理文本并獲得一個匹配(Match)實例,再使用Match實例獲得所需信息。常用的函數(shù)是findall,原型如下:
findall(string[, pos[, endpos]]) | re.findall(pattern, string[, flags])
該函數(shù)表示搜索字符串string,以列表形式返回全部能匹配的子串。
其中參數(shù)re包括三個常見值:
(1)re.I(re.IGNORECASE):忽略大小寫(括號內(nèi)是完整寫法)
(2)re.M(re.MULTILINE):允許多行模式
(3)re.S(re.DOTALL):支持點任意匹配模式
Pattern對象是一個編譯好的正則表達(dá)式,通過Pattern提供的一系列方法可以對文本進(jìn)行匹配查找。Pattern不能直接實例化,必須使用re.compile()進(jìn)行構(gòu)造。
2.complie方法
re正則表達(dá)式模塊包括一些常用的操作函數(shù),比如complie()函數(shù)。其原型如下:
compile(pattern[,flags] )
該函數(shù)根據(jù)包含正則表達(dá)式的字符串創(chuàng)建模式對象,返回一個pattern對象。參數(shù)flags是匹配模式,可以使用按位或“|”表示同時生效,也可以在正則表達(dá)式字符串中指定。Pattern對象是不能直接實例化的,只能通過compile方法得到。
簡單舉個實例,使用正則表達(dá)式獲取字符串中的數(shù)字內(nèi)容,如下所示:
>>> import re >>> string="A1.45,b5,6.45,8.82" >>> regex = re.compile(r"\d+\.?\d*") >>> print regex.findall(string) ['1.45', '5', '6.45', '8.82'] >>>
3.match方法
match方法是從字符串的pos下標(biāo)處起開始匹配pattern,如果pattern結(jié)束時已經(jīng)匹配,則返回一個Match對象;如果匹配過程中pattern無法匹配,或者匹配未結(jié)束就已到達(dá)endpos,則返回None。該方法原型如下:
match(string[, pos[, endpos]]) | re.match(pattern, string[, flags])
參數(shù)string表示字符串;pos表示下標(biāo),pos和endpos的默認(rèn)值分別為0和len(string);參數(shù)flags用于編譯pattern時指定匹配模式。
4.search方法
search方法用于查找字符串中可以匹配成功的子串。從字符串的pos下標(biāo)處起嘗試匹配pattern,如果pattern結(jié)束時仍可匹配,則返回一個Match對象;若無法匹配,則將pos加1后重新嘗試匹配;直到pos=endpos時仍無法匹配則返回None。 函數(shù)原型如下:
search(string[, pos[, endpos]]) | re.search(pattern, string[, flags])
參數(shù)string表示字符串;pos表示下標(biāo),pos和endpos的默認(rèn)值分別為0和len(string));參數(shù)flags用于編譯pattern時指定匹配模式。
5.group和groups方法
group([group1, …])方法用于獲得一個或多個分組截獲的字符串,當(dāng)它指定多個參數(shù)時將以元組形式返回。groups([default])方法以元組形式返回全部分組截獲的字符串,相當(dāng)于調(diào)用group(1,2,…last)。default表示沒有截獲字符串的組以這個值替代,默認(rèn)為None。
二.正則表達(dá)式抓取網(wǎng)絡(luò)數(shù)據(jù)常見方法
在第三小節(jié)作者將介紹常用的正則表達(dá)式抓取網(wǎng)絡(luò)數(shù)據(jù)的一些技巧,這些技巧都是作者自然語言處理和數(shù)據(jù)抓取實際編程中的總結(jié),可能不是很系統(tǒng),但是也能給讀者提供一些抓取數(shù)據(jù)的思路以及解決實際的一些問題。
1.抓取標(biāo)簽間的內(nèi)容
HTML語言是采用標(biāo)簽對的形式來編寫網(wǎng)站的,包括起始標(biāo)簽和結(jié)束標(biāo)簽,比如<head></head>、<tr></tr>、<script><script>等。下面講解抓取標(biāo)簽對之間的文本內(nèi)容。
(1) 抓取title標(biāo)簽間的內(nèi)容
首先爬取網(wǎng)頁的標(biāo)題,采用的正則表達(dá)式為'<title>(.*?)</title>',爬取百度標(biāo)題代碼如下:
# coding=utf-8 import re import urllib url = "http://www.baidu.com/" content = urllib.urlopen(url).read() title = re.findall(r'<title>(.*?)</title>', content) print title[0] # 百度一下,你就知道
代碼調(diào)用urllib庫的urlopen()函數(shù)打開超鏈接,并借用正則表達(dá)式庫中的findall()函數(shù)尋找title標(biāo)簽間的內(nèi)容,由于findall()函數(shù)獲取所有滿足該正則表達(dá)式的文本,故輸出第一個值title[0]即可。下面是獲取標(biāo)簽的另一種方法。
pat = r'(?<=<title>).*?(?=</title>)' ex = re.compile(pat, re.M|re.S) obj = re.search(ex, content) title = obj.group() print title # 百度一下,你就知道
(2) 抓取超鏈接標(biāo)簽間的內(nèi)容
在HTML中,<a href=URL></a>用于標(biāo)識超鏈接,test03_08.py文件用于獲取完整的超鏈接和超鏈接<a>和</a>之間的內(nèi)容。
# coding=utf-8 import re import urllib url = "http://www.baidu.com/" content = urllib.urlopen(url).read() #獲取完整超鏈接 res = r"<a.*?href=.*?<\/a>" urls = re.findall(res, content) for u in urls: print unicode(u,'utf-8') #獲取超鏈接<a>和</a>之間內(nèi)容 res = r'<a .*?>(.*?)</a>' texts = re.findall(res, content, re.S|re.M) for t in texts: print unicode(t,'utf-8')
輸出結(jié)果部分內(nèi)容如下所示,這里如果直接輸出print u或print t可能會亂碼,需要調(diào)用函數(shù)unicode(u,'utf-8')進(jìn)行轉(zhuǎn)碼。
#獲取完整超鏈接 <a rel="external nofollow" rel="external nofollow" name="tj_trnews" class="mnav">新聞</a> <a rel="external nofollow" rel="external nofollow" name="tj_trhao123" class="mnav">hao123</a> <a rel="external nofollow" rel="external nofollow" name="tj_trmap" class="mnav">地圖</a> <a rel="external nofollow" rel="external nofollow" name="tj_trvideo" class="mnav">視頻</a> ... #獲取超鏈接<a>和</a>之間內(nèi)容 新聞 hao123 地圖 視頻 ...
(3) 抓取tr\td標(biāo)簽間的內(nèi)容
網(wǎng)頁中常用的布局包括table布局或div布局,其中table表格布局中常見的標(biāo)簽包括tr、th和td,表格行為tr(table row),表格數(shù)據(jù)為td(table data),表格表頭th(table heading)。那么如何抓取這些標(biāo)簽之間的內(nèi)容呢?下面代碼是獲取它們之間內(nèi)容。
假設(shè)存在HTML代碼如下所示:
<html> <head><title>表格</title></head> <body> <table border=1> <tr><th>學(xué)號</th><th>姓名</th></tr> <tr><td>1001</td><td>楊秀璋</td></tr> <tr><td>1002</td><td>嚴(yán)娜</td></tr> </table> </body> </html>
則爬取對應(yīng)值的Python代碼如下:
# coding=utf-8 import re import urllib content = urllib.urlopen("test.html").read() #打開本地文件 #獲取<tr></tr>間內(nèi)容 res = r'<tr>(.*?)</tr>' texts = re.findall(res, content, re.S|re.M) for m in texts: print m #獲取<th></th>間內(nèi)容 for m in texts: res_th = r'<th>(.*?)</th>' m_th = re.findall(res_th, m, re.S|re.M) for t in m_th: print t #直接獲取<td></td>間內(nèi)容 res = r'<td>(.*?)</td><td>(.*?)</td>' texts = re.findall(res, content, re.S|re.M) for m in texts: print m[0],m[1]
輸出結(jié)果如下,首先獲取tr之間的內(nèi)容,然后再在tr之間內(nèi)容中獲取<th>和</th>之間值,即“學(xué)號”、“姓名”,最后講述直接獲取兩個<td>之間的內(nèi)容方法。
>>> <th>學(xué)號</th><th>姓名</th> <td>1001</td><td>楊秀璋</td> <td>1002</td><td>嚴(yán)娜</td> 學(xué)號 姓名 1001 楊秀璋 1002 嚴(yán)娜 >>>
2.抓取標(biāo)簽中的參數(shù)
(1) 抓取超鏈接標(biāo)簽的URL
HTML超鏈接的基本格式為“<a href=URL>鏈接內(nèi)容</a>”,現(xiàn)在需要獲取其中的URL鏈接地址,方法如下:
# coding=utf-8 import re content = ''' <a rel="external nofollow" rel="external nofollow" name="tj_trnews" class="mnav">新聞</a> <a rel="external nofollow" rel="external nofollow" name="tj_trhao123" class="mnav">hao123</a> <a rel="external nofollow" rel="external nofollow" name="tj_trmap" class="mnav">地圖</a> <a rel="external nofollow" rel="external nofollow" name="tj_trvideo" class="mnav">視頻</a> ''' res = r"(?<=href=\").+?(?=\")|(?<=href=\').+?(?=\')" urls = re.findall(res, content, re.I|re.S|re.M) for url in urls: print url
輸出內(nèi)容如下:
>>> http://news.baidu.com http://www.hao123.com http://map.baidu.com http://v.baidu.com >>>
(2) 抓取圖片超鏈接標(biāo)簽的URL
HTML插入圖片使用標(biāo)簽的基本格式為“<img src=圖片地址 />”,則需要獲取圖片URL鏈接地址的方法如下:
content = '''<img alt="Python" src="http://www..csdn.net/eastmount.jpg" />''' urls = re.findall('src="(.*?)"', content, re.I|re.S|re.M) print urls # ['http://www..csdn.net/eastmount.jpg']
其中圖片對應(yīng)的超鏈接為“http://www..csdn.net/eastmount.jpg”,這些資源通常存儲在服務(wù)器端,最后一個“/”后面的字段即為資源的名稱,該圖片名稱為“eastmount.jpg”。那么如何獲取URL中最后一個參數(shù)呢?
(3) 獲取URL中最后一個參數(shù)
通常在使用Python爬取圖片過程中,會遇到圖片對應(yīng)的URL最后一個字段通常用于命名圖片,如前面的“eastmount.jpg”,需要通過URL“/”后面的參數(shù)獲取圖片。
content = '''<img alt="Python" src="http://www..csdn.net/eastmount.jpg" />''' urls = 'http://www..csdn.net/eastmount.jpg' name = urls.split('/')[-1] print name # eastmount.jpg
該段代碼表示采用字符“/”分割字符串,并且獲取最后一個獲取的值,即為圖片名稱。
3.字符串處理及替換
在使用正則表達(dá)式爬取網(wǎng)頁文本時,通常需要調(diào)用find()函數(shù)找到指定的位置,再進(jìn)行進(jìn)一步爬取,比如獲取class屬性為“infobox”的表格table,再進(jìn)行定位爬取。
start = content.find(r'<table class="infobox"') #起點位置 end = content.find(r'</table>') #重點點位置 infobox = text[start:end] print infobox
同時爬取過程中可能會爬取到無關(guān)變量,此時需要對無關(guān)內(nèi)容進(jìn)行過濾,這里推薦使用replace函數(shù)和正則表達(dá)式進(jìn)行處理。比如,爬取內(nèi)容如下:
# coding=utf-8 import re content = ''' <tr><td>1001</td><td>楊秀璋<br /></td></tr> <tr><td>1002</td><td>顏 娜</td></tr> <tr><td>1003</td><td><B>Python</B></td></tr> ''' res = r'<td>(.*?)</td><td>(.*?)</td>' texts = re.findall(res, content, re.S|re.M) for m in texts: print m[0],m[1]
輸出如下所示:
>>> 1001 楊秀璋<br /> 1002 顏 娜 1003 <B>Python</B> >>>
此時需要過濾多余字符串,如換行(<br />)、空格( )、加粗(<B></B>)。
過濾代碼如下:
# coding=utf-8 import re content = ''' <tr><td>1001</td><td>楊秀璋<br /></td></tr> <tr><td>1002</td><td>顏 娜</td></tr> <tr><td>1003</td><td><B>Python</B></td></tr> ''' res = r'<td>(.*?)</td><td>(.*?)</td>' texts = re.findall(res, content, re.S|re.M) for m in texts: value0 = m[0].replace('<br />', '').replace(' ', '') value1 = m[1].replace('<br />', '').replace(' ', '') if '<B>' in value1: m_value = re.findall(r'<B>(.*?)</B>', value1, re.S|re.M) print value0, m_value[0] else: print value0, value1
采用replace將字符串“<br />”或“' ”替換成空白,實現(xiàn)過濾,而加粗(<B></B>)需要使用正則表達(dá)式過濾,輸出結(jié)果如下:
>>> 1001 楊秀璋 1002 顏娜 1003 Python >>>
三.實戰(zhàn)爬取個人博客實例
在講述了正則表達(dá)式、常用網(wǎng)絡(luò)數(shù)據(jù)爬取模塊、正則表達(dá)式爬取數(shù)據(jù)常見方法等內(nèi)容之后,我們將講述一個簡單的正則表達(dá)式爬取網(wǎng)站的實例。這里作者用正則表達(dá)式爬取作者的個人博客網(wǎng)站的簡單示例,獲取所需內(nèi)容。
作者的個人網(wǎng)址“http://www.eastmountyxz.com/”打開如下圖所示。
假設(shè)現(xiàn)在需要爬取的內(nèi)容如下:
1.博客網(wǎng)址的標(biāo)題(title)內(nèi)容。
2.爬取所有圖片的超鏈接,比如爬取<img src=”xxx.jpg” />中的“xxx.jpg”。
3.分別爬取博客首頁中的四篇文章的標(biāo)題、超鏈接及摘要內(nèi)容,比如標(biāo)題為“再見北理工:憶北京研究生的編程時光”。
1.分析過程
第一步 瀏覽器源碼定位
首先通過瀏覽器定位這些元素源代碼,發(fā)現(xiàn)它們之間的規(guī)律,這稱為DOM樹文檔節(jié)點樹分析,找到所需爬取節(jié)點對應(yīng)的屬性和屬性值,如圖3.6所示。
標(biāo)題“再見北理工:憶北京研究生的編程時光”位于<div class=”essay”></div>節(jié)點下,它包括一個<h1></h1>記錄標(biāo)題,一個<p></p>記錄摘要信息,即:
<div class="essay"> <h1 style="text-align:center"> <a rel="external nofollow" > 再見北理工:憶北京研究生的編程時光 </a> </h1> <p style="text-indent: 2em;"> 兩年前,我本科畢業(yè)寫了這樣一篇文章:《 回憶自己的大學(xué)四年得與失 》,感慨了自己在北理軟院四年的所得所失;兩年后,我離開了帝都,回到了貴州家鄉(xiāng),準(zhǔn)備開啟一段新的教師生涯,在此也寫一篇文章紀(jì)念下吧! 還是那句話:這篇文章是寫給自己的,希望很多年之后,回想起自己北京的六年時光,也是美好的回憶。文章可能有點長,但希望大家像讀小說一樣耐心品讀,.... </p> </div>
其余三篇文章同樣為<div class=”essay1”></div>、
<div class=”essay2”></div>和<div class=”essay3”></div>。
第二步 正則表達(dá)式爬取標(biāo)題
網(wǎng)站的標(biāo)題通常位于<head><title>...</title></head>之間,爬取博客網(wǎng)站的標(biāo)題“秀璋學(xué)習(xí)天地”的方法是通過正則表達(dá)式“<title>(.*?)</title>”實現(xiàn),代碼如下,首先通過urlopen()函數(shù)訪問博客網(wǎng)址,然后定義正則表達(dá)式爬取。如下圖所示:
第三步 正則表達(dá)式爬取所有圖片地址
由于HTML插入圖片標(biāo)簽格式為“<img src=圖片地址 />”,則使用正則表達(dá)式獲取圖片URL鏈接地址的方法如下,獲取以“src=”開頭,以雙引號結(jié)尾的內(nèi)容即可。
import re import urllib url = "http://www.eastmountyxz.com/" content = urllib.urlopen(url).read() urls = re.findall(r'src="(.*?)"', content) for url in urls: print url
輸出共顯示了6張圖片,但每張圖片省略了博客地址“http://www.eastmountyxz.com/”,增加相關(guān)地址則可以通過瀏覽器訪問,如“http://www.eastmountyxz.com/images/11.gif”。
第四步 正則表達(dá)式爬取博客內(nèi)容
前面第一步講述了如何定位四篇文章的標(biāo)題,第一篇文章位于<div class=”essay”>和</div>標(biāo)簽之間,第二篇位于<div class=”essay1”>和</div>,依次類推。但是該HTML代碼存在一個錯誤:class屬性通常表示一類標(biāo)簽,它們的值都應(yīng)該是相同的,所以這四篇文章的class屬性都應(yīng)該是“essay”,而name或id可以用來標(biāo)識其唯一值。
這里使用find()函數(shù)定位<div class=”essay”>開頭,</div>結(jié)尾,獲取它們之間的值。比如獲取第一篇文章的標(biāo)題和超鏈接代碼如下:
import re import urllib url = "http://www.eastmountyxz.com/" content = urllib.urlopen(url).read() start = content.find(r'<div class="essay">') end = content.find(r'<div class="essay1">') print content[start:end]
該部分代碼分為三步驟:
(1) 調(diào)用urllib庫的urlopen()函數(shù)打開博客地址,并讀取內(nèi)容賦值給content變量。
(2) 調(diào)用find()函數(shù)查找特定的內(nèi)容,比如class屬性為“essay”的div標(biāo)簽,依次定位獲取開始和結(jié)束的位置。
(3) 進(jìn)行下一步分析,獲取源碼中的超鏈接和標(biāo)題等內(nèi)容。
定位這段內(nèi)容之后,再通過正則表達(dá)式獲取具體內(nèi)容,代碼如下:
import re import urllib url = "http://www.eastmountyxz.com/" content = urllib.urlopen(url).read() start = content.find(r'<div class="essay">') end = content.find(r'<div class="essay1">') page = content[start:end] res = r"(?<=href=\").+?(?=\")|(?<=href=\').+?(?=\')" t1 = re.findall(res, page) #超鏈接 print t1[0] t2 = re.findall(r'<a .*?>(.*?)</a>', page) #標(biāo)題 print t2[0] t3 = re.findall('<p style=.*?>(.*?)</p>', page, re.M|re.S) #摘要( print t3[0]
調(diào)用正則表達(dá)式分別獲取內(nèi)容,由于爬取的段落(P)存在換行內(nèi)容,所以需要加入re.M和re.S支持換行查找,最后輸出結(jié)果如下:
>>> http://blog.csdn.net/eastmount/article/details/52201984 再見北理工:憶北京研究生的編程時光 兩年前,我本科畢業(yè)寫了這樣一篇文章:《 回憶自己的大學(xué)四年得與失 》,感慨了自己在北理軟院四年的所得所失;兩年后,我離開了帝都,回到了貴州家鄉(xiāng),準(zhǔn)備開啟一段新的教師生涯,在此也寫一篇文章紀(jì)念下吧! 還是那句話:這篇文章是寫給自己的,希望很多年之后,回想起自己北京的六年時光,也是美好的回憶。文章可能有點長,但希望大家像讀小說一樣耐心品讀,.... >>>
2.代碼實現(xiàn)
完整代碼參考test03_10.py文件,代碼如下所示。
#coding:utf-8 import re import urllib url = "http://www.eastmountyxz.com/" content = urllib.urlopen(url).read() #爬取標(biāo)題 title = re.findall(r'<title>(.*?)</title>', content) print title[0] #爬取圖片地址 urls = re.findall(r'src="(.*?)"', content) for url in urls: print url #爬取內(nèi)容 start = content.find(r'<div class="essay">') end = content.find(r'<div class="essay1">') page = content[start:end] res = r"(?<=href=\").+?(?=\")|(?<=href=\').+?(?=\')" t1 = re.findall(res, page) #超鏈接 print t1[0] t2 = re.findall(r'<a .*?>(.*?)</a>', page) #標(biāo)題 print t2[0] t3 = re.findall('<p style=.*?>(.*?)</p>', page, re.M|re.S) #摘要( print t3[0] print '' start = content.find(r'<div class="essay1">') end = content.find(r'<div class="essay2">') page = content[start:end] res = r"(?<=href=\").+?(?=\")|(?<=href=\').+?(?=\')" t1 = re.findall(res, page) #超鏈接 print t1[0] t2 = re.findall(r'<a .*?>(.*?)</a>', page) #標(biāo)題 print t2[0] t3 = re.findall('<p style=.*?>(.*?)</p>', page, re.M|re.S) #摘要( print t3[0]
輸出結(jié)果如圖所示。
通過上面的代碼,讀者會發(fā)現(xiàn)使用正則表達(dá)式爬取網(wǎng)站還是比較繁瑣,尤其是定位網(wǎng)頁節(jié)點時,后面將講述Python提供的常用第三方擴(kuò)展包,利用這些包的函數(shù)進(jìn)行定向爬取。
希望這篇文字對你有所幫助,尤其是剛接觸爬蟲的同學(xué)或是遇到類似問題的同學(xué),更推薦大家使用BeautifulSoup、Selenium、Scrapy等庫來爬取數(shù)據(jù)。
以上這篇python爬蟲 正則表達(dá)式使用技巧及爬取個人博客的實例講解就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章

Python辦公自動化Word轉(zhuǎn)Excel文件批量處理

使用pandas實現(xiàn)篩選出指定列值所對應(yīng)的行

Python判斷對象是否為文件對象(file object)的三種方法示例

Django添加bootstrap框架時無法加載靜態(tài)文件的解決方式

Python解析Excle文件中的數(shù)據(jù)方法