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

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

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

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