python爬虫详解
python爬蟲(chóng)詳解
1、基本概念
1.1、什么是爬蟲(chóng)
? ? 網(wǎng)絡(luò)爬蟲(chóng),是一種按照一定規(guī)則,自動(dòng)抓取互聯(lián)網(wǎng)信息的程序或者腳本。另外一些不常使用的名字還有螞蟻、自動(dòng)索引、模擬程序或者蠕蟲(chóng)。隨著網(wǎng)絡(luò)的迅速發(fā)展,萬(wàn)維網(wǎng)成為大量信息的載體,如何有效地提取并利用這些信息成為一個(gè)巨大的挑戰(zhàn)。例如:傳統(tǒng)的通用搜索引擎AltaVista,Yahoo!和Google等,作為一個(gè)輔助人們檢索信息的工具也存在著一定的局限性,通用搜索引擎的目標(biāo)是盡可能大的網(wǎng)絡(luò)覆蓋率,返回的結(jié)果包含大量用戶不關(guān)心的網(wǎng)頁(yè),為了解決上述問(wèn)題,定向抓取相關(guān)網(wǎng)頁(yè)資源的爬蟲(chóng)應(yīng)運(yùn)而生。
? ? 由于互聯(lián)網(wǎng)數(shù)據(jù)的多樣性和資源的有限性,根據(jù)用戶需求定向抓取網(wǎng)頁(yè)并分析,已成為主流的爬取策略。只要你能通過(guò)瀏覽器訪問(wèn)的數(shù)據(jù)都可以通過(guò)爬蟲(chóng)獲取,爬蟲(chóng)的本質(zhì)是模擬瀏覽器打開(kāi)網(wǎng)頁(yè),獲取網(wǎng)頁(yè)中我們想要的那部分?jǐn)?shù)據(jù)。
1.2、Python為什么適合爬蟲(chóng)
? ? 因?yàn)閜ython的腳本特性,python易于配置,對(duì)字符的處理也非常靈活,加上python有豐富的網(wǎng)絡(luò)抓取模塊,所以?xún)烧呓?jīng)常聯(lián)系在一起。
? ??相比與其他靜態(tài)編程語(yǔ)言,如java,c#,C++,python抓取網(wǎng)頁(yè)文檔的接口更簡(jiǎn)潔;相比其他動(dòng)態(tài)腳本語(yǔ)言,如perl,shell,python的urllib2包提供了較為完整的訪問(wèn)網(wǎng)頁(yè)文檔的API。此外,抓取網(wǎng)頁(yè)有時(shí)候需要模擬瀏覽器的行為,很多網(wǎng)站對(duì)于生硬的爬蟲(chóng)抓取都是封殺的。這是我們需要模擬user agent的行為構(gòu)造合適的請(qǐng)求,譬如模擬用戶登陸、模擬session/cookie的存儲(chǔ)和設(shè)置。在python里都有非常優(yōu)秀的第三方包幫你搞定,如Requests,mechanize。
? ??抓取的網(wǎng)頁(yè)通常需要處理,比如過(guò)濾html標(biāo)簽,提取文本等。python的beautifulsoap提供了簡(jiǎn)潔的文檔處理功能,能用極短的代碼完成大部分文檔的處理。
1.3、Python爬蟲(chóng)組成部分
? ? Python爬蟲(chóng)架構(gòu)主要由五個(gè)部分組成,分別是調(diào)度器、URL管理器、網(wǎng)頁(yè)下載器、網(wǎng)頁(yè)解析器、應(yīng)用程序(爬取的有價(jià)值數(shù)據(jù))。
? ? 調(diào)度器:相當(dāng)于一臺(tái)電腦的CPU,主要負(fù)責(zé)調(diào)度URL管理器、下載器、解析器之間的協(xié)調(diào)工作。
? ? URL管理器:包括待爬取的URL地址和已爬取的URL地址,防止重復(fù)抓取URL和循環(huán)抓取URL,實(shí)現(xiàn)URL管理器主要用三種方式,通過(guò)內(nèi)存、數(shù)據(jù)庫(kù)、緩存數(shù)據(jù)庫(kù)來(lái)實(shí)現(xiàn)。
? ? 網(wǎng)頁(yè)下載器:通過(guò)傳入一個(gè)URL地址來(lái)下載網(wǎng)頁(yè),將網(wǎng)頁(yè)轉(zhuǎn)換成一個(gè)字符串,網(wǎng)頁(yè)下載器有urllib(Python官方內(nèi)置標(biāo)準(zhǔn)庫(kù))包括需要登錄、代理、和cookie,requests(第三方包)
? ? 網(wǎng)頁(yè)解析器:將一個(gè)網(wǎng)頁(yè)字符串進(jìn)行解析,可以按照我們的要求來(lái)提取出我們有用的信息,也可以根據(jù)DOM樹(shù)的解析方式來(lái)解析。網(wǎng)頁(yè)解析器有正則表達(dá)式(直觀,將網(wǎng)頁(yè)轉(zhuǎn)成字符串通過(guò)模糊匹配的方式來(lái)提取有價(jià)值的信息,當(dāng)文檔比較復(fù)雜的時(shí)候,該方法提取數(shù)據(jù)的時(shí)候就會(huì)非常的困難)、html.parser(Python自帶的)、beautifulsoup(第三方插件,可以使用Python自帶的html.parser進(jìn)行解析,也可以使用lxml進(jìn)行解析,相對(duì)于其他幾種來(lái)說(shuō)要強(qiáng)大一些)、lxml(第三方插件,可以解析 xml 和 HTML),html.parser 和 beautifulsoup 以及 lxml 都是以 DOM 樹(shù)的方式進(jìn)行解析的。
? ? 應(yīng)用程序:就是從網(wǎng)頁(yè)中提取的有用數(shù)據(jù)組成的一個(gè)應(yīng)用。
1.4、URI和URL的概念
? ? 在了解爬蟲(chóng)前,我們還需要了解一下什么是URL?
1.4.1、網(wǎng)頁(yè)、網(wǎng)站、網(wǎng)絡(luò)服務(wù)器、搜素引擎
? ? 網(wǎng)頁(yè):一份網(wǎng)頁(yè)文檔是交給瀏覽器顯示的簡(jiǎn)單文檔。這種文檔是由超文本標(biāo)記語(yǔ)言HTML來(lái)編寫(xiě)的,網(wǎng)頁(yè)文檔可以插入各種各樣不同類(lèi)型的資源:
- 樣式信息?— 控制頁(yè)面的觀感
- 腳本— 為頁(yè)面添加交互性
- 多媒體— 圖像,音頻,和視頻
? ? 網(wǎng)絡(luò)上所有可用的網(wǎng)頁(yè)都可以通過(guò)一個(gè)獨(dú)一無(wú)二的地址訪問(wèn)到。要訪問(wèn)一個(gè)頁(yè)面,只需在你的瀏覽器地址欄中鍵入頁(yè)面的地址,即URL。
? ??網(wǎng)站:網(wǎng)站是共享唯一域名的相互鏈接的網(wǎng)頁(yè)的集合。給定網(wǎng)站的每個(gè)網(wǎng)頁(yè)都提供了明確的鏈接—一般都是可點(diǎn)擊文本的形式—允許用戶從一個(gè)網(wǎng)頁(yè)跳轉(zhuǎn)到另一個(gè)網(wǎng)頁(yè)。要訪問(wèn)網(wǎng)站,請(qǐng)?jiān)跒g覽器地址欄中輸入域名,瀏覽器將顯示網(wǎng)站的主要網(wǎng)頁(yè)或主頁(yè)。
? ??網(wǎng)絡(luò)服務(wù)器:一個(gè)網(wǎng)絡(luò)服務(wù)器是一臺(tái)托管一個(gè)或多個(gè)網(wǎng)站的計(jì)算機(jī)。 "托管"意思是所有的網(wǎng)頁(yè)和它們的支持文件在那臺(tái)計(jì)算機(jī)上都可用。網(wǎng)絡(luò)服務(wù)器會(huì)根據(jù)每位用戶的請(qǐng)求,將任意網(wǎng)頁(yè)從托管的網(wǎng)站中發(fā)送到任意用戶的瀏覽器中。別把網(wǎng)站和網(wǎng)絡(luò)服務(wù)器弄混了。例如,當(dāng)你聽(tīng)到某人說(shuō):"我的網(wǎng)站沒(méi)有響應(yīng)",這實(shí)際上指的是網(wǎng)絡(luò)服務(wù)器沒(méi)響應(yīng),并因此導(dǎo)致網(wǎng)站不可用。
? ??搜索引擎:搜索引擎是一個(gè)特定類(lèi)型的網(wǎng)站,用以幫助用戶在其他網(wǎng)站中尋找網(wǎng)頁(yè)。例如:有Google,?Bing,?Yandex,?DuckDuckGo等等。瀏覽器是一個(gè)接收并顯示網(wǎng)頁(yè)的軟件,搜索引擎則是一個(gè)幫助用戶從其他網(wǎng)站中尋找網(wǎng)頁(yè)的網(wǎng)站。
1.4.2、什么是URL
? ? 早在1989年,網(wǎng)絡(luò)發(fā)明人蒂姆·伯納斯 - 李(Tim Berners-Lee)就提出了網(wǎng)站的三大支柱:
? ? 1)URL?,跟蹤Web文檔的地址系統(tǒng)
? ? 2)HTTP,一個(gè)傳輸協(xié)議,以便在給定URL時(shí)查找文檔
? ? 3)HTML, 允許嵌入超鏈接的文檔格式
? ???Web的最初目的是提供一種簡(jiǎn)單的方式來(lái)訪問(wèn),閱讀和瀏覽文本文檔。從那時(shí)起,網(wǎng)絡(luò)已經(jīng)發(fā)展到提供圖像,視頻和二進(jìn)制數(shù)據(jù)的訪問(wèn),但是這些改進(jìn)幾乎沒(méi)有改變?nèi)笾е?/p>
? ??在Web之前,很難訪問(wèn)文檔并從一個(gè)文檔跳轉(zhuǎn)到另一個(gè)文檔。WWW(World Wide Web,萬(wàn)維網(wǎng))簡(jiǎn)稱(chēng)為3W,使用統(tǒng)一資源定位符(URL)來(lái)標(biāo)志W(wǎng)WW上的各種文檔。
? ??完整的工作流程如下∶
? ??1)Web用戶使用瀏覽器(指定URL)與Web服務(wù)器建立連接,并發(fā)送瀏覽請(qǐng)求。
? ? 2)Web服務(wù)器把URL轉(zhuǎn)換為文件路徑,并返回信息給 Web瀏覽器。
? ? 3)通信完成,關(guān)閉連接。
? ? HTTP:超文本傳送協(xié)議(HTTP)是在客戶程序(如瀏覽器)與WWW服務(wù)器程序之間進(jìn)行交互所使用的協(xié)議。HTTP使用統(tǒng)一資源標(biāo)識(shí)符(Uniform Resource Identifiers, URI)來(lái)傳輸數(shù)據(jù)和建立連接,它使用TCP連接進(jìn)行可靠傳輸,服務(wù)器默認(rèn)監(jiān)聽(tīng)在80端口。?
? ? URL:代表統(tǒng)一資源定位器。URL 只不過(guò)是 Web 上給定的唯一資源的地址。理論上,每個(gè)有效的 URL 都指向一個(gè)唯一的資源。此類(lèi)資源可以是 HTML 頁(yè)面、CSS 文檔、圖像等。
? ? URL的組成:
? ? 1)協(xié)議部分(http:):它表示瀏覽器必須使用的協(xié)議來(lái)請(qǐng)求資源(協(xié)議是在計(jì)算機(jī)網(wǎng)絡(luò)中交換或傳輸數(shù)據(jù)的一套方法),通常對(duì)于網(wǎng)站,協(xié)議是 HTTPS 或 HTTP(其不安全版本)。這里使用的是HTTP協(xié)議,在"HTTP"后面的“//”為分隔符;
? ? 2)域名部分(www.example.com):一個(gè)URL中,也可以直接使用IP地址;
? ? 3)端口部分(80):域名和端口之間使用“:”作為分隔符。端口不是一個(gè)URL必須的部分,如果省略端口部分,將采用默認(rèn)端口(默認(rèn)端口可以省略)。
? ? 4)資源路徑:資源路徑包含,虛擬目錄部分和文件名部分
? ? 虛擬目錄部分(/path/to/):從域名后的第一個(gè)“/”開(kāi)始到最后一個(gè)“/”為止,是虛擬目錄部分。虛擬目錄也不是一個(gè)URL必須的部分。
? ? 文件名部分(myfile.html):從域名后的最后一個(gè)“/”開(kāi)始到“?”為止,是文件名部分,如果沒(méi)有“?”,則是從域名后的最后一個(gè)“/”開(kāi)始到“#”為止,是文件部分。
? ? 6)參數(shù)部分(key1=value1&key2=value2):從“?”開(kāi)始到“#”為止之間的部分為參數(shù)部分,又稱(chēng)搜索部分、查詢(xún)部分。
? ? 7)錨部分(SomewhereInTheDocument):從“#”開(kāi)始到最后,都是錨部分。錨點(diǎn)代表資源內(nèi)的一種“書(shū)簽”,為瀏覽器提供顯示位于該“書(shū)簽”位置的內(nèi)容的方向。例如,在 HTML 文檔中,瀏覽器將滾動(dòng)到定義錨點(diǎn)的位置;在視頻或音頻文檔上,瀏覽器將嘗試轉(zhuǎn)到錨點(diǎn)所代表的時(shí)間。
? ??URI,是uniform resource identifier,統(tǒng)一資源標(biāo)識(shí)符,用來(lái)唯一的標(biāo)識(shí)一個(gè)資源。URL是uniform resource locator,統(tǒng)一資源定位器,它是一種具體的URI,即URL可以用來(lái)標(biāo)識(shí)一個(gè)資源,而且還指明了如何locate這個(gè)資源。
1.5、引入模塊
? ? ?在進(jìn)行爬蟲(chóng)時(shí),我們會(huì)用到一些模塊,怎么去使用這些模塊呢?
? ? 模塊(module):就是用來(lái)從邏輯上組織Python代碼(變量、函數(shù)、類(lèi)),本質(zhì)就是py文件,提供代碼的可維護(hù)性,Python使用import來(lái)導(dǎo)入模塊,如果沒(méi)有基礎(chǔ)的可以先看這篇文章:https://blog.csdn.net/xiaoxianer321/article/details/116723566。
? ? 導(dǎo)入模塊:
#導(dǎo)入內(nèi)置模塊 import sys #導(dǎo)入標(biāo)準(zhǔn)庫(kù) import os #導(dǎo)入第三方庫(kù)(需要安裝:pip install bs4) import bs4 from bs4 import BeautifulSoupprint(os.getcwd()) #打印當(dāng)前工作目錄 #import bs4 導(dǎo)入整個(gè)模塊 print(bs4.BeautifulSoup.getText) #from bs4 import BeautifulSoup 導(dǎo)入指定模塊的部分屬性至當(dāng)前工作空間 print(BeautifulSoup.getText)安裝方式1:在終端中使用命令
安裝方式二:pycharm在設(shè)置中安裝
? ? 我們大概會(huì)用到以下這些模塊:
import urllib.request,urllib.error #定制URL,獲取網(wǎng)頁(yè)數(shù)據(jù) from bs4 import BeautifulSoup #網(wǎng)頁(yè)解析,獲取數(shù)據(jù) import re #正則表達(dá)式,進(jìn)行文件匹配 import xlwt #進(jìn)行excel操作 import sqlite3 #進(jìn)行SQLite數(shù)據(jù)庫(kù)操作2、urllib庫(kù)詳解
? ? Python3 中將 Python2 中的 urllib 和 urllib2 兩個(gè)庫(kù)整合為一個(gè) urllib 庫(kù),所以現(xiàn)在一般說(shuō)的都是 Python3 中的 urllib 庫(kù),它是python3內(nèi)置標(biāo)準(zhǔn)庫(kù),不需要額外安裝。
? ??urllib的四個(gè)模塊:
2.1、request模塊
? ??request模塊提供了最基本的構(gòu)造 HTTP 請(qǐng)求的方法,利用它可以模擬瀏覽器的一個(gè)請(qǐng)求發(fā)起過(guò)程,同時(shí)它還帶有處理authenticaton(授權(quán)驗(yàn)證),redirections(重定向),cookies(瀏覽器Cookies)以及其它內(nèi)容。
2.1.1、urllib.request.urlopen() 函數(shù)
? ??打開(kāi)一個(gè)url方法,返回一個(gè)文件對(duì)象HttpResponse。urlopen默認(rèn)會(huì)發(fā)送get請(qǐng)求,當(dāng)傳入data參數(shù)時(shí),則會(huì)發(fā)起POST請(qǐng)求。
語(yǔ)法: urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)參數(shù)說(shuō)明: url:請(qǐng)求的 url,也可以是request對(duì)象 data:請(qǐng)求的 data,如果設(shè)置了這個(gè)值,那么將變成 post 請(qǐng)求,如果要傳遞一個(gè)字典,則應(yīng)該用urllib.parse模塊的urlencode()函數(shù)編碼; timeout:設(shè)置網(wǎng)站的訪問(wèn)超時(shí)時(shí)間句柄對(duì)象; cafile和capath:用于 HTTPS 請(qǐng)求中,設(shè)置 CA 證書(shū)及其路徑; cadefault:忽略*cadefault*參數(shù); context:如果指定了*context*,則它必須是一個(gè)ssl.SSLContext實(shí)例。urlopen() 返回對(duì)象HTTPResponse提供的方法和屬性:1)read()、readline()、readlines()、fileno()、close():對(duì) HTTPResponse 類(lèi)型數(shù)據(jù)進(jìn)行操作; 2)info():返回 HTTPMessage 對(duì)象,表示遠(yuǎn)程服務(wù)器 返回的頭信息 ; 3)getcode():返回 HTTP 狀態(tài)碼 geturl():返回請(qǐng)求的 url; 4)getheaders():響應(yīng)的頭部信息; 5)status:返回狀態(tài)碼; 6)reason:返回狀態(tài)的詳細(xì)信息.案例一:使用urlopen()函數(shù)抓取百度
import urllib.request url = "http://www.baidu.com/" res = urllib.request.urlopen(url) # get方式請(qǐng)求 print(res) # 返回HTTPResponse對(duì)象<http.client.HTTPResponse object at 0x00000000026D3D00> # 讀取響應(yīng)體 bys = res.read() # 調(diào)用read()方法得到的是bytes對(duì)象。 print(bys) # <!DOCTYPE html><!--STATUS OK-->\n\n\n <html><head><meta... print(bys.decode("utf-8")) # 獲取字符串內(nèi)容,需要指定解碼方式,這部分我們放到html文件中就是百度的主頁(yè)# 獲取HTTP協(xié)議版本號(hào)(10 是 HTTP/1.0, 11 是 HTTP/1.1) print(res.version) # 11# 獲取響應(yīng)碼 print(res.getcode()) # 200 print(res.status) # 200# 獲取響應(yīng)描述字符串 print(res.reason) # OK# 獲取實(shí)際請(qǐng)求的頁(yè)面url(防止重定向用) print(res.geturl()) # http://www.baidu.com/# 獲取響應(yīng)頭信息,返回字符串 print(res.info()) # Bdpagetype: 1 Bdqid: 0x803fb2b9000fdebb... # 獲取響應(yīng)頭信息,返回二元元組列表 print(res.getheaders()) # [('Bdpagetype', '1'), ('Bdqid', '0x803fb2b9000fdebb'),...] print(res.getheaders()[0]) # ('Bdpagetype', '1') # 獲取特定響應(yīng)頭信息 print(res.getheader(name="Content-Type")) # text/html;charset=utf-8? ? 在簡(jiǎn)單的了解了一下使用urllib.request.urlopen(url)函數(shù),會(huì)返回一個(gè)HTTPResponse對(duì)象,對(duì)象中包含了請(qǐng)求后響應(yīng)的各項(xiàng)信息。
? ? 請(qǐng)求url最常見(jiàn)的方式莫過(guò)于發(fā)送get請(qǐng)求或post請(qǐng)求,為了更方便的看到效果,我們可以使用這個(gè)網(wǎng)站http://httpbin.org/來(lái)測(cè)試我們的請(qǐng)求。
案例二:get請(qǐng)求
我們?cè)趆ttp://httpbin.org/網(wǎng)站,發(fā)送一個(gè)get測(cè)試請(qǐng)求:
然后我們?cè)谑褂胮ython模擬瀏覽器發(fā)送一個(gè)get請(qǐng)求
import urllib.request # 請(qǐng)求的URL url = "http://httpbin.org/get" # 模擬瀏覽器打開(kāi)網(wǎng)頁(yè)(get請(qǐng)求) res = urllib.request.urlopen(url) print(res.read().decode("utf-8"))?請(qǐng)求結(jié)果如下:
我們會(huì)發(fā)現(xiàn)python模擬瀏覽器的請(qǐng)求很像。
?案例三:pos請(qǐng)求
import urllib.request import urllib.parseurl = "http://httpbin.org/post" # 按POST請(qǐng)求的格式封裝數(shù)據(jù),請(qǐng)求內(nèi)容,需要傳遞data data = bytes(urllib.parse.urlencode({"hello": "world"}), encoding="utf-8") res = urllib.request.urlopen(url, data=data) # 輸出響應(yīng)結(jié)果 print(res.read().decode("utf-8"))模擬瀏覽器發(fā)出的請(qǐng)求(提交的數(shù)據(jù)會(huì)以form表單的形式發(fā)送出去),響應(yīng)結(jié)果如下:
{"args": {}, "data": "", "files": {}, "form": {"hello": "world"}, "headers": {"Accept-Encoding": "identity", "Content-Length": "11", "Content-Type": "application/x-www-form-urlencoded", "Host": "httpbin.org", "User-Agent": "Python-urllib/3.8", "X-Amzn-Trace-Id": "Root=1-60e0754e-7ea455cc757714f14db8f2d2"}, "json": null, "origin": "183.216.63.84", "url": "http://httpbin.org/post" }案例四:?偽裝Headers
? ? 通過(guò)上面的案例,不難發(fā)現(xiàn)使用urllib發(fā)送的請(qǐng)求,比較不同的地方是:"User-Agent",使用urllib發(fā)送的會(huì)有一個(gè)默認(rèn)的Headers:User-Agent: Python-urllib/3.8。所以遇到一些驗(yàn)證User-Agent的網(wǎng)站時(shí),有可能會(huì)直接拒絕爬蟲(chóng),因此我們需要自定義Headers把自己偽裝的像一個(gè)瀏覽器一樣。
? ? 其實(shí)我們使用抓包工具也能看到http請(qǐng)求,使用抓包工具,抓取未指定請(qǐng)求頭的get請(qǐng)求如下:
? ? 而我直接使用谷歌瀏覽器時(shí),使用抓包工具獲取到的User-Agent如下:
? ? 當(dāng)然也可以直接在瀏覽器中查看:
?例如:我去爬取豆瓣網(wǎng)時(shí):
import urllib.requesturl = "http://douban.com" resp = urllib.request.urlopen(url) print(resp.read().decode('utf-8'))返回錯(cuò)誤:反爬蟲(chóng) raise HTTPError(req.full_url, code, msg, hdrs, fp) urllib.error.HTTPError: HTTP Error 418: HTTP 418 I'm a teapot客戶端錯(cuò)誤響應(yīng)代碼表示服務(wù)器拒絕煮咖啡,因?yàn)樗且粋€(gè)茶壺。這個(gè)錯(cuò)誤是對(duì)1998年愚人節(jié)玩笑的超文本咖啡壺控制協(xié)議的引用。? ? 自定義Headers:
import urllib.requesturl = "http://douban.com" # 自定義headers headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36' } req = urllib.request.Request(url, headers=headers) # urlopen(也可以是request對(duì)象) print(urllib.request.urlopen(req).read().decode('utf-8')) # 獲取字符串內(nèi)容,需要指定解碼方式? ? 當(dāng)我再次使用抓包工具,抓取指定請(qǐng)求頭的get請(qǐng)求,結(jié)果如下:
案例五:設(shè)置請(qǐng)求超時(shí)時(shí)間
? ? ?我們?cè)谂廊【W(wǎng)頁(yè)時(shí),難免會(huì)遇到請(qǐng)求超時(shí),或者無(wú)法響應(yīng)的網(wǎng)址,為了提高代碼的健壯性,我可以設(shè)置請(qǐng)求超時(shí)時(shí)間。
import urllib.request,urllib.errorurl = "http://httpbin.org/get" try:resp = urllib.request.urlopen(url, timeout=0.01)print(resp.read().decode('utf-8')) except urllib.error.URLError as e:print("time out")輸出:time out2.1.2、urllib.request.urlretrieve()?函數(shù)
? ??urlretrieve()函數(shù)的作用是直接將遠(yuǎn)程的數(shù)據(jù)下載到本地
# 語(yǔ)法:urlretrieve(url, filename=None, reporthook=None, data=None)# 參數(shù)說(shuō)明url:傳入的網(wǎng)址 filename:指定了保存本地路徑(如果參數(shù)未指定,urllib會(huì)生成一個(gè)臨時(shí)文件保存數(shù)據(jù)) reporthook:是一個(gè)回調(diào)函數(shù),當(dāng)連接上服務(wù)器、以及相應(yīng)的數(shù)據(jù)塊傳輸完畢時(shí)會(huì)觸發(fā)該回調(diào),我們可以利用這個(gè)回調(diào)函數(shù)來(lái)顯示當(dāng)前的下載進(jìn)度 data:指 post 到服務(wù)器的數(shù)據(jù),該方法返回一個(gè)包含兩個(gè)元素的(filename, headers)元組,filename 表示保存到本地的路徑,header表示服務(wù)器的響應(yīng)頭使用案例:
import urllib.requesturl = "http://www.hao6v.com/" filename = "C:\\Users\\Administrator\\Desktop\\python_3.8.5\\電影.html" def callback(blocknum,blocksize,totalsize):"""@blocknum:目前為此傳遞的數(shù)據(jù)塊數(shù)量@blocksize:每個(gè)數(shù)據(jù)塊的大小,單位是byte,字節(jié)@totalsize:遠(yuǎn)程文件的大小"""if totalsize == 0:percent = 0else:percent = blocknum * blocksize / totalsizeif percent > 1.0:percent = 1.0percent = percent * 100print("download : %.2f%%" % (percent))local_filename, headers= urllib.request.urlretrieve(url, filename, callback)案例效果:
2.2、error模塊
? ??urllib.error 模塊為 urllib.request 所引發(fā)的異常定義了異常類(lèi),基礎(chǔ)異常類(lèi)是 URLError。
2.2.1、HTTP協(xié)議(RFC2616)狀態(tài)碼定義
? ? 所有HTTP響應(yīng)的第一行都是狀態(tài)行,依次是當(dāng)前HTTP版本號(hào),3位數(shù)字組成的狀態(tài)代碼,以及描述狀態(tài)的短語(yǔ),彼此由空格分隔。?
? ? 狀態(tài)代碼的第一個(gè)數(shù)字代表當(dāng)前響應(yīng)的類(lèi)型:
? ? 1xx消息——請(qǐng)求已被服務(wù)器接收,繼續(xù)處理
? ? 2xx成功——請(qǐng)求已成功被服務(wù)器接收、理解、并接受
? ? 3xx重定向——需要后續(xù)操作才能完成這一請(qǐng)求
? ? 4xx請(qǐng)求錯(cuò)誤——4xx類(lèi)的狀態(tài)碼用于看起來(lái)客戶端有錯(cuò)誤的情況下,請(qǐng)求含有詞法錯(cuò)誤或者無(wú)法被執(zhí)行
? ? 5xx服務(wù)器錯(cuò)誤——由數(shù)字“5”打頭的響應(yīng)狀態(tài)碼表示服務(wù)器已經(jīng)明顯處于錯(cuò)誤的狀況下或沒(méi)有能力執(zhí)行請(qǐng)求,或在處理某個(gè)正確請(qǐng)求時(shí)發(fā)生錯(cuò)誤。
部分狀態(tài)碼如下:
| 狀態(tài)碼 | 定義 |
| 100 | 繼續(xù)。客戶端應(yīng)該繼續(xù)它的請(qǐng)求。該間歇響應(yīng)用于提醒客戶端服務(wù)器已經(jīng)接收和接受請(qǐng)求的開(kāi) 始部分。 客戶端應(yīng)該繼續(xù)發(fā)送請(qǐng)求的剩余部分, 或者如果請(qǐng)求已經(jīng)發(fā)送完了, 就乎略該響應(yīng)。 服務(wù)器在請(qǐng)求完成后必須發(fā)送最終響應(yīng)。 |
| 101 | 切換協(xié)議。 |
| 200 | OK。請(qǐng)求已經(jīng)成功。該響應(yīng)返回的信息取決于請(qǐng)求中使用的方法,例如: GET與所請(qǐng)求資源相對(duì)應(yīng)的實(shí)體將在響應(yīng)中發(fā)送; HEAD 與所請(qǐng)求資源相對(duì)應(yīng)的實(shí)體頭部將在響應(yīng)中發(fā)送,而沒(méi)有消息體; POST描述或包含行為結(jié)果的實(shí)體; TRACE 包含終點(diǎn)服務(wù)器收到的請(qǐng)求消息的實(shí)體。 |
| 201 | 創(chuàng)建。請(qǐng)求全部成功,且創(chuàng)建了新資源。原始服務(wù)器必須在返回 201 狀態(tài)碼之前創(chuàng)建資源。 如果該行為不能立即實(shí)施,服務(wù)器應(yīng)該代之以202(Accepted)響應(yīng)。 |
| 202 | 請(qǐng)求已經(jīng)接受處理,但是處理還沒(méi)有完成。 |
| 203 | 實(shí)體頭部中返回的元信息不是在原始服務(wù)器有效的確定集合, 而是從本地或第三方拷貝 收集的。現(xiàn)在的集合可能是原始版本的子集或超集。 |
| 204 | 服務(wù)器已經(jīng)完成請(qǐng)求,但不需要返回實(shí)體,且可能希望返回更新的元信息。響應(yīng)可能包 括新的或更新的元信息,通過(guò)實(shí)體頭部的形式。如果存在這些頭部,則應(yīng)該與所請(qǐng)求變量相 關(guān)。 |
| 205 | 重置內(nèi)容。服務(wù)器已經(jīng)完成請(qǐng)求且用戶代理應(yīng)該復(fù)位引起請(qǐng)求發(fā)送的文檔視圖。 |
| 300 | 多重選項(xiàng)。所請(qǐng)求的資源符合表述集合中的任何一個(gè),每個(gè)都有它自己的特殊位置。代理驅(qū)動(dòng)的協(xié) 商信息提供給用戶(或用戶代理)來(lái)選擇喜歡的表述,并重定向請(qǐng)求到它的位置。? |
| 301 | 所請(qǐng)求的資源已經(jīng)指定到一個(gè)新的永久 URI, 且將來(lái)任何對(duì)該資源的引用都應(yīng)該使用所 返回的 URI 之一。 |
| 302 | ?所請(qǐng)求的資源臨時(shí)存在于不同的 URI。 |
| 303 | 請(qǐng)求的響應(yīng)可以在不同的URI中發(fā)現(xiàn),且應(yīng)該使用GET方法到該資源來(lái)獲取它。 |
| 307 | 臨時(shí)重定向 |
| 400 | ?服務(wù)器不能理解請(qǐng)求,由于畸形的語(yǔ)法。 |
| 403 | 服務(wù)器理解請(qǐng)求, 但拒絕完成它。 認(rèn)證也沒(méi)用, 請(qǐng)求不該重復(fù)。 |
| 404 | 未找到。服務(wù)器不能發(fā)現(xiàn)匹配Request-URI的任何東西。 |
| 408 | 請(qǐng)求超時(shí) |
| 500 | 服務(wù)器錯(cuò)誤 |
| 503 | 服務(wù)不可用 |
| 504 | 網(wǎng)關(guān)超時(shí) |
| 505 | HTTP版本不支持 |
2.2.2、?urllib.error.URLError
import urllib.request,urllib.errortry:url = "http://www.baidus.com"resp = urllib.request.urlopen(url)print(resp.read().decode('utf-8')) # except urllib.error.HTTPError as e: # print("請(qǐng)檢查url是否正確") # URLError是urllib.request異常的超類(lèi) except urllib.error.URLError as e:if hasattr(e, "code"):print(e.code)if hasattr(e, "reason"):print(e.reason)案例效果:
? ? ?URLError,為urllib.request 所引發(fā)的基礎(chǔ)異常類(lèi),這里打印出來(lái)的403,就是urllib.error.HTTPError,另外還有一個(gè)ContentTooShortError,此異常會(huì)在 urlretrieve() 函數(shù)檢測(cè)到已下載的數(shù)據(jù)量小于期待的數(shù)據(jù)量(由 Content-Length 頭給定)時(shí)被引發(fā)。?
2.3、parse 模塊
? ? urllib.parse 模塊提供了很多解析和組建 URL 的函數(shù)。下面只列出了部分
? ? 解析url的函數(shù):urllib.parse.urlparse、urllib.parse.urlsplit、urllib.parse.urldefrag
? ? 組件url的函數(shù):urllib.parse.urlunparse、urllib.parse.urljoin
????查詢(xún)參數(shù)的構(gòu)造與解析:urllib.parse.urlencode、urllib.parse.parse_qs、
urllib.parse.parse_qsl2.3.1、urllib.parse.urlparse
# 語(yǔ)法 urllib.parse.urlparse(urlstring, scheme='', allow_fragments=True)scheme:設(shè)置默認(rèn)值 allow_fragments:是否允許fragment? ? ?將URL解析成 ParseResult 對(duì)象。對(duì)象中包含了六個(gè)元素:也就是我們前面說(shuō)過(guò)的url的組成,只不過(guò)urlparse函數(shù),將其解析成6個(gè)元素。
| scheme | 0 | URL協(xié)議 | scheme?參數(shù) |
| netloc | 1 | 網(wǎng)絡(luò)位置部分(域名) | 空字符串 |
| path | 2 | 分層路徑 | 空字符串 |
| params | 3 | 最后路徑元素的參數(shù) | 空字符串 |
| query | 4 | 查詢(xún)參數(shù) | 空字符串 |
| fragment | 5 | 片段識(shí)別 | 空字符串 |
?使用案例:
import urllib.parseurl = "http://www.example.com:80/path/to/myfile.html?key1=value&key2=value2#SomewhereIntheDocument" parsed_result = urllib.parse.urlparse(url) print(parsed_result) print('協(xié)議-scheme :', parsed_result.scheme) print('域名-netloc :', parsed_result.netloc) print('路徑-path :', parsed_result.path) print('路徑參數(shù)-params :', parsed_result.params) print('查詢(xún)參數(shù)-query :', parsed_result.query) print('片段-fragment:', parsed_result.fragment) print('用戶名-username:', parsed_result.username) print('密碼-password:', parsed_result.password) print('主機(jī)名-hostname:', parsed_result.hostname) print('端口號(hào)-port :', parsed_result.port)輸出結(jié)果: ParseResult(scheme='http', netloc='www.example.com:80', path='/path/to/myfile.html', params='', query='key1=value&key2=value2', fragment='SomewhereIntheDocument') 協(xié)議-scheme : http 域名-netloc : www.example.com:80 路徑-path : /path/to/myfile.html 路徑參數(shù)-params : 查詢(xún)參數(shù)-query : key1=value&key2=value2 片段-fragment: SomewhereIntheDocument 用戶名-username: None 密碼-password: None 主機(jī)名-hostname: www.example.com 端口號(hào)-port : 802.3.2、urllib.parse.urlsplit
? ? 這類(lèi)似于urlparse,所不同的是, urlsplit() 并不會(huì)把路徑參數(shù)(params) 從 路徑(path) 中分離出來(lái)。此函數(shù)返回一個(gè)名為tuple的5項(xiàng):(協(xié)議、域名、路徑、查詢(xún)、片段標(biāo)識(shí)符)
使用案例:
import urllib.parseurl = "http://www.example.com:80/path/to/myfile.html?key1=value&key2=value2#SomewhereIntheDocument" # urlsplit分割,唯一的區(qū)別就是不會(huì)把params拆分出來(lái) parsed_result = urllib.parse.urlsplit(url) print(parsed_result) print('協(xié)議-scheme :', parsed_result.scheme) print('域名-netloc :', parsed_result.netloc) print('路徑-path :', parsed_result.path) # parsed_result.params 沒(méi)有這項(xiàng) print('查詢(xún)參數(shù)-query :', parsed_result.query) print('片段-fragment:', parsed_result.fragment) print('用戶名-username:', parsed_result.username) print('密碼-password:', parsed_result.password) print('主機(jī)名-hostname:', parsed_result.hostname) print('端口號(hào)-port :', parsed_result.port)2.3.3、urllib.parse.urlsplit?
? ? urllib.parse.urldefrag,如果url包含片段標(biāo)識(shí)符,則返回修改后的url版本(不包含片段標(biāo)識(shí)符),并將片段標(biāo)識(shí)符作為單獨(dú)的字符串返回。如果url中沒(méi)有片段標(biāo)識(shí)符,則返回原url和空字符串。
使用案例:
import urllib.parseurl = "http://www.example.com:80/path/to/myfile.html?key1=value&key2=value2#SomewhereIntheDocument" parsed_result = urllib.parse.urldefrag(url) print(parsed_result) # DefragResult(url='http://www.example.com:80/path/to/myfile.html?key1=value&key2=value2', fragment='SomewhereIntheDocument') url1 = "http://www.example.com:80/path/to/myfile.html?key1=value&key2=value2" parsed_result1 = urllib.parse.urldefrag(url1) print(parsed_result1)# 輸出結(jié)果: # DefragResult(url='http://www.example.com:80/path/to/myfile.html?key1=value&key2=value2', fragment='SomewhereIntheDocument') # DefragResult(url='http://www.example.com:80/path/to/myfile.html?key1=value&key2=value2', fragment='')2.3.4、urllib.parse.urlunparse
? ??urlunparse()接收一個(gè)列表的參數(shù),而且列表的長(zhǎng)度是有要求的,是必須六個(gè)參數(shù)以上,否則拋出異常。
import urllib.parseurl_compos = ('http', 'www.example.com:80', '/path/to/myfile.html', 'params2', 'query=key1=value&key2=value2', 'SomewhereIntheDocument') print(urllib.parse.urlunparse(url_compos))# 輸出結(jié)果: # http://www.example.com:80/path/to/myfile.html;params2?query=key1=value&key2=value2#SomewhereIntheDocument2.3.5、urllib.parse.urljoin
import urllib.parse# 連接兩個(gè)參數(shù)的url, 將第二個(gè)參數(shù)中缺的部分用第一個(gè)參數(shù)的補(bǔ)齊,如果第二個(gè)有完整的路徑,則以第二個(gè)為主。 print(urllib.parse.urljoin('https://movie.douban.com/', 'index')) print(urllib.parse.urljoin('https://movie.douban.com/', 'https://accounts.douban.com/login'))# 輸出結(jié)果: # https://movie.douban.com/index # https://accounts.douban.com/login2.3.6、urllib.parse.urlencode
? ? 可以將一個(gè) dict 轉(zhuǎn)換成合法的查詢(xún)參數(shù)。
import urllib.parsequery_args = {'name': 'dark sun','country': '中國(guó)' } query_args = urllib.parse.urlencode(query_args) print(query_args)# 輸出結(jié)果 # name=dark+sun&country=%E4%B8%AD%E5%9B%BD2.3.7、urllib.parse.parse_qs
? ? 解析作為字符串參數(shù)提供的查詢(xún)字符串,數(shù)據(jù)作為字典返回。字典鍵是唯一的查詢(xún)變量名,值是每個(gè)名稱(chēng)的值列表。
import urllib.parsequery_args = {'name': 'dark sun','country': '中國(guó)' } query_args = urllib.parse.urlencode(query_args) print(query_args) # name=dark+sun&country=%E4%B8%AD%E5%9B%BDprint(urllib.parse.parse_qs(query_args)) # 返回字典 print(urllib.parse.parse_qsl(query_args)) # 返回列表# 輸出結(jié)果 # {'name': ['dark sun'], 'country': ['中國(guó)']} # [('name', 'dark sun'), ('country', '中國(guó)')]2.4、robotparser模塊
? ? 此模塊提供了一個(gè)單獨(dú)的類(lèi)?RobotFileParser,它可以回答關(guān)于某個(gè)特定用戶代理是否能在 Web 站點(diǎn)獲取發(fā)布?robots.txt?文件的 URL 的問(wèn)題。(/robots.txt該文件是一個(gè)簡(jiǎn)單的基于文本的訪問(wèn)控制系統(tǒng),文件向網(wǎng)絡(luò)機(jī)器人提供有關(guān)其網(wǎng)站的說(shuō)明,什么機(jī)器人可以訪問(wèn),哪些鏈接不可以訪問(wèn))
3、BeautifulSoup4
? ??學(xué)了urllib標(biāo)準(zhǔn)庫(kù)之后,我們已經(jīng)能爬到些比較正常的網(wǎng)頁(yè)源碼(html文檔)了,但這離結(jié)果還差一步——就是如何篩選我們想要的數(shù)據(jù),這時(shí)候BeautifulSoup庫(kù)就來(lái)了,BeautifulSoup目前最新版本為BeautifulSoup4。
3.1、BeautifulSoup4的簡(jiǎn)介及使用
3.1.1、BeautifulSoup4的簡(jiǎn)介
? ? Beautiful Soup 官方定義:是一個(gè)可以從HTML或XML文件中提取數(shù)據(jù)的Python庫(kù).它能夠通過(guò)你喜歡的轉(zhuǎn)換器實(shí)現(xiàn)慣用的文檔導(dǎo)航,查找,修改文檔的方式.Beautiful Soup會(huì)幫你節(jié)省數(shù)小時(shí)甚至數(shù)天的工作時(shí)間。(官網(wǎng)文檔:https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/)
? ??BeautifulSoup本身支持Python標(biāo)準(zhǔn)庫(kù)中的HTML解析器,但若想使BeautifulSoup使用html5lib/lxml 解析器解析器,可以使用下面方法。(官方推薦:使用lxml作為解析器,因?yàn)樾矢摺?
pip install html5lib pip install lxml3.1.2、BeautifulSoup4的使用
? ? ?BeautifulSoup(markup, features)接受兩個(gè)參數(shù):
? ? ?第一個(gè)參數(shù)(markup):文件對(duì)象或字符串對(duì)象
? ? ?第二個(gè)參數(shù)(features):解析器,未指定則使用python標(biāo)準(zhǔn)解析器(html.parser),但會(huì)產(chǎn)警告
from bs4 import BeautifulSoup # 導(dǎo)入BeautifulSoup4庫(kù)# 未指定就使用html.parser這個(gè)python標(biāo)準(zhǔn)解析器 BeautifulSoup(markup, "html.parser") 未指定會(huì)產(chǎn)生警告 GuessedAtParserWarning: No parser was explicitly specified# BeautifulSoup 第一個(gè)參數(shù)接受:一個(gè)文件對(duì)象或字符串對(duì)象 soup1 = BeautifulSoup(open("C:\\Users\\Administrator\\Desktop\\python_3.8.5\\電影.html")) soup2 = BeautifulSoup("<html>hello python</html>") # 得到文檔的對(duì)象 print(type(soup2)) # <class 'bs4.BeautifulSoup'> print(soup1) # <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" .... print(soup2) # <html><head></head><body>hello python</body></html>3.2、對(duì)象的種類(lèi)
? ? ?BeautifulSoup將復(fù)雜HTML文檔轉(zhuǎn)換成一個(gè)復(fù)雜的樹(shù)形結(jié)構(gòu),每個(gè)節(jié)點(diǎn)都是Python對(duì)象,所有對(duì)象可以歸納為4種: Tag , NavigableString , BeautifulSoup , Comment 。
3.2.1、Tag 標(biāo)簽對(duì)象
? ??Tag有很多方法和屬性,在?遍歷文檔樹(shù)?和?搜索文檔樹(shù)?中有詳細(xì)解釋.現(xiàn)在介紹一下tag中最重要的屬性: name和attribute。
3.2.2、NavigableString 對(duì)象(可以遍歷的字符串)
? ?被包含在一個(gè)標(biāo)簽對(duì)中的字符串內(nèi)容,可用tag.string來(lái)獲取其內(nèi)容(標(biāo)簽內(nèi)容中含注釋或其他標(biāo)簽均無(wú)法獲取)
3.2.3、BeautifulSoup 對(duì)象
? ??表示的是一個(gè)文檔(document)的全部?jī)?nèi)容
3.2.4、Comment?對(duì)象(注釋及特殊字符串)
? ??Comment 對(duì)象是一個(gè)特殊類(lèi)型的 NavigableString 對(duì)象,其實(shí)輸出的內(nèi)容仍然不包括注釋符號(hào),但是如果不好好處理它,可能會(huì)對(duì)我們的文本處理造成意想不到的麻煩。
使用案例:
from bs4 import BeautifulSoup # 導(dǎo)入BeautifulSoup4庫(kù) # python 標(biāo)準(zhǔn)解析器 未指定就使用這個(gè) BeautifulSoup(markup, "html.parser") soup2 = BeautifulSoup("<html>""<p class='boldest'>我是p標(biāo)簽<b>hello python</b></p>""<!--我是標(biāo)簽外部的內(nèi)容注釋-->""<p class='boldest2'><!--我p標(biāo)簽內(nèi)的注釋-->我是獨(dú)立的p標(biāo)簽</p>""<a><!--我a標(biāo)簽內(nèi)的注釋-->我是鏈接</a>""<h1><!--這是一個(gè)h1標(biāo)簽的注釋--></h1>""</html>","html5lib") # 得到文檔的對(duì)象 # Tag 標(biāo)簽對(duì)象 print(type(soup2.p)) # 輸出Tag對(duì)象<class 'bs4.element.Tag'> print(soup2.p.name) # 輸出Tag標(biāo)簽對(duì)象的名稱(chēng) print(soup2.p.attrs) # 輸出第一個(gè)p標(biāo)簽的屬性信息:{'class': ['boldest']} soup2.p['class'] = ['boldest', 'boldest1'] print(soup2.p.attrs) # {'class': ['boldest', 'boldest1']}# NavigableString 可以遍歷的字符串對(duì)象 print(type(soup2.b.string)) # <class 'bs4.element.NavigableString'> print(soup2.b.string) # hello python print(soup2.a.string) # None 存在注釋或者其他標(biāo)簽內(nèi)容均無(wú)法獲取 print(soup2.b.string.replace_with("hello world")) # replace_with()方法可替換標(biāo)簽中的內(nèi)容 print(soup2.b.string) # hello world# BeautifulSoup 對(duì)象 print(type(soup2)) # <class 'bs4.BeautifulSoup'> print(soup2) # <html><head></head><body><p class="boldest">我是p標(biāo)簽<b>hello python</b></p><!--我是標(biāo)簽外部的內(nèi)容注釋--><p><!--我p標(biāo)簽內(nèi)的注釋-->我是獨(dú)立的p標(biāo)簽</p></body></html> print(soup2.name) # [document]# Comment 注釋及特殊字符串(是一個(gè)特殊類(lèi)型的 NavigableString 對(duì)象) print(type(soup2.h1.string)) # <class 'bs4.element.Comment'> print(soup2.h1.string) # 這是一個(gè)h1標(biāo)簽的注釋 (利用 .string 來(lái)輸出它的內(nèi)容,注釋符被去除了,不是我們想要的) print(soup2.h1.prettify()) # 會(huì)以特殊格式輸出:<h1> <!--這是一個(gè)h1標(biāo)簽的注釋--> </h1>3.3、對(duì)象屬性-遍歷文檔
3.3.1、子節(jié)點(diǎn)
| 屬性(BeautifulSoup對(duì)象) | 描述 |
| .tag標(biāo)簽名 | 使用標(biāo)簽名獲取一個(gè)標(biāo)簽及其內(nèi)容 |
| .contents / .chidren | 將tag的子節(jié)點(diǎn)以列表的方式輸出 |
| .descendants | 可以對(duì)所有tag的子孫節(jié)點(diǎn)進(jìn)行遞歸循環(huán) |
| .string | 如果tag只有一個(gè) NavigableString 類(lèi)型子節(jié)點(diǎn),那么這個(gè)tag可以使用 .string 得到子節(jié)點(diǎn) |
| .strings/stripped_strings | tag中有多個(gè)字符串,可以使用.strings來(lái)循環(huán)獲取stripped_strings可以去除多余空白內(nèi)容 |
使用案例:
from bs4 import BeautifulSoupmarkup = '''<!DOCTYPE html> <html><head><meta charset="UTF-8"><title>I’m the title</title></head><body><h1>HelloWorld</h1><div><div><p><b>我是一個(gè)段落...</b>我是第一段我是第二段<b>我是另一個(gè)段落</b>我是第一段</p><a>我是一個(gè)鏈接</a></div><div><p>picture</p><img src="example.png"/></div></div></body> </html>''' soup = BeautifulSoup(markup, "html5lib") # BeautifulSoup 對(duì)象 print(soup.head.name) # soup.head可以獲取標(biāo)簽,獲取標(biāo)簽名 - 輸出:head print(soup.head.contents) # 將tag的子節(jié)點(diǎn)以列表的方式輸出--輸出:['\n ', <meta charset="utf-8"/>, '\n ', <title>I’m the title</title>, '\n '] print(soup.head.contents[1]) # <meta charset="utf-8"/> print(soup.head.children) # list_iterator object for child in soup.head.children:print(child) # <meta charset="utf-8"/> <title>I’m the title</title> # 標(biāo)簽中的內(nèi)容其實(shí)也是一個(gè)節(jié)點(diǎn) 使用contents和children無(wú)法直接獲取間接節(jié)點(diǎn)中的內(nèi)容,但是.descendants 屬性可以 for child in soup.head.descendants:print(child) # <meta charset="utf-8"/> <title>I’m the title</title> I’m the title print(soup.head.title.string) # 輸出:I’m the title 注:title中有其他節(jié)點(diǎn)或者注釋都無(wú)法獲取print(soup.body.div.div.p.strings) # 使用.string-None 使用.strings 獲得generator object for string in soup.body.div.div.p.stripped_strings: # stripped_strings 可以去除多余空白內(nèi)容print(repr(string)) # '我是一個(gè)段落...'# '我是第一段\n 我是第二段'# '我是另一個(gè)段落'# '我是第一段'3.3.2、父節(jié)點(diǎn)
| 屬性 | 描述 |
| .parent | 獲取某個(gè)元素的父節(jié)點(diǎn) |
| .parents | 可以遞歸得到元素的所有父輩節(jié)點(diǎn) |
使用案例:
from bs4 import BeautifulSoupmarkup = '''<!DOCTYPE html> <html><head><meta charset="UTF-8"><title>I’m the title</title></head><body><h1>HelloWorld</h1><div><div><p><b>我是一個(gè)段落...</b>我是第一段我是第二段<b>我是另一個(gè)段落</b>我是第一段</p><a>我是一個(gè)鏈接</a></div><div><p>picture</p><img src="example.png"/></div></div></body> </html>''' soup = BeautifulSoup(markup, "html5lib") # BeautifulSoup 對(duì)象 title = soup.head.title print(title.parent) # 輸出父節(jié)點(diǎn) # <head> # <meta charset="utf-8"/> # <title>I’m the title</title> # </head> print(title.parents) # generator object PageElement.parents for parent in title.parents:print(parent) # 輸出head父節(jié)點(diǎn) 和 html父節(jié)點(diǎn)3.3.3、兄弟節(jié)點(diǎn)
| 屬性 | 描述 |
| .next_sibling | 查詢(xún)兄弟節(jié)點(diǎn),表示下一個(gè)兄弟節(jié)點(diǎn) |
| .previous_sibling | 查詢(xún)兄弟節(jié)點(diǎn),表示上一個(gè)兄弟節(jié)點(diǎn) |
| .next_siblings | 對(duì)當(dāng)前節(jié)點(diǎn)的兄弟節(jié)點(diǎn)迭代輸出(下) |
| .previous_siblings | 對(duì)當(dāng)前節(jié)點(diǎn)的兄弟節(jié)點(diǎn)迭代輸出(上) |
使用案例:
from bs4 import BeautifulSoupmarkup = '''<!DOCTYPE html> <html><head><meta charset="UTF-8"><title>I’m the title</title></head><body><div><p><b id=“b1”>我是第一個(gè)段落</b><b id=“b2”>我是第二個(gè)段落</b><b id=“b3”>我是第三個(gè)段落</b><b id=“b4”>我是第四個(gè)段落</b></p><a>我是一個(gè)鏈接</a></div></body> </html>''' soup = BeautifulSoup(markup, "html5lib") # BeautifulSoup 對(duì)象 p = soup.body.div.p.b print(p) # <b id="“b1”">我是第一個(gè)段落</b> print(p.next_sibling) # <b id="“b2”">我是第二個(gè)段落</b> print(p.next_sibling.previous_sibling) # <b id="“b1”">我是第一個(gè)段落</b> print(p.next_siblings) # generator object PageElement.next_siblings for nsl in p.next_siblings:print(nsl) # <b id="“b2”">我是第二個(gè)段落</b># <b id="“b3”">我是第三個(gè)段落</b># <b id="“b4”">我是第四個(gè)段落</b>3.3.4、回退和前進(jìn)
| 屬性 | 描述 |
| .next_element | 解析下一個(gè)元素對(duì)象 |
| .previous_element | 解析上一個(gè)元素對(duì)象 |
| .next_elements | 迭代解析元素對(duì)象 |
| .previous_elements | 迭代解析元素對(duì)象 |
使用案例:
from bs4 import BeautifulSoupmarkup = '''<!DOCTYPE html> <html><head><meta charset="UTF-8"><title>I’m the title</title></head><body><div><p><b id=“b1”>我是第一個(gè)段落</b><b id=“b2”>我是第二個(gè)段落</b><b id=“b3”>我是第三個(gè)段落</b><b id=“b4”>我是第四個(gè)段落</b></p><a>我是一個(gè)鏈接<h3>h3</h3></a></div></body> </html>''' soup = BeautifulSoup(markup, "html5lib") # BeautifulSoup 對(duì)象 p = soup.body.div.p.b print(p) # <b id="“b1”">我是第一個(gè)段落</b> print(p.next_element) # 我是第一個(gè)段落 print(p.next_element.next_element) # <b id="“b2”">我是第二個(gè)段落</b> print(p.next_element.next_element.next_element) # 我是第二個(gè)段落 for element in soup.body.div.a.next_element: # 對(duì):我是一個(gè)鏈接 字符串的遍歷print(element)? ? 注:next_element,會(huì)把標(biāo)簽中的內(nèi)容,也會(huì)認(rèn)為是一個(gè)節(jié)點(diǎn)。例如:案例中取a節(jié)點(diǎn)的next_element,則是一個(gè)字符串(我是一個(gè)鏈接)
3.4、對(duì)象的屬性和方法-搜索文檔樹(shù)
? ? 這里的搜索文檔,其實(shí)就是按照某種條件去搜索過(guò)濾文檔,過(guò)濾的規(guī)則,往往會(huì)使用搜索的API,或者我們也可以自定義正則/過(guò)濾器,去搜索文檔。
3.4.1、find_all()
? ??最簡(jiǎn)單的過(guò)濾器是字符串.在搜索方法中傳入一個(gè)字符串參數(shù),Beautiful Soup會(huì)查找與字符串完整匹配的內(nèi)容。
語(yǔ)法:find_all(?name?,?attrs?,?recursive?,?string?,?**kwargs?)? 返回列表list
find_all(?name?,?attrs?,?recursive?,?string?,?**kwargs?)?參數(shù)說(shuō)明: name:查找所有名字為 name 的tag(name可以是字符串,也可以是列表) attrs: 對(duì)標(biāo)簽屬性值的檢索字符串,可標(biāo)注屬性檢索 recursive: 是否對(duì)子孫全部檢索,默認(rèn)True string: <>…</>中字符串區(qū)域的檢索字符串使用案例:
from bs4 import BeautifulSoup import remarkup = '''<!DOCTYPE html> <html><head><meta charset="UTF-8"><title id="myTitle">I’m the title</title></head><body><div><p><b id=“b1” class="bcl1">我是第一個(gè)段落</b><b>我是第二個(gè)段落</b><b id=“b3”>我是第三個(gè)段落</b><b id=“b4”>我是第四個(gè)段落</b></p><a href="www.temp.com">我是一個(gè)鏈接<h3>h3</h3></a><div id="dv1">str</div></div></body> </html>''' # 語(yǔ)法:find_all( name , attrs , recursive , string , **kwargs ) soup = BeautifulSoup(markup, "html5lib") # BeautifulSoup 對(duì)象 # 第一個(gè)參數(shù)name,可以是一個(gè)標(biāo)簽名也可以是列表 print(soup.findAll('b')) # 返回包含b標(biāo)簽的列表 [<b id="“b1”">我是第一個(gè)段落</b>, <b id="“b2”">我是第二個(gè)段落</b>, <b id="“b3”">我是第三個(gè)段落</b>, <b id="“b4”">我是第四個(gè)段落</b>] print(soup.findAll(['a', 'h3'])) # 按列表匹配多個(gè) [<a href="www.temp.com">我是一個(gè)鏈接<h3>h3</h3></a>, <h3>h3</h3>]# 第二個(gè)參數(shù)attrs,可以指定參數(shù)名字,也可以不指定 print(soup.findAll('b', 'bcl1')) # 匹配class='bcl1'的b標(biāo)簽[<b class="bcl1" id="“b1”">我是第一個(gè)段落</b>] print(soup.findAll(id="myTitle")) # 指定id [<title id="myTitle">I’m the title</title>] print(soup.find_all("b", attrs={"class": "bcl1"})) # [<b class="bcl1" id="“b1”">我是第一個(gè)段落</b>] print(soup.findAll(id=True)) # 匹配所有有id屬性的標(biāo)簽# 第三個(gè)參數(shù)recursive 默認(rèn)True 如果只想搜索tag的直接子節(jié)點(diǎn),可以使用參數(shù) recursive=False print(soup.html.find_all("title", recursive=False)) # [] recursive=False。找html的直接子節(jié)點(diǎn),是head,所以找不到title# 第四個(gè)參數(shù)string print(soup.findAll('div', string='str')) # [<div id="dv1">str</div>] print(soup.find(string=re.compile("我是第二個(gè)"))) # 搜索我是第二個(gè)段落# 其他參數(shù) limit 參數(shù) print(soup.findAll('b', limit=2)) # 當(dāng)搜索到的結(jié)果數(shù)量達(dá)到 limit 的限制時(shí),就停止搜索返回結(jié)果,[<b class="bcl1" id="“b1”">我是第一個(gè)段落</b>, <b>我是第二個(gè)段落</b>]?3.4.2、find()?
? ??find()與find_all()?的區(qū)別是?find_all()?方法的返回結(jié)果是值包含一個(gè)元素的列表,而?find()?方法直接返回結(jié)果(即找到了就不再找,只返第一個(gè)匹配的),find_all()?方法沒(méi)有找到目標(biāo)是返回空列表,?find()?方法找不到目標(biāo)時(shí),返回?None。
? ? 語(yǔ)法:find(?name?,?attrs?,?recursive?,?string?,?**kwargs?)
使用案例:
soup = BeautifulSoup(markup, "html5lib") # BeautifulSoup 對(duì)象 # 第一個(gè)參數(shù)name,可以是一個(gè)標(biāo)簽名也可以是列表 print(soup.find('b')) # 返回<b class="bcl1" id="“b1”">我是第一個(gè)段落</b>,只要找到一個(gè)即返回# 第二個(gè)參數(shù)attrs,可以指定參數(shù)名字,也可以不指定 print(soup.find('b', 'bcl1')) # <b class="bcl1" id="“b1”">我是第一個(gè)段落</b> print(soup.find(id="myTitle")) # <title id="myTitle">I’m the title</title> print(soup.find("b", attrs={"class": "bcl1"})) # <b class="bcl1" id="“b1”">我是第一個(gè)段落</b> print(soup.find(id=True)) # 匹配到第一個(gè)<title id="myTitle">I’m the title</title># 第三個(gè)參數(shù)recursive 默認(rèn)True 如果只想搜索tag的直接子節(jié)點(diǎn),可以使用參數(shù) recursive=False print(soup.html.find("title", recursive=False)) # None recursive=False。找html的直接子節(jié)點(diǎn),是head,所以找不到title# 第四個(gè)參數(shù)string print(soup.find('div', string='str')) # [<div id="dv1">str</div>] print(soup.find(string=re.compile("我是第二個(gè)"))) # 我是第二個(gè)段落3.4.3、find_parents() 和 find_parent()
? ??find_parents()?和?find_parent()?用來(lái)搜索當(dāng)前節(jié)點(diǎn)的父輩節(jié)點(diǎn)。
? ? 語(yǔ)法:
? ? find_parents(?name?,?attrs?,?recursive?,?string?,?**kwargs?)
? ? find_parent(?name?,?attrs?,?recursive?,?string?,?**kwargs?)
3.4.4、find_next_siblings() 和 find_next_sibling()
? ??find_next_siblings()?方法返回所有符合條件的后面的兄弟節(jié)點(diǎn),find_next_sibling()?只返回符合條件的后面的第一個(gè)tag節(jié)點(diǎn);
? ? 語(yǔ)法:
? ??find_next_siblings(?name?,?attrs?,?recursive?,?string?,?**kwargs?)
? ? find_next_sibling(?name?,?attrs?,?recursive?,?string?,?**kwargs?)
3.4.5、find_previous_siblings() 和 find_previous_sibling()
? ??通過(guò)?.previous_siblings?屬性對(duì)當(dāng)前tag的前面解析。find_previous_siblings()?方法返回所有符合條件的前面的兄弟節(jié)點(diǎn),find_previous_sibling()?方法返回第一個(gè)符合條件的前面的兄弟節(jié)點(diǎn);
? ? 語(yǔ)法:
? ? find_previous_siblings(?name?,?attrs?,?recursive?,?string?,?**kwargs?)
? ? find_previous_sibling(?name?,?attrs?,?recursive?,?string?,?**kwargs?)
3.4.6、find_all_next() 和 find_next()
? ??find_all_next()?方法返回所有符合條件的節(jié)點(diǎn),find_next()?方法返回第一個(gè)符合條件的節(jié)點(diǎn)。
? ? ?語(yǔ)法:
? ??find_all_next(?name?,?attrs?,?recursive?,?string?,?**kwargs?)
? ? find_next(?name?,?attrs?,?recursive?,?string?,?**kwargs?)
3.4.7、find_all_previous() 和 find_previous()
? ??find_all_previous()?方法返回所有符合條件的節(jié)點(diǎn)元素,find_previous()?方法返回第一個(gè)符合條件的節(jié)點(diǎn)元素。
? ? 語(yǔ)法:
? ? find_all_previous(?name?,?attrs?,?recursive?,?string?,?**kwargs?)
? ? find_previous(?name?,?attrs?,?recursive?,?string?,?**kwargs?)
? ? 這些其實(shí)和前面的屬性用法類(lèi)似,但是比屬性又多了像find_all()一樣的參數(shù)。這里就不再詳細(xì)介紹了,可以看官網(wǎng)的API。
3.4.8、CSS選擇器查找
? ??Beautiful Soup支持大部分的CSS選擇器,在?Tag?或?BeautifulSoup?對(duì)象的?.select()?方法中傳入字符串參數(shù), 即可使用CSS選擇器的語(yǔ)法找到tag。
使用案例:
from bs4 import BeautifulSoup import remarkup = '''<!DOCTYPE html> <html><head><meta charset="UTF-8"><title id="myTitle">I’m the title</title></head><body><div><p><b id=“b1” class="bcl1">我是第一個(gè)段落</b><b>我是第二個(gè)段落</b><b id=“b3”>我是第三個(gè)段落</b><b id=“b4”>我是第四個(gè)段落</b></p><a href="www.temp.com">我是一個(gè)鏈接<h3>h3</h3></a><div id="dv1">str</div></div></body> </html>''' soup = BeautifulSoup(markup, "html5lib") # BeautifulSoup 對(duì)象 print(soup.select("html head title")) # [<title id="myTitle">I’m the title</title>] print(soup.select("body a")) # [<a href="www.temp.com">我是一個(gè)鏈接<h3>h3</h3></a>] print(soup.select("#dv1")) # [<div id="dv1">str</div>]3.5、對(duì)象的屬性和方法-修改文檔樹(shù)
3.5.1、修改tag的名稱(chēng)和屬性
使用案例:
from bs4 import BeautifulSoupsoup = BeautifulSoup('<b class="boldest">Extremely bold</b>', "html5lib") tag = soup.b tag.name = "blockquote" print(tag) # <blockquote class="boldest">Extremely bold</blockquote> tag['class'] = 'veryBold' tag['id'] = 1 print(tag) # <blockquote class="veryBold" id="1">Extremely bold</blockquote>del tag['id'] # 刪除屬性3.5.2、修改 .string
? ??tag的?.string?屬性賦值,就相當(dāng)于用當(dāng)前的標(biāo)簽中的內(nèi)容
from bs4 import BeautifulSoupsoup = BeautifulSoup('<b class="boldest">Extremely bold</b>', "html5lib") tag = soup.b tag.string = "replace" print(tag) # <b class="boldest">replace</b>3.5.3、append()
? ? ?向tag中添加內(nèi)容
from bs4 import BeautifulSoupsoup = BeautifulSoup('<b class="boldest">Extremely bold</b>', "html5lib") tag = soup.b tag.append(" append") print(tag) # <b class="boldest">Extremely bold append</b>3.5.4、NavigableString() 和 .new_tag()
from bs4 import BeautifulSoup, NavigableString, Commentsoup = BeautifulSoup('<div><b class="boldest">Extremely bold</b></div>', "html5lib") tag = soup.div new_string = NavigableString('NavigableString') tag.append(new_string) print(tag) # <div><b class="boldest">Extremely bold</b>NavigableString</div>new_comment = soup.new_string("Nice to see you.", Comment) tag.append(new_comment) print(tag) # <div><b class="boldest">Extremely bold</b>NavigableString<!--Nice to see you.--></div># 添加標(biāo)簽,推薦使用工廠方法new_tag new_tag = soup.new_tag("a", href="http://www.example.com") tag.append(new_tag) print(tag) # <div><b class="boldest">Extremely bold</b>NavigableString<!--Nice to see you.--><a href="http://www.example.com"></a></div>3.5.5、insert()
? ??把元素插入到指定的位置
from bs4 import BeautifulSoupmarkup = '<a href="http://example.com/">I linked to <i>example.com</i></a>' soup = BeautifulSoup(markup,"html5lib") tag = soup.a tag.insert(1, "but did not endorse ") # 和append的區(qū)別就是.contents屬性獲取不一致 print(tag) # <a href="http://example.com/">I linked to but did not endorse <i>example.com</i></a> print(tag.contents) # ['I linked to ', 'but did not endorse ', <i>example.com</i>]3.5.6、insert_before() 和 insert_after()
? ??當(dāng)前tag或文本節(jié)點(diǎn)前/后插入內(nèi)容
from bs4 import BeautifulSoupmarkup = '<a href="http://example.com/">I linked to</a>' soup = BeautifulSoup(markup, "html5lib") tag = soup.new_tag("i") tag.string = "Don't" soup.a.string.insert_before(tag) print(soup.a) # <a href="http://example.com/"><i>Don't</i>I linked to</a>soup.a.i.insert_after(soup.new_string(" ever ")) print(soup.a) # <a href="http://example.com/"><i>Don't</i> ever I linked to</a>3.5.7、clear()
? ??移除當(dāng)前tag的內(nèi)容
from bs4 import BeautifulSoupmarkup = '<a href="http://example.com/">I linked to</a>' soup = BeautifulSoup(markup, "html5lib") tag = soup.a tag.clear() print(tag) # <a href="http://example.com/"></a>3.5.8、extract()
? ??將當(dāng)前tag移除文檔樹(shù),并作為方法結(jié)果返回
from bs4 import BeautifulSoupmarkup = '<a href="http://example.com/">I linked to <i>example.com</i></a>' soup = BeautifulSoup(markup, "html5lib") a_tag = soup.a i_tag = soup.i.extract()print(a_tag) # <a href="http://example.com/">I linked to </a> print(i_tag) # <i>example.com</i> 我們移除的內(nèi)容3.5.9、decompose()
? ??將當(dāng)前節(jié)點(diǎn)移除文檔樹(shù)并完全銷(xiāo)毀
from bs4 import BeautifulSoupmarkup = '<a href="http://example.com/">I linked to <i>example.com</i></a>' soup = BeautifulSoup(markup, "html5lib") a_tag = soup.a soup.i.decompose() print(a_tag) # <a href="http://example.com/">I linked to </a>3.5.10、replace_with()
? ??移除文檔樹(shù)中的某段內(nèi)容,并用新tag或文本節(jié)點(diǎn)替代它
from bs4 import BeautifulSoupmarkup = '<a href="http://example.com/">I linked to <i>example.com</i></a>' soup = BeautifulSoup(markup, "html5lib") new_tag = soup.new_tag("b") new_tag.string = "example.net" soup.a.i.replace_with(new_tag) print(soup.a) # <a href="http://example.com/">I linked to <b>example.net</b></a>3.5.11、wrap()和unwrap()
? ??wrap()對(duì)指定的tag元素進(jìn)行包裝,unwrap()移除tag內(nèi)的所有tag標(biāo)簽,該方法常被用來(lái)進(jìn)行標(biāo)記的解包
from bs4 import BeautifulSoupmarkup = '<a href="http://example.com/">I linked to <i>example.com</i></a>' soup = BeautifulSoup(markup, "html5lib") a_tag = soup.aa_tag.i.unwrap() print(a_tag) # <a href="http://example.com/">I linked to example.com</a>soup2 = BeautifulSoup("<p>I wish I was bold.</p>", "html5lib") soup2.p.string.wrap(soup2.new_tag("b")) print(soup2.p) # <p><b>I wish I was bold.</b></p>3.6、輸出
3.6.1、格式化輸出
? ? prettify()?方法將Beautiful Soup的文檔樹(shù)格式化后以Unicode編碼輸出,每個(gè)XML/HTML標(biāo)簽都獨(dú)占一行。
from bs4 import BeautifulSoupmarkup = '<a href="http://example.com/">I linked to <i>example.com</i></a>' soup = BeautifulSoup(markup, "html5lib") print(soup) # <html><head></head><body><a href="http://example.com/">I linked to <i>example.com</i></a></body></html> print(soup.prettify()) #<html># <head># </head># <body># <a href="http://example.com/"># I linked to# <i># example.com# </i># </a># </body># </html>3.6.2、壓縮輸出
? ??如果只想得到結(jié)果字符串,不重視格式,那么可以對(duì)一個(gè)?BeautifulSoup?對(duì)象或?Tag?對(duì)象使用Python的str()?方法。
3.6.3、get_text()只輸出tag中的文本內(nèi)容
? ??如果只想得到tag中包含的文本內(nèi)容,那么可以調(diào)用?get_text()?方法。
from bs4 import BeautifulSoupmarkup = '<a href="http://example.com/">I linked to <i>example.com</i>點(diǎn)我</a>' soup = BeautifulSoup(markup, "html5lib") print(soup) # <html><head></head><body><a href="http://example.com/">I linked to <i>example.com</i></a></body></html> print(str(soup)) # <html><head></head><body><a href="http://example.com/">I linked to <i>example.com</i>點(diǎn)我</a></body></html> print(soup.get_text()) # I linked to example.com4、re標(biāo)準(zhǔn)庫(kù)(模塊)
? ? BeautifulSoup庫(kù),重html文檔中篩選我們想要的數(shù)據(jù),但這些數(shù)據(jù)可能還有很多更細(xì)致的內(nèi)容,比如,我們?nèi)〉降氖遣皇俏覀兿胍逆溄印⑹遣皇俏覀冃枰崛〉泥]箱數(shù)據(jù)等等,為了更細(xì)致精確的提取數(shù)據(jù),那么正則來(lái)了。
? ? 正則表達(dá)式(英語(yǔ):Regular Expression,在代碼中常簡(jiǎn)寫(xiě)為regex、regexp或RE),是計(jì)算機(jī)科學(xué)的一個(gè)概念。正則表達(dá)式使用單個(gè)字符串來(lái)描述、匹配一系列匹配某個(gè)句法規(guī)則的字符串。在其他語(yǔ)言中,我們也經(jīng)常會(huì)接觸到正則表達(dá)式。? ? ? ? ? ? ??
?使用案例:
import re# 創(chuàng)建正則對(duì)象 pat = re.compile('\d{2}') #出現(xiàn)2次數(shù)字的 # search 在任意位置對(duì)給定的正則表達(dá)式模式搜索第一次出現(xiàn)的匹配情況 s = pat.search("12abc") print(s.group()) # 12# match 從字符串起始部分對(duì)模式進(jìn)行匹配 m = pat.match('1224abc') print(m.group()) # 12# search 和 match 的區(qū)別 匹配的位置不也一樣 s1 = re.search('foo', 'bfoo').group() print(s1) # foo try:m1 = re.match('foo','bfoo').group() # AttributeError except:print('匹配失敗') # 匹配失敗# 原生字符串(\B 不是以py字母結(jié)尾的) allList = ["py!", "py.", "python"] for li in allList:# re.match(正則表達(dá)式,要匹配的字符串)if re.match(r'py\B', li):print(li) # python# findall() s = "apple Apple APPLE" print(re.findall(r'apple', s)) # ['apple'] print(re.findall(r'apple', s, re.I)) # ['apple', 'Apple', 'APPLE']# sub()查找并替換 print(re.sub('a', 'A', 'abcdacdl')) # AbcdAcdl5、實(shí)踐案例
? ? 我們以豆瓣https://movie.douban.com/top250網(wǎng)站為例,去爬取電影信息。
5.1、第一步使用urllib庫(kù)獲取網(wǎng)頁(yè)
? ? 首先,我們分析一下這個(gè)網(wǎng)頁(yè)的結(jié)構(gòu),是一個(gè)還算比較規(guī)則的網(wǎng)頁(yè),每頁(yè)25條,一共10頁(yè)。
? ? 我們點(diǎn)擊第一頁(yè):url =?https://movie.douban.com/top250?start=0&filter=
? ? 我們點(diǎn)擊第二頁(yè):url =?https://movie.douban.com/top250?start=25&filter=
? ? 我們點(diǎn)擊第三頁(yè):url =?https://movie.douban.com/top250?start=50&filter=
import urllib.request, urllib.error# 定義基礎(chǔ)url,發(fā)現(xiàn)規(guī)律,每頁(yè)最后變動(dòng)的是start=后面的數(shù)字 baseurl = "https://movie.douban.com/top250?start="# 定義一個(gè)函數(shù)getHtmlByURL,得到指定url網(wǎng)頁(yè)的內(nèi)容 def geturl(url):# 自定義headers(偽裝,告訴豆瓣服務(wù)器,我們是什么類(lèi)型的機(jī)器,以免被反爬蟲(chóng))headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36'}# 利用Request類(lèi)來(lái)構(gòu)造自定義頭的請(qǐng)求req = urllib.request.Request(url, headers=headers)# 定義一個(gè)接收變量,用于接收html = ""try:# urlopen()方法的參數(shù),發(fā)送給服務(wù)器并接收響應(yīng)resp = urllib.request.urlopen(req)# urlopen()獲取頁(yè)面內(nèi)容,返回的數(shù)據(jù)格式為bytes類(lèi)型,需要decode()解碼,轉(zhuǎn)換成str類(lèi)型html = resp.read().decode("utf-8")except urllib.error.URLError as e:if hasattr(e, "code"):print(e.code)if hasattr(e, "reason"):print(e.reason)return htmldef main():print(geturl(baseurl + "0"))if __name__ == "__main__":main()? ? 第一步:我們已經(jīng)成功獲取到了指定的網(wǎng)頁(yè)內(nèi)容;
5.2、第二步使用BeautifulSoup和re庫(kù)解析數(shù)據(jù)
5.2.1、定位數(shù)據(jù)塊
? ? 我們發(fā)現(xiàn),我們需要的數(shù)據(jù)都在<li></li>標(biāo)簽中一個(gè)叫<div class="item"></div>中
import urllib.request, urllib.error from bs4 import BeautifulSoup import re # 定義基礎(chǔ)url,發(fā)現(xiàn)規(guī)律,每頁(yè)最后變動(dòng)的是start=后面的數(shù)字 baseurl = "https://movie.douban.com/top250?start="# 定義一個(gè)函數(shù)getHtmlByURL,得到指定url網(wǎng)頁(yè)的內(nèi)容 def geturl(url):# 自定義headers(偽裝,告訴豆瓣服務(wù)器,我們是什么類(lèi)型的機(jī)器,以免被反爬蟲(chóng))headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36'}# 利用Request類(lèi)來(lái)構(gòu)造自定義頭的請(qǐng)求req = urllib.request.Request(url, headers=headers)# 定義一個(gè)接收變量,用于接收html = ""try:# urlopen()方法的參數(shù),發(fā)送給服務(wù)器并接收響應(yīng)resp = urllib.request.urlopen(req)# urlopen()獲取頁(yè)面內(nèi)容,返回的數(shù)據(jù)格式為bytes類(lèi)型,需要decode()解碼,轉(zhuǎn)換成str類(lèi)型html = resp.read().decode("utf-8")except urllib.error.URLError as e:if hasattr(e, "code"):print(e.code)if hasattr(e, "reason"):print(e.reason)return html# 定義一個(gè)函數(shù),并解析這個(gè)網(wǎng)頁(yè) def analysisData(url):# 獲取指定網(wǎng)頁(yè)html = geturl(url)# 指定解析器解析html,得到BeautifulSoup對(duì)象soup = BeautifulSoup(html, "html5lib")# 定位我們的數(shù)據(jù)塊在哪for item in soup.findAll('div', class_="item"):print(item)return "" def main():print(analysisData(baseurl + "0"))if __name__ == "__main__":main()輸出的第一個(gè)數(shù)據(jù)塊:
<div class="item"><div class="pic"><em class="">1</em><a href="https://movie.douban.com/subject/1292052/"><img alt="肖申克的救贖" class="" src="https://img2.doubanio.com/view/photo/s_ratio_poster/public/p480747492.jpg" width="100"/></a></div><div class="info"><div class="hd"><a class="" href="https://movie.douban.com/subject/1292052/"><span class="title">肖申克的救贖</span><span class="title">?/?The Shawshank Redemption</span><span class="other">?/?月黑高飛(港) / 刺激1995(臺(tái))</span></a><span class="playable">[可播放]</span></div><div class="bd"><p class="">導(dǎo)演: 弗蘭克·德拉邦特 Frank Darabont???主演: 蒂姆·羅賓斯 Tim Robbins /...<br/>1994?/?美國(guó)?/?犯罪 劇情</p><div class="star"><span class="rating5-t"></span><span class="rating_num" property="v:average">9.7</span><span content="10.0" property="v:best"></span><span>2390982人評(píng)價(jià)</span></div><p class="quote"><span class="inq">希望讓人自由。</span></p></div></div></div> <div class="item">5.2.2、使用正則解析數(shù)據(jù)塊
import urllib.request, urllib.error from bs4 import BeautifulSoup import re# 定義基礎(chǔ)url,發(fā)現(xiàn)規(guī)律,每頁(yè)最后變動(dòng)的是start=后面的數(shù)字 baseurl = "https://movie.douban.com/top250?start="# 定義一個(gè)函數(shù)getHtmlByURL,得到指定url網(wǎng)頁(yè)的內(nèi)容 def geturl(url):# 自定義headers(偽裝,告訴豆瓣服務(wù)器,我們是什么類(lèi)型的機(jī)器,以免被反爬蟲(chóng))headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36'}# 利用Request類(lèi)來(lái)構(gòu)造自定義頭的請(qǐng)求req = urllib.request.Request(url, headers=headers)# 定義一個(gè)接收變量,用于接收html = ""try:# urlopen()方法的參數(shù),發(fā)送給服務(wù)器并接收響應(yīng)resp = urllib.request.urlopen(req)# urlopen()獲取頁(yè)面內(nèi)容,返回的數(shù)據(jù)格式為bytes類(lèi)型,需要decode()解碼,轉(zhuǎn)換成str類(lèi)型html = resp.read().decode("utf-8")except urllib.error.URLError as e:if hasattr(e, "code"):print(e.code)if hasattr(e, "reason"):print(e.reason)return html# 定義正則對(duì)象獲取指定的內(nèi)容 # 提取鏈接(鏈接的格式都是<a href="開(kāi)頭的) findLink = re.compile(r'<a href="(.*?)">') # 提取圖片 findImgSrc = re.compile(r'<img.*src="(.*?)"', re.S) # re.S讓 '.' 特殊字符匹配任何字符,包括換行符; # 提取影片名稱(chēng) findTitle = re.compile(r'<span class="title">(.*)</span>') # 提取影片評(píng)分 findRating = re.compile(r'<span class="rating_num" property="v:average">(.*)</span>') # 提取評(píng)價(jià)人數(shù) findJudge = re.compile(r'<span>(\d*)人評(píng)價(jià)</span>') # 提取簡(jiǎn)介 inq = re.compile(r'<span class="inq">(.*)</span>') # 提取相關(guān)內(nèi)容 findBd = re.compile(r'<p class="">(.*)</p>(.*)<div', re.S)# 定義一個(gè)函數(shù),并解析這個(gè)網(wǎng)頁(yè) def analysisData(baseurl):# 獲取指定網(wǎng)頁(yè)html = geturl(baseurl)# 指定解析器解析html,得到BeautifulSoup對(duì)象soup = BeautifulSoup(html, "html5lib")dataList = []# 定位我們的數(shù)據(jù)塊在哪for item in soup.find_all('div', class_="item"):# item 是 bs4.element.Tag 對(duì)象,這里將其轉(zhuǎn)換成字符串來(lái)處理item = str(item)# 定義一個(gè)列表 來(lái)存儲(chǔ)每一個(gè)電影解析的內(nèi)容data = []# findall返回的是一個(gè)列表,這里提取鏈接link = re.findall(findLink, item)[0]data.append(link) # 添加鏈接img = re.findall(findImgSrc, item)[0]data.append(img) # 添加圖片鏈接title = re.findall(findTitle, item)# 一般都有一個(gè)中文名 一個(gè)外文名if len(title) == 2:# ['肖申克的救贖', '\xa0/\xa0The Shawshank Redemption']titlename = title[0] + title[1].replace(u'\xa0', '')else:titlename = title[0] + ""data.append(titlename) # 添加標(biāo)題pf = re.findall(findRating, item)[0]data.append(pf)pjrs = re.findall(findJudge, item)[0]data.append(pjrs)# 有的可能沒(méi)有inqInfo = re.findall(inq, item)if len(inqInfo) == 0:data.append(" ")else:data.append(inqInfo[0])bd = re.findall(findBd, item)[0]# [('\n 導(dǎo)演: 弗蘭克·德拉邦特 Frank Darabont\xa0\xa0\xa0主演: 蒂姆·羅賓斯 Tim Robbins /...<br/>\n 1994\xa0/\xa0美國(guó)\xa0/\xa0犯罪 劇情\n ', '\n\n \n ')]bd[0].replace(u'\xa0', '').replace('<br/>', '')bd = re.sub('<\\s*b\\s*r\\s*/\\s*>', "", bd[0])bd = re.sub('(\\s+)?', '', bd)data.append(bd)dataList.append(data)return dataListdef main():print(analysisData(baseurl + "0"))if __name__ == "__main__":main()第一頁(yè)解析結(jié)果:后面需要對(duì)analysisData稍加改造,將豆瓣Top250的10頁(yè)進(jìn)行處理
[['https://movie.douban.com/subject/1292052/', 'https://img2.doubanio.com/view/photo/s_ratio_poster/public/p480747492.jpg', '肖申克的救贖/The Shawshank Redemption', '9.7', '2391074', '希望讓人自由。', '導(dǎo)演:弗蘭克·德拉邦特FrankDarabont主演:蒂姆·羅賓斯TimRobbins/...1994/美國(guó)/犯罪劇情'], ['https://movie.douban.com/subject/1291546/', 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2561716440.jpg', '霸王別姬', '9.6', '1780355', '風(fēng)華絕代。', '導(dǎo)演:陳凱歌KaigeChen主演:張國(guó)榮LeslieCheung/張豐毅FengyiZha...1993/中國(guó)大陸中國(guó)香港/劇情愛(ài)情同性'], ['https://movie.douban.com/subject/1292720/', 'https://img2.doubanio.com/view/photo/s_ratio_poster/public/p2372307693.jpg', '阿甘正傳/Forrest Gump', '9.5', '1800723', '一部美國(guó)近現(xiàn)代史。', '導(dǎo)演:羅伯特·澤米吉斯RobertZemeckis主演:湯姆·漢克斯TomHanks/...1994/美國(guó)/劇情愛(ài)情'], ['https://movie.douban.com/subject/1295644/', 'https://img2.doubanio.com/view/photo/s_ratio_poster/public/p511118051.jpg', '這個(gè)殺手不太冷/Léon', '9.4', '1971155', '怪蜀黍和小蘿莉不得不說(shuō)的故事。', '導(dǎo)演:呂克·貝松LucBesson主演:讓·雷諾JeanReno/娜塔莉·波特曼...1994/法國(guó)美國(guó)/劇情動(dòng)作犯罪'], ['https://movie.douban.com/subject/1292722/', 'https://img9.doubanio.com/view/photo/s_ratio_poster/public/p457760035.jpg', '泰坦尼克號(hào)/Titanic', '9.4', '1762280', '失去的才是永恒的。 ', '導(dǎo)演:詹姆斯·卡梅隆JamesCameron主演:萊昂納多·迪卡普里奧Leonardo...1997/美國(guó)墨西哥澳大利亞加拿大/劇情愛(ài)情災(zāi)難'], ['https://movie.douban.com/subject/1292063/', 'https://img2.doubanio.com/view/photo/s_ratio_poster/public/p2578474613.jpg', '美麗人生/La vita è bella', '9.6', '1105760', '最美的謊言。', '導(dǎo)演:羅伯托·貝尼尼RobertoBenigni主演:羅伯托·貝尼尼RobertoBeni...1997/意大利/劇情喜劇愛(ài)情戰(zhàn)爭(zhēng)'], ['https://movie.douban.com/subject/1291561/', 'https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2557573348.jpg', '千與千尋/千と千尋の神隠し', '9.4', '1877996', '最好的宮崎駿,最好的久石讓。 ', '導(dǎo)演:宮崎駿HayaoMiyazaki主演:柊瑠美RumiH?ragi/入野自由Miy...2001/日本/劇情動(dòng)畫(huà)奇幻'], ['https://movie.douban.com/subject/1295124/', 'https://img2.doubanio.com/view/photo/s_ratio_poster/public/p492406163.jpg', "辛德勒的名單/Schindler's List", '9.5', '918645', '拯救一個(gè)人,就是拯救整個(gè)世界。', '導(dǎo)演:史蒂文·斯皮爾伯格StevenSpielberg主演:連姆·尼森LiamNeeson...1993/美國(guó)/劇情歷史戰(zhàn)爭(zhēng)'], ['https://movie.douban.com/subject/3541415/', 'https://img2.doubanio.com/view/photo/s_ratio_poster/public/p2616355133.jpg', '盜夢(mèng)空間/Inception', '9.3', '1734973', '諾蘭給了我們一場(chǎng)無(wú)法盜取的夢(mèng)。', '導(dǎo)演:克里斯托弗·諾蘭ChristopherNolan主演:萊昂納多·迪卡普里奧Le...2010/美國(guó)英國(guó)/劇情科幻懸疑冒險(xiǎn)'], ['https://movie.douban.com/subject/3011091/', 'https://img1.doubanio.com/view/photo/s_ratio_poster/public/p524964039.jpg', "忠犬八公的故事/Hachi: A Dog's Tale", '9.4', '1192778', '永遠(yuǎn)都不能忘記你所愛(ài)的人。', '導(dǎo)演:萊塞·霍爾斯道姆LasseHallstr?m主演:理查·基爾RichardGer...2009/美國(guó)英國(guó)/劇情'], ['https://movie.douban.com/subject/1889243/', 'https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2614988097.jpg', '星際穿越/Interstellar', '9.3', '1408128', '愛(ài)是一種力量,讓我們超越時(shí)空感知它的存在。', '導(dǎo)演:克里斯托弗·諾蘭ChristopherNolan主演:馬修·麥康納MatthewMc...2014/美國(guó)英國(guó)加拿大/劇情科幻冒險(xiǎn)'], ['https://movie.douban.com/subject/1292064/', 'https://img2.doubanio.com/view/photo/s_ratio_poster/public/p479682972.jpg', '楚門(mén)的世界/The Truman Show', '9.3', '1325913', '如果再也不能見(jiàn)到你,祝你早安,午安,晚安。', '導(dǎo)演:彼得·威爾PeterWeir主演:金·凱瑞JimCarrey/勞拉·琳妮Lau...1998/美國(guó)/劇情科幻'], ['https://movie.douban.com/subject/1292001/', 'https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2574551676.jpg', "海上鋼琴師/La leggenda del pianista sull'oceano", '9.3', '1409712', '每個(gè)人都要走一條自己堅(jiān)定了的路,就算是粉身碎骨。 ', '導(dǎo)演:朱塞佩·托納多雷GiuseppeTornatore主演:蒂姆·羅斯TimRoth/...1998/意大利/劇情音樂(lè)'], ['https://movie.douban.com/subject/3793023/', 'https://img2.doubanio.com/view/photo/s_ratio_poster/public/p579729551.jpg', '三傻大鬧寶萊塢/3 Idiots', '9.2', '1583056', '英俊版憨豆,高情商版謝耳朵。', '導(dǎo)演:拉庫(kù)馬·希拉尼RajkumarHirani主演:阿米爾·汗AamirKhan/卡...2009/印度/劇情喜劇愛(ài)情歌舞'], ['https://movie.douban.com/subject/2131459/', 'https://img2.doubanio.com/view/photo/s_ratio_poster/public/p1461851991.jpg', '機(jī)器人總動(dòng)員/WALL·E', '9.3', '1113357', '小瓦力,大人生。', '導(dǎo)演:安德魯·斯坦頓AndrewStanton主演:本·貝爾特BenBurtt/艾麗...2008/美國(guó)/科幻動(dòng)畫(huà)冒險(xiǎn)'], ['https://movie.douban.com/subject/1291549/', 'https://img2.doubanio.com/view/photo/s_ratio_poster/public/p1910824951.jpg', '放牛班的春天/Les choristes', '9.3', '1098339', '天籟一般的童聲,是最接近上帝的存在。 ', '導(dǎo)演:克里斯托夫·巴拉蒂ChristopheBarratier主演:熱拉爾·朱尼奧Gé...2004/法國(guó)瑞士德國(guó)/劇情喜劇音樂(lè)'], ['https://movie.douban.com/subject/1307914/', 'https://img2.doubanio.com/view/photo/s_ratio_poster/public/p2564556863.jpg', '無(wú)間道/無(wú)間道', '9.3', '1074152', '香港電影史上永不過(guò)時(shí)的杰作。', '導(dǎo)演:劉偉強(qiáng)/麥兆輝主演:劉德華/梁朝偉/黃秋生2002/中國(guó)香港/劇情犯罪驚悚'], ['https://movie.douban.com/subject/25662329/', 'https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2614500649.jpg', '瘋狂動(dòng)物城/Zootopia', '9.2', '1555912', '迪士尼給我們營(yíng)造的烏托邦就是這樣,永遠(yuǎn)善良勇敢,永遠(yuǎn)出乎意料。', '導(dǎo)演:拜倫·霍華德ByronHoward/瑞奇·摩爾RichMoore主演:金妮弗·...2016/美國(guó)/喜劇動(dòng)畫(huà)冒險(xiǎn)'], ['https://movie.douban.com/subject/1292213/', 'https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2455050536.jpg', '大話西游之大圣娶親/西遊記大結(jié)局之仙履奇緣', '9.2', '1283551', '一生所愛(ài)。', '導(dǎo)演:劉鎮(zhèn)偉JeffreyLau主演:周星馳StephenChow/吳孟達(dá)ManTatNg...1995/中國(guó)香港中國(guó)大陸/喜劇愛(ài)情奇幻古裝'], ['https://movie.douban.com/subject/5912992/', 'https://img9.doubanio.com/view/photo/s_ratio_poster/public/p1363250216.jpg', '熔爐/???', '9.3', '778782', '我們一路奮戰(zhàn)不是為了改變世界,而是為了不讓世界改變我們。', '導(dǎo)演:黃東赫Dong-hyukHwang主演:孔侑YooGong/鄭有美Yu-miJung/...2011/韓國(guó)/劇情'], ['https://movie.douban.com/subject/1291841/', 'https://img9.doubanio.com/view/photo/s_ratio_poster/public/p616779645.jpg', '教父/The Godfather', '9.3', '781422', '千萬(wàn)不要記恨你的對(duì)手,這樣會(huì)讓你失去理智。', '導(dǎo)演:弗朗西斯·福特·科波拉FrancisFordCoppola主演:馬龍·白蘭度M...1972/美國(guó)/劇情犯罪'], ['https://movie.douban.com/subject/1849031/', 'https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2614359276.jpg', '當(dāng)幸福來(lái)敲門(mén)/The Pursuit of Happyness', '9.1', '1273152', '平民勵(lì)志片。 ', '導(dǎo)演:加布里爾·穆奇諾GabrieleMuccino主演:威爾·史密斯WillSmith...2006/美國(guó)/劇情傳記家庭'], ['https://movie.douban.com/subject/1291560/', 'https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2540924496.jpg', '龍貓/となりのトトロ', '9.2', '1062785', '人人心中都有個(gè)龍貓,童年就永遠(yuǎn)不會(huì)消失。', '導(dǎo)演:宮崎駿HayaoMiyazaki主演:日高法子NorikoHidaka/坂本千夏Ch...1988/日本/動(dòng)畫(huà)奇幻冒險(xiǎn)'], ['https://movie.douban.com/subject/3319755/', 'https://img1.doubanio.com/view/photo/s_ratio_poster/public/p501177648.jpg', '怦然心動(dòng)/Flipped', '9.1', '1511459', '真正的幸福是來(lái)自?xún)?nèi)心深處。', '導(dǎo)演:羅伯·萊納RobReiner主演:瑪?shù)铝铡た_爾MadelineCarroll/卡...2010/美國(guó)/劇情喜劇愛(ài)情'], ['https://movie.douban.com/subject/1296141/', 'https://img1.doubanio.com/view/photo/s_ratio_poster/public/p1505392928.jpg', '控方證人/Witness for the Prosecution', '9.6', '378892', '比利·懷德滿分作品。', '導(dǎo)演:比利·懷爾德BillyWilder主演:泰隆·鮑華TyronePower/瑪琳·...1957/美國(guó)/劇情犯罪懸疑']]5.3、將數(shù)據(jù)導(dǎo)出excel
import urllib.request, urllib.error from bs4 import BeautifulSoup import re import xlwt# 定義基礎(chǔ)url,發(fā)現(xiàn)規(guī)律,每頁(yè)最后變動(dòng)的是start=后面的數(shù)字 baseurl = "https://movie.douban.com/top250?start="# 定義一個(gè)函數(shù)getHtmlByURL,得到指定url網(wǎng)頁(yè)的內(nèi)容 def geturl(url):# 自定義headers(偽裝,告訴豆瓣服務(wù)器,我們是什么類(lèi)型的機(jī)器,以免被反爬蟲(chóng))headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36'}# 利用Request類(lèi)來(lái)構(gòu)造自定義頭的請(qǐng)求req = urllib.request.Request(url, headers=headers)# 定義一個(gè)接收變量,用于接收html = ""try:# urlopen()方法的參數(shù),發(fā)送給服務(wù)器并接收響應(yīng)resp = urllib.request.urlopen(req)# urlopen()獲取頁(yè)面內(nèi)容,返回的數(shù)據(jù)格式為bytes類(lèi)型,需要decode()解碼,轉(zhuǎn)換成str類(lèi)型html = resp.read().decode("utf-8")except urllib.error.URLError as e:if hasattr(e, "code"):print(e.code)if hasattr(e, "reason"):print(e.reason)return html# 定義正則對(duì)象獲取指定的內(nèi)容 # 提取鏈接(鏈接的格式都是<a href="開(kāi)頭的) findLink = re.compile(r'<a href="(.*?)">') # 提取圖片 findImgSrc = re.compile(r'<img.*src="(.*?)"', re.S) # re.S讓 '.' 特殊字符匹配任何字符,包括換行符; # 提取影片名稱(chēng) findTitle = re.compile(r'<span class="title">(.*)</span>') # 提取影片評(píng)分 findRating = re.compile(r'<span class="rating_num" property="v:average">(.*)</span>') # 提取評(píng)價(jià)人數(shù) findJudge = re.compile(r'<span>(\d*)人評(píng)價(jià)</span>') # 提取簡(jiǎn)介 inq = re.compile(r'<span class="inq">(.*)</span>') # 提取相關(guān)內(nèi)容 findBd = re.compile(r'<p class="">(.*)</p>(.*)<div', re.S)# 定義接收10頁(yè)的列表 dataList = []# 定義一個(gè)函數(shù),并解析這個(gè)網(wǎng)頁(yè) def analysisData(baseurl):# 獲取指定網(wǎng)頁(yè)for i in range(0, 10): # 獲取網(wǎng)頁(yè)選項(xiàng)的函數(shù),10次url = baseurl + str(i * 25)html = geturl(url)# 指定解析器解析html,得到BeautifulSoup對(duì)象soup = BeautifulSoup(html, "html5lib")# 定位我們的數(shù)據(jù)塊在哪for item in soup.find_all('div', class_="item"):# item 是 bs4.element.Tag 對(duì)象,這里將其轉(zhuǎn)換成字符串來(lái)處理item = str(item)# 定義一個(gè)列表 來(lái)存儲(chǔ)每一個(gè)電影解析的內(nèi)容data = []# findall返回的是一個(gè)列表,這里提取鏈接link = re.findall(findLink, item)[0]data.append(link) # 添加鏈接img = re.findall(findImgSrc, item)[0]data.append(img) # 添加圖片鏈接title = re.findall(findTitle, item)# 一般都有一個(gè)中文名 一個(gè)外文名if len(title) == 2:# ['肖申克的救贖', '\xa0/\xa0The Shawshank Redemption']titlename = title[0] + title[1].replace(u'\xa0', '')else:titlename = title[0] + ""data.append(titlename) # 添加標(biāo)題pf = re.findall(findRating, item)[0]data.append(pf)pjrs = re.findall(findJudge, item)[0]data.append(pjrs)inqInfo = re.findall(inq, item)if len(inqInfo) == 0:data.append(" ")else:data.append(inqInfo[0])bd = re.findall(findBd, item)[0]# [('\n 導(dǎo)演: 弗蘭克·德拉邦特 Frank Darabont\xa0\xa0\xa0主演: 蒂姆·羅賓斯 Tim Robbins /...<br/>\n 1994\xa0/\xa0美國(guó)\xa0/\xa0犯罪 劇情\n ', '\n\n \n ')]bd[0].replace(u'\xa0', '').replace('<br/>', '')bd = re.sub('<\\s*b\\s*r\\s*/\\s*>', "", bd[0])bd = re.sub('(\\s+)?', '', bd)data.append(bd)dataList.append(data)return dataListdef main():analysisData(baseurl)savepath = "C:\\Users\\Administrator\\Desktop\\python_3.8.5\\豆瓣250.xls"book = xlwt.Workbook(encoding="utf-8", style_compression=0) # 創(chuàng)建Workbook對(duì)象sheet = book.add_sheet("豆瓣電影Top250", cell_overwrite_ok=True) # 創(chuàng)建工作表col = ("電影詳情鏈接", "圖片鏈接", "電影中/外文名", "評(píng)分", "評(píng)論人數(shù)", "概況", "相關(guān)信息")print(len(dataList))for i in range(0, 7):sheet.write(0, i, col[i])for i in range(0, 250):print('正在保存第'+str((i+1))+'條')data = dataList[i]for j in range(len(data)):sheet.write(i + 1, j, data[j])book.save(savepath)if __name__ == "__main__":main()最終效果:
總結(jié)
以上是生活随笔為你收集整理的python爬虫详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 两个主机通信过程
- 下一篇: python学习---语法