[Python從零到壹] 四.網絡爬蟲之入門基礎及正則表達式抓取博客案例 | 【生長吧!Python】(python從0到1)
Python系列整體框架包括基礎語法10篇、網絡爬蟲30篇、可視化分析10篇、機器學習20篇、大數據分析20篇、圖像識別30篇、人工智能40篇、Python安全20篇、其他技巧10篇。您的關注、和轉發就是對秀璋最大的支持,知識無價人有情,希望我們都能在人生路上開心快樂、共同成長。
https://blog.csdn.net/Eastmount
https://github.com/eastmountyxz/Python-zero2one
文章目錄
一.什么是網絡爬蟲
二.正則表達式
1.re模塊
2.complie方法
3.match方法
4.search方法
5.group和groups方法
三.Python網絡數據爬取的常用模塊
1.urllib模塊
2.urlparse模塊
四.正則表達式抓取網絡數據的常見方法
1.抓取標簽間的內容
2.爬取標簽中的參數
3.字符串處理及替換
五.個人博客爬取實例
1.分析過程
2.代碼實現
六.總結
一.什么是網絡爬蟲
隨著互聯網的迅速發展,萬維網成為大量信息的載體,越來越多的網民可以通過互聯網獲取所需的信息,同時如何有效地提取并利用這些信息也成為了一個巨大的挑戰。搜索引擎(Search Engine)作為輔助人們檢索信息的工具,它成為了用戶訪問萬維網的入口和工具,常見的搜索引擎比如Google、Yahoo、百度、搜狗等。但是,這些通用性搜索引擎也存在著一定的局限性,比如搜索引擎返回的結果包含大量用戶不關心的網頁;再如它們是基于關鍵字檢索,缺乏語義理解,導致反饋的信息不準確;通用的搜索引擎無法處理非結構性數據,圖片、音頻、視頻等復雜類型的數據。
為了解決上述問題,定向抓取相關網頁資源的網絡爬蟲應運而生,下圖是Google搜索引擎的架構圖,它從萬維網中爬取相關數據,通過文本和連接分析,再進行打分排序,最后返回相關的搜索結果至瀏覽器。同時,現在比較熱門的知識圖譜也是為了解決類似的問題而提出的。
網絡爬蟲又被稱為網頁蜘蛛或網絡機器人,它是一種按照一定的規則,自動地抓取萬維網信息的程序或者腳本。網絡爬蟲根據既定的抓取目標,有選擇的訪問萬維網上的網頁與相關的鏈接,獲取所需要的信息。與通用爬蟲不同,定向爬蟲并不追求大的覆蓋,而將目標定為抓取與某一特定主題內容相關的網頁,為面向主題的用戶查詢準備數據資源。
網絡爬蟲按照系統結構和實現技術,大致可以分為以下幾種類型:通用網絡爬蟲(General Purpose Web Crawler)、聚焦網絡爬蟲(Focused Web Crawler)、增量式網絡爬蟲(Incremental Web Crawler)、深層網絡爬蟲(Deep Web Crawler)。實際的網絡爬蟲系統通常是幾種爬蟲技術相結合實現的。
數據分析通常包括前期準備、數據爬取、數據預處理、數據分析、可視化繪圖及分析評估六個步驟,如下圖所示。其中數據爬取主要劃分為四個步驟:
需求分析。首先需要分析網絡數據爬取的需求,了解所爬取主題的網址、內容分布,所獲取語料的字段、圖集等內容。
技術選擇。網頁抓取技術可以通過Python、Java、C++、C#等不同編程語言實現,主要涉及的技術包括:Urllib庫、正則表達式、Selenium、BeautifulSoup、Scrapy等技術。
網頁抓取。確定好爬取技術后,需要分析網頁的DOM樹結構,通過XPATH技術定位網頁所爬取內容的節點,再抓取數據;同時,部分網站涉及到頁面跳轉、登錄驗證等。
存儲技術。數據存儲技術主要是存儲爬取的數據信息,主要包括SQL數據庫、純文本格式、CSV\XLS文件等。
二.正則表達式
正則表達式是用于處理字符串的強大工具,通常被用來檢索、替換那些符合某種規則的文本。這篇文章首先引入正則表達式的基本概念,然后講解其常用的方法,并結合Python網絡數據爬取常用模塊和常見正則表達式的網站分析方法進行講解,最后使用正則表達式爬取了個人博客網站。
正則表達式(Regular Expression,簡稱Regex或RE)又稱為正規表示法或常規表示法,常常用來檢索、替換那些符合某個模式的文本,它首先設定好了一些特殊的字符及字符組合,通過組合的“規則字符串”來對表達式進行過濾,從而獲取或匹配我們想要的特定內容。它非常靈活,其邏輯性和功能性也非常強,并能迅速地通過表達式從字符串中找到所需信息,但對于剛接觸的人來說,比較晦澀難懂。
由于正則表達式主要應用對象是文本,因此它在各種文本編輯器中都有應用,小到著名編輯器EditPlus,大到Microsoft Word、Visual Studio等大型編輯器,都可以使用正則表達式來處理文本內容。
1.re模塊
Python通過re模塊提供對正則表達式的支持,但在使用正則表達式之前需要導入re模塊,才能調用該模塊的功能函數。
import re
其基本步驟是先將正則表達式的字符串形式編譯為Pattern實例,然后使用Pattern實例處理文本并獲得一個匹配(match)實例,再使用match實例獲得所需信息。常用的函數是findall,原型如下:
findall(string[, pos[, endpos]]) | re.findall(pattern, string[, flags])
該函數表示搜索字符串string,以列表形式返回全部能匹配的子串。其中參數re包括三個常見值,每個常見值括號內的內容是完整的寫法。
re.I(re.IGNORECASE):使匹配忽略大小寫
re.M(re.MULTILINE):允許多行匹配
re.S(re.DOTALL):匹配包括換行在內的所有字符
Pattern對象是一個編譯好的正則表達式,通過Pattern提供的一系列方法可以對文本進行匹配查找。Pattern不能直接實例化,必須使用re.compile()進行構造。
2.complie方法
re正則表達式模塊包括一些常用的操作函數,比如complie()函數。其原型如下:
compile(pattern[,flags] )
該函數根據包含正則表達式的字符串創建模式對象,返回一個pattern對象。參數flags是匹配模式,可以使用按位或“|”表示同時生效,也可以在正則表達式字符串中指定。Pattern對象是不能直接實例化的,只能通過compile方法得到。
簡單舉個實例,使用正則表達式獲取字符串中的數字內容,如下所示:
>>> 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])
參數string表示字符串;pos表示下標,pos和endpos的默認值分別為0和len(string);參數flags用于編譯pattern時指定匹配模式。
4.search方法
search方法用于查找字符串中可以匹配成功的子串。從字符串的pos下標處嘗試匹配pattern,如果pattern結束時仍可匹配,則返回一個match對象;若pattern結束時仍無法匹配,則將pos加1后重新嘗試匹配;直到pos=endpos時仍無法匹配則返回None。函數原型如下:
search(string[, pos[, endpos]]) | re.search(pattern, string[, flags])
參數string表示字符串;pos表示下標,pos和endpos的默認值分別為0和len(string));參數flags用于編譯pattern時指定匹配模式。
5.group和groups方法
group([group1, …])方法用于獲得一個或多個分組截獲的字符串,當它指定多個參數時將以元組形式返回,沒有截獲字符串的組返回None,截獲了多次的組返回最后一次截獲的子串。groups([default])方法以元組形式返回全部分組截獲的字符串,相當于多次調用group,其參數default表示沒有截獲字符串的組以這個值替代,默認為None。
三.Python網絡數據爬取的常用模塊
本小節介紹Python網絡數據爬取的常用模塊或庫,主要包括urlparse模塊、urllib模塊、urllib2模塊和requests模塊,這些模塊中的函數都是基礎知識,但也非常重要。
1.urllib模塊
本書首先介紹Python網絡數據爬取最簡單并且應用比較廣泛的第三方庫函數urllib。urllib是Python用于獲取URL(Uniform Resource Locators,統一資源定址器)的庫函數,可以用來抓取遠程數據并保存,甚至可以設置消息頭(header)、代理、超時認證等。
urllib模塊提供的上層接口讓我們像讀取本地文件一樣讀取www或ftp上的數據。它比C++、C#等其他編程語言使用起來更方便。其常用的方法如下:
urlopen
urlopen(url, data=None, proxies=None)
該方法用于創建一個遠程URL的類文件對象,然后像本地文件一樣操作這個類文件對象來獲取遠程數據。參數url表示遠程數據的路徑,一般是網址;參數data表示以post方式提交到url的數據;參數proxies用于設置代理。urlopen返回一個類文件對象。urlopen提供了如下表所示。
注意,在Python中我們可以導入相關擴展包,通過help函數查看相關的使用說明,如下圖所示。
下面通過一個實例講述Urllib庫函數爬取百度官網的實例。
# -*- coding:utf-8 -*- import urllib.request import webbrowser as web url = "http://www.baidu.com" content = urllib.request.urlopen(url) print(content.info()) #頭信息 print(content.geturl()) #請求url print(content.getcode()) #http狀態碼 #保存網頁至本地并通過瀏覽器打開 open("baidu.html","wb").write(content.read()) web.open_new_tab("baidu.html")
該段調用調用urllib.urlopen(url)函數打開百度鏈接,并輸出消息頭、url、http狀態碼等信息,如下圖所示。
代碼import webbrowser as web引用webbrowser第三方庫,然后可以使用類似于“module_name.method”調用對應的函數。open().write()表示在本地創建靜態的baidu.html文件,并讀取已經打開的百度網頁內容,執行文件寫操作。web.open_new_tab(“baidu.html”)表示通過瀏覽器打開已經下載的靜態網頁新標簽。其中下載并打開的百度官網靜態網頁“baidu.html”文件如下圖所示。
同樣可以使用web.open_new_tab(“http://www.baidu.com”)在瀏覽器中直接打開在線網頁。
urlretrieve
urlretrieve(url, filename=None, reporthook=None, data=None)
urlretrieve方法是將遠程數據下載到本地。參數filename指定了保存到本地的路徑,如果省略該參數,urllib會自動生成一個臨時文件來保存數據;參數reporthook是一個回調函數,當連接上服務器,相應的數據塊傳輸完畢時會觸發該回調,通常使用該回調函數來顯示當前的下載進度;參數data指傳遞到服務器的數據。下面通過例子來演示將新浪首頁網頁抓取到本地,保存在“D:/sina.html”文件中,同時顯示下載進度。
# -*- coding:utf-8 -*- import urllib.request # 函數功能:下載文件至本地,并顯示進度 # a-已經下載的數據塊, b-數據塊的大小, c-遠程文件的大小 def Download(a, b, c): per = 100.0 * a * b / c if per > 100: per = 100 print('%.2f' % per) url = 'http://www.sina.com.cn' local = 'd://sina.html' urllib.request.urlretrieve(url, local, Download)
上面介紹了urllib模塊中常用的兩個方法,其中urlopen()用于打開網頁,urlretrieve()方法是將遠程數據下載到本地,主要用于爬取圖片。注意,Python2可以直接引用,而Python3需要通過urllib.request調用。
# -*- coding:utf-8 -*- import urllib.request url = 'https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png' local = 'baidu.png' urllib.request.urlretrieve(url, local)
抓取百度logo圖片如下圖所示:
2.urlparse模塊
urlparse模塊主要是對url進行分析,其主要操作是拆分和合并url各個部件。它可以將url拆分為6個部分,并返回元組,也可以把拆分后的部分再組成一個url。主要有函數有urljoin、urlsplit、urlunsplit、urlparse等。
urlparse
urlparse.urlparse(urlstring[, scheme[, allow_fragments]])
該函數將urlstring值解析成6個部分,從urlstring中取得url,并返回元組(scheme, netloc, path, params, query, fragment)。該函數可以用來確定網絡協議(HTTP、FTP等)、服務器地址、文件路徑等。實例代碼如下所示。
# coding=utf-8 from urllib.parse import urlparse url = urlparse('http://www.eastmount.com/index.asp?id=001') print(url) #url解析成六部分 print(url.netloc) #輸出網址
輸出如下所示,包括scheme、netloc、path、params、query、fragment六部分內容。
>>> ParseResult( scheme='http', netloc='www.eastmount.com', path='/index.asp', params='', query='id=001', fragment='' ) www.eastmount.com >>>
同樣可以調用urlunparse()函數將一個元組內容構建成一條Url。函數如下:
urlunparse
urlparse.urlunparse(parts)
該元組類似urlparse函數,它接收元組(scheme, netloc, path, params, query, fragment)后,會重新組成一個具有正確格式的url,以便供Python的其他HTML解析模塊使用。示例代碼如下:
# coding=utf-8 import urllib.parse url = urllib.parse.urlparse('http://www.eastmount.com/index.asp?id=001') print(url) #url解析成六部分 print(url.netloc) #輸出網址 #重組URL u = urllib.parse.urlunparse(url) print(u)
輸出如下圖所示。
四.正則表達式抓取網絡數據的常見方法
1.抓取標簽間的內容
HTML語言是采用標簽對的形式來編寫網站的,包括起始標簽和結束標簽,比如< head> head>、< tr> tr>、< script>< script>等。下面講解抓取標簽對之間的文本內容,比如抓取< title>Python title>標簽對之間的“Python”內容。
(1) 抓取title標簽間的內容
'
首先我們可以采用該正則表達式來抓取起始標簽< title >和結束標簽< /title >之間的內容,“(.*?)”就代表著我們需要抓取的內容。下面這段代碼是爬取百度官網的標題,即“百度一下,你就知道”。
# coding=utf-8 import re import urllib.request url = "http://www.baidu.com/" content = urllib.request.urlopen(url).read() title = re.findall(r'
代碼調用urllib庫的urlopen()函數打開超鏈接,并調用正則表達式re庫中的findall()函數尋找title標簽間的內容。由于findall()函數是獲取所有滿足該正則表達式的文本,這里只需要輸出第一個值title[0]即可。注意,Python3需要轉換utf8編碼,否則會報錯。
下面講解另一種方法,用來獲取標題起始標簽(< title>)和結束標簽( title>)之間的內容,同樣輸出百度官網標題“百度一下,你就知道”。
# coding=utf-8 import re import urllib.request url = "http://www.baidu.com/" content = urllib.request.urlopen(url).read() pat = r'(?<=
2.抓取超鏈接標簽間的內容
在HTML中,< a href=url>超鏈接標題 a>用于標識超鏈接,下面的代碼用于獲取完整的超鏈接,同時獲取超鏈接< a>和 a>之間的標題內容。
# coding=utf-8 import re import urllib.request url = "http://www.baidu.com/" content = urllib.request.urlopen(url).read() #獲取完整超鏈接 res = r"
輸出結果部分內容如下所示,這里如果采用“print(u)”或“print(t)”語句直接輸出結果。
3.抓取tr標簽和td標簽間的內容
網頁常用的布局包括table布局或div布局,其中table表格布局中常見的標簽包括tr、th和td,表格行為tr(table row),表格數據為td(table data),表格表頭為th(table heading)。那么如何抓取這些標簽間的內容呢?下面是獲取它們之間內容的代碼。假設存在HTML代碼如下所示:
學號 | 姓名 |
---|---|
1001 | 楊秀璋 |
1002 | 燕娜 |
運行結果如下圖所示:
正則表達式爬取tr、th、td標簽之間內容的Python代碼如下。
# coding=utf-8 import re import urllib.request content = urllib.request.urlopen("test.html").read() #打開本地文件 #獲取
輸出結果如下,首先獲取tr之間的內容,然后再在tr之間內容中獲取< th>和 th>之間值,即“學號”、“姓名”,最后是獲取兩個< td>和 td>之間的內容。注意,Python3解析本地文件可能會出錯,掌握方法更重要。
如果包含屬性值,則正則表達式修改為“< td id=.?>(.?) td>”。同樣,如果不一定是id屬性開頭,則可以使用正則表達式“
2.爬取標簽中的參數
(1) 抓取超鏈接標簽的url
HTML超鏈接的基本格式為“< a href=url>鏈接內容 a>”,現在需要獲取其中的url鏈接地址,方法如下:
# coding=utf-8 import re content = ''' 新聞 hao123 地圖 視頻 ''' res = r"(?<=href=\").+?(?=\")|(?<=href=\').+?(?=\')" urls = re.findall(res, content, re.I|re.S|re.M) for url in urls: print(url)
輸出內容如下:
2.抓取圖片超鏈接標簽的url
在HTML中,我們可以看到各式各樣的圖片,其圖片標簽的基本格式為“< img src=圖片地址 />”,只有通過抓取了這些圖片的原地址,才能下載對應的圖片至本地。那么究竟怎么獲取圖片標簽中的原圖地址呢?下面這段代碼就是獲取圖片鏈接地址的方法。
content = '''''' urls = re.findall('src="(.*?)"', content, re.I|re.S|re.M) print urls # ['http://m.bai1xia.com/news/zb_users/upload/2022/05/20220530234011_30974.jpg']
原圖地址為“http://…/eastmount.jpg”,它對應一張圖片,該圖片是存儲在“www.yangxiuzhang.com”網站服務器端的,最后一個“/”后面的字段為圖片名稱,即為“eastmount.jpg”。那么如何獲取url中最后一個參數呢?
3.獲取url中最后一個參數
在使用Python爬取圖片過程中,通常會遇到圖片對應的url最后一個字段用來命名圖片的情況,如前面的“eastmount.jpg”,需要通過解析url“/”后面的參數來獲取圖片。
content = '''''' urls = 'http://m.bai1xia.com/news/zb_users/upload/2022/05/20220530234011_61810.jpg' name = urls.split('/')[-1] print name # eastmount.jpg
該段代碼urls.split(’/’)[-1]表示采用字符“/”分割字符串,并且獲取最后一個所獲取的值,即為圖片名稱“eastmount.jpg”。
3.字符串處理及替換
在使用正則表達式爬取網頁文本時,通常需要調用find()函數找到指定的位置,再進行進一步爬取,比如獲取class屬性為“infobox”的表格table,再進行定位爬取。
start = content.find(r'
1001 | 楊秀璋 |
1002 | 燕 娜 |
1003 | Python | (.*?) | (.*?) | ' texts = re.findall(res, content, re.S|re.M) for m in texts: print(m[0],m[1])
1001 | 楊秀璋 |
1002 | 顏 娜 |
1003 | Python | (.*?) | (.*?) | ' texts = re.findall(res, content, re.S|re.M) for m in texts: value0 = m[0].replace('