2020年Python爬蟲全套課程(學完可做項目)

爬蟲:通過編寫程序,模擬瀏覽器上網(wǎng),然后讓其去互聯(lián)網(wǎng)上抓取數(shù)據(jù)的過程。
爬蟲的價值體現(xiàn)在實際應用和就業(yè)兩方面。
爬蟲在法律中是不被禁止的,但是具有違法的風險,好比一把鋒利的水果刀,可以用來削蘋果,同樣也可以進行暴力。所以爬蟲可以分為善意爬蟲和惡意爬蟲。
爬蟲所帶來的風險可以體現(xiàn)在如下兩方面:
- 爬蟲干擾了被訪問網(wǎng)站的正常運營
- 爬蟲抓去了受到法律保護的特定類型的數(shù)據(jù)或信息
如何在使用或編寫爬蟲的構成中避免進入局子的厄運呢?
- 時常優(yōu)化自己的程序,避免干擾被訪問網(wǎng)站的正常運行
- 在使用或傳播爬取到的數(shù)據(jù)時,需要審查抓取到的內容,若發(fā)現(xiàn)了涉及到用戶隱私或商業(yè)機密等敏感內容就需要及時停止
爬蟲在使用場景中的分類:
通用爬蟲:爬取系統(tǒng)重要組成的部分,抓取的是互聯(lián)網(wǎng)當中的一整張頁面數(shù)據(jù)。
聚焦爬蟲:建立在通用爬蟲的基礎之上,抓取的是頁面中特定的局部內容。
增量式爬蟲:檢測網(wǎng)站中數(shù)據(jù)更新的情況,只會抓取網(wǎng)站中最新更新出來的數(shù)據(jù)。
反爬機制:門戶網(wǎng)站,可以通過制定相應的策略或者技術手段,防止爬蟲程序進行網(wǎng)站數(shù)據(jù)的爬取。
反反爬策略:爬蟲程序可以通過制定相關的策略或者技術手段,破解門戶網(wǎng)站中具備的反爬機制,從而可以獲取門戶網(wǎng)站信息。
robots.txt協(xié)議:君子協(xié)議。規(guī)定了網(wǎng)站中哪些數(shù)據(jù)可以被爬取,哪些數(shù)據(jù)不可以被爬取??稍谌魏尉W(wǎng)站的后面加上robots.txt來查看,allow是可以爬取,disallow是不可爬取。
http協(xié)議:服務器和客戶端之間進行數(shù)據(jù)交互的一種形式。例如說人與人之間進行溝通用的是語言,亦或者在智取威虎山當中的黑話。
常用的請求頭信息:
- user-agent:請求載體的身份標識,會顯示出我所們用的瀏覽器和電腦的版本位數(shù)等等的信息(除了可以使用瀏覽器來爬取之外也可以使用代碼來偽裝一個身份標識)
- Connection:請求完畢后,是斷開連接還是保持連接。
常用響應頭信息:Content-Type:服務器響應回客戶端的數(shù)據(jù)類型
https協(xié)議:安全的超文本傳輸協(xié)議,相比于http而言更加地安全,是進行數(shù)據(jù)加密的。
加密方式:
- 對稱秘鑰加密:相當于在客戶端向服務器發(fā)送信息的時候,對信息進行加鎖,同時會產生一個密鑰,然后發(fā)送過程中將密鑰和加密的文件同時傳過服務器。這樣有一個缺點就是在進行密鑰和密文傳輸?shù)倪^程中有可能會被第三方攔截工具攔截從而暴露。
- 非對稱密鑰加密:可以解決對稱秘鑰加密的安全隱患。首先讓服務器端設定一個加密方式發(fā)送給客戶端,客戶端在拿到了該加密方式之后,針對該加密方式設定出一個密文發(fā)給服務器(公鑰),由于密鑰的制定者是服務器,因此服務器在拿到密文之后就可以根據(jù)自己所指定的密鑰進行解密(私鑰)。這樣做的缺點有兩個,一個是效率比較低,另一點是無法保證在服務器端在傳輸給客戶端密鑰的時候該密鑰不會被攔截并篡改,并在篡改之后將密鑰發(fā)給客戶端。
- 證書秘鑰加密(https):在針對非對稱密鑰加密的缺點上進行改良,同時在服務器端與客戶端在足夠信任的基礎上誕生了證書密鑰機構,理解為將公鑰先發(fā)給我證書認購機構,認購機構對其進行簽名后將證書發(fā)送給客戶端,客戶端只有看到簽名后才會認為這是從服務器端所發(fā)來的。如下圖:

requests模塊:python中原生的一款基于網(wǎng)絡請求的模塊。功能強大且使用起來簡單便捷效率極高。作用:模擬瀏覽器發(fā)發(fā)請求。
在涉及到網(wǎng)絡請求的模塊有兩個,一個是urllib,另一個是requests。urllib模塊比較古老,相對于requests更為復雜與麻煩,因此后續(xù)所學習的內容均以requests為主。
使用方式:(requests模塊的編碼流程)
- 指定url(以字符串的形式指定)
- 發(fā)起請求
- 獲取響應數(shù)據(jù)
- 持久化存儲(存儲的是響應數(shù)據(jù))
在開始前我們要配置環(huán)境,電腦里要有requests:win+r然后cmd,在里面輸入pip install requests即可。
實戰(zhàn)——爬取搜狗首頁
import requests if __name__ == "__main__": url = 'https://www.sougou.com/' response = requests.get(url=url) page_text = response.text print(page_text) with open('./sougou.html','w',encoding='utf-8') as fp: fp.write(page_text) print('爬取數(shù)據(jù)結束?。?!')
實戰(zhàn)——網(wǎng)頁采集器
#User-Agent(請求載體的身份標識) #UA偽裝:門戶網(wǎng)站的服務器會檢測對應請求的載體身份標識,如果檢測到請求和載體身份標識為某一款瀏覽器則會通過 #說明和這個請求是一個正常的請求,但如果檢測到的請求載體身份標識不是基于某一款瀏覽器的,則表示該請求為不正常請求(爬蟲) #服務器端就很有可能拒絕該次請求 #UA偽裝:讓爬蟲對應的請求載體身份標稱偽裝成某一款瀏覽器 if __name__ == "__main__": #UA偽裝:將對應的User-Agent封裝到一個字典中 headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.66 Safari/537.36 Edg/103.0.1264.44"} url = 'https://www.sogou.com/web' #后面之所以是亂碼,這是由于url編碼的特性決定的,但是在我們的requests模塊中也是可以用中文的。 #因為我們要設置的是動態(tài)的搜索框,而不是只搜索特定的內容,因此我們要處理url所攜帶的參數(shù),設置一個字典。 kw = input('enter a word') param = { 'query':kw } #對指定的url發(fā)起的請求對應的url是攜帶參數(shù)的,并且請求過程中處理了參數(shù) response = requests.get(url=url,params=param,headers=headers) #parmas是設置參數(shù),我們在之前把parma設置為一個字典,自字典的內容使我們所輸入的內容。 page_text = response.text fileName = kw+'html' with open(fileName,'w',encoding='utf-8') as fp: fp.write(page_text) print(fileName,'保存成功!?。?#39;)
實戰(zhàn)——破解百度翻譯
POST請求通常是攜帶參數(shù)的,我們要用requests模塊發(fā)送一個post請求,而在Response-Headers下的Content-Type所表示的是服務器端響應客戶端請求的類型,這個類型是一個json串。這個json串就在Response下。
if __name__ == "__main__": #指定url post_url = 'https://fanyi.baidu.com/sug/' #進行UA偽裝 headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.66 Safari/537.36 Edg/103.0.1264.44"} #post請求參數(shù)處理(同get請求一致) word = input('enter a word!') data = { 'kw':word } #請求發(fā)送 response = requests.post(url=post_url,data=data,headers=headers) #獲取相應數(shù)據(jù) :json()方法返回的是obj(如果確認相應數(shù)據(jù)是json類型的才可以使用json方法進行對象的返回) dic_obj = response.json() print(dic_obj) #持久化存儲 fileName = word+'.json' fp = open(fileName,'w',encoding='utf-8') json.dump(dic_obj,fp=fp,ensure_ascii=False) print('over!!!')
實戰(zhàn)——爬取豆瓣網(wǎng)站
if __name__ == "__main__": url = 'https://movie.douban.com/j/search_subjects?' para = { 'type': 'movie', 'tag': '經典', 'sort': 'recommend', 'page_limit': '20', 'page_start': '40' } headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.66 Safari/537.36 Edg/103.0.1264.44"} response = requests.get(url=url,params=para,headers=headers) list_data = response.json() fp = open('./douban.json','w',encoding='utf-8') json.dump(list_data,fp=fp,ensure_ascii=False) print('over!')
實戰(zhàn)——爬取肯德基地址
if __name__ == "__main__": url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword' para = { 'cname':'', 'pid':'', 'keyword': '北京', 'pageIndex': '1', 'pageSize': '10' } headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.66 Safari/537.36 Edg/103.0.1264.44"} response = requests.get(url=url,params=para,headers=headers) list_data = response.text fp = open('./kfc','w',encoding='utf-8') json.dump(list_data,fp=fp,ensure_ascii=True) print('over!')
實戰(zhàn)——爬取藥監(jiān)總局
這個網(wǎng)站的內容中,我們要爬取的內容是動態(tài)加載出來的數(shù)據(jù),大概率是阿賈克斯所制作的數(shù)據(jù),因此我們無法通過傳統(tǒng)的方式來獲取(無法直接對url發(fā)送請求即可或許),而接下來我就就來進行驗證是否真的是阿賈克斯請求導致的動態(tài)網(wǎng)址。
我們刷新該網(wǎng)址,然后可以在開發(fā)者人員模式下的網(wǎng)絡里找到XHR,里面有一行,點開之后查看Content-TYPE后能發(fā)現(xiàn)是json格式的,然后點擊response后可以看到這個json串,在百度里搜索json在線就能出現(xiàn)一個解析格式化的網(wǎng)址,將這個json串剪切到里面就可以查看到我們首頁當中所顯示的行政機關、日期和許可編號之類的。意味著當前頁面中的信息是我們的阿賈克斯請求所請求到的。
通過對這個json串進行深度剖析之后發(fā)現(xiàn)并沒有現(xiàn)成的url,但是卻有id,通過對每個網(wǎng)頁當中不同的鏈接進行觀察會發(fā)現(xiàn),每一個鏈接的唯一區(qū)別就只有id不一樣,所以我們可以根據(jù)每家企業(yè)所對應的id值能夠和網(wǎng)頁的域名拼接出完整的url地址。
而我們在首頁已經得到了一個教訓就是網(wǎng)頁當中的內容可能是動態(tài)加載的,因此我們不可能輕松地搞定,為了防止每一個鏈接下也是如此,所以我們要進行驗證,打開任意一個鏈接,然后在這個鏈接下在開發(fā)者人員工具模式下可以看到content-TYPE中顯示該網(wǎng)頁是html格式,然后再response中搜索網(wǎng)頁中的文字但是搜不到,因此我們可以判定這個網(wǎng)頁也是動態(tài)加載數(shù)據(jù)。然后我們在XHR中可以看到又出現(xiàn)了一個阿賈克斯的Name,然后將該Name的response下的json串繼續(xù)在線解析格式化輸出看一下,就可以發(fā)現(xiàn)這些內容里就擁有我們想要爬取的最終的內容。最后通過對比不同的鏈接可以發(fā)現(xiàn),每一個鏈接的post請求的url都是相同的,只有參數(shù)id不同。因此倘若我們可以批量奪取多家企業(yè)的id后,就可以將id和url形成一個萬能蒸的詳情頁對應想求數(shù)據(jù)的阿賈克斯請求的url。
那么進入代碼環(huán)節(jié),因為我們要爬取的內容在藥品官網(wǎng)的鏈接里的阿賈克斯請求POST里面,所以我們的url是在網(wǎng)絡的XHR里的那個url里。同時json串都是有封裝的,其封裝內容是在最下面的from data里,我們在代碼里創(chuàng)建一個data的列表將其進行封裝即可。在搞好了url、headesUA反爬、data封裝之后,將這個json串進行保存賦值給json_ids,下一步就可以將json_ids里面的id進行提取并存儲了。存儲的話我們要存儲在列表里,因此要提前建立一個列表這里命名為id_list,然后通過循環(huán)將id安置在列表當中,同時還要知道我們的id是在哪里,觀察json在線解析里可得知每一個id都在醫(yī)藥首頁的XHR阿賈克斯中json串所顯示的的response的list中的列表里,如下圖所示:通過查看可得知id在list的列表中是鍵,而id的內容在list列表中是值。

我們可以用尾部追加append的方式一個個地將list中的id一個個地打印到我們所創(chuàng)建的空列表當中,批量獲取id完成,接下來就要獲取企業(yè)詳情頁的數(shù)據(jù)。
之前我們分析過每一個鏈接都是一樣的,不同時只是id,因此我們可以通過循環(huán),for id in id_list,意思就是設id是我們剛才所創(chuàng)建的存儲id的列表的id_list的表達形式,然后封裝的時候就以data的列表命名,然后鍵是id,值就是id_list里面的內容,最后通過requests的post請求,設置好網(wǎng)址url,還有反爬UA,最后還有post所對應的data就是剛封裝好的id,以json的形式組合完成命名為derail_json,最后再將其通過追加append的方式保存到all_data_list當中,保存為json錢再將jsonimport一下。

我們下一步就要實行分頁操作,因此要做一下循環(huán),負責搞定網(wǎng)頁的爬取的分頁的在data里的page,range(1,6),根據(jù)顧頭不顧尾的方式是循環(huán)五次(1,2,3,4,5)的意思,因此將page的類型先確定為是字符串而并非是數(shù)字,然后在data字典里的page就變成了(1,2,3,4,5),獲取前五頁所有企業(yè)的id進而獲得所有企業(yè)的詳情數(shù)據(jù)。

總結:首先講解的是什么是requests模塊,requests模塊就是一個模擬瀏覽器發(fā)送請求的工具。使用方式大致分為:指定url(包括UA尾偽裝和請求參數(shù)的處理)、發(fā)送請求、獲取相應數(shù)據(jù)、持久化存儲四個步驟。接下來就要攝入到數(shù)據(jù)解析的講解過程當中,主要應用在聚焦爬蟲(一整張頁面的當中局部的內容進行爬?。?。
數(shù)據(jù)解析分類:正則、bs4、xpath(常用)
大部分網(wǎng)頁要存儲的位置,無非只有兩種,一種是存放在標簽中間,另一種是存放在標簽所對應的屬性中,我們最終想要拿到這些數(shù)據(jù),只需要找到標簽的位置或存儲標簽所對應的屬性值存儲下來。
正則解析原理:
- 進行指定標簽的定位
- 標簽或者標簽定位的屬性中存儲的數(shù)據(jù)值進行提取(解析)

正則練習

圖片數(shù)據(jù)的爬取,最后用text對圖片的url進行返回的結果是字符串形式的數(shù)據(jù),而如果使用content返回的是二進制數(shù)據(jù),json返回的是對象。想要獲取圖片我們用的是content,然后保存的話就是with open來保存。
import requests url='https://static.qiushibaike.com/images/download/slogan.png' img_data = requests.get(url=url).content with open('./picture.jpg','wb') as fp: fp.write(img_data)
實戰(zhàn)——爬取減肥藥品信息(不保存)
import requests, re,csv url = 'https://www.111.com.cn/search/search.action?keyWord=%25E5%2587%258F%25E8%2582%25A5%25E8%258D%25AF' headers = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36"} response = requests.get(url, headers=headers) pattern = re.compile('<li id="producteg_.*?".*?' 'alt="(?P<name>.*?)".*?' '<span>(?P<price>.*?)</span>.*?' ' href="//(?P<href>.*?)".*?' '評論 <em>(?P<comment>.*?)</em>條',re.S) Drugsli_list = pattern.finditer(response.text) for drug in Drugsli_list: dic = drug.groupdict() print(dic) dic['price'] = drug.group('price').strip()+"元" dic['href'] = 'https://' + drug.group('href') dic['comment'] = drug.group('comment')+"條評論" print("結束!")
bs4是python語言中獨有的,不像正則可以在python,也可以在其它的語言中運用,在運用的時候需要的環(huán)境除了bs4在之還需要lxml。

bs4數(shù)據(jù)解析原理:
- 實例化一個BeautifulSoup對象,并且將頁面源代碼數(shù)據(jù)加載到該對象中。
- 通過調用BeautifulSoup對象中相關的屬性或者方法進行標簽定位和數(shù)據(jù)提取
如何實例化BeautifulSoup對象:
from bs4 import BeautifulSoup
one:將本地的html文檔中的數(shù)據(jù)加載到該對象中
fp = open('./test.html','r',encoding='utf-8')
soup = BeautifulSoup(fp,'lxml')
two:將互聯(lián)網(wǎng)上獲取的頁面源碼加載到該對象中
page_text = response.text
soup = BeautifulSoup(page_text,'lxml')
提供的用于數(shù)據(jù)解析的方法和屬性:
- soup.find('tagName') = soup.tagName:返回的是文檔中第一次出現(xiàn)的tagName標簽(這里的tagName是一個參數(shù),意為標簽的名字,例如soup.a意思是返回第一個出現(xiàn)的a標簽的內容)
- 根據(jù)屬性去針對標簽進行選擇 。soup.find('div',class_='song')。意思是找到屬性為song的div標簽。class要加下劃線是因為如果不加下劃線的話就不是一個參數(shù)名稱,而是一個關鍵字了。
- find_all:找到符合條件的所有標簽。soup.find_all('a'),返回的就是所有的a標簽,相比于find,不再只輸出第一個了。
- select:參數(shù)當中可以放置一些選擇器,返回的是一個列表。select('某種選擇器(id,class,標簽...)')。層級選擇器:也可以通過索引的形式進行返回,例如select('.tang > ul > li > a')[0],同時這里面的>是表示也層級,空格時表示多個層級。
我們可以看到最后輸出的內容弄個大多都套在標簽當中。
獲取標簽之間的文本數(shù)據(jù)
- soup.a.text/string/get_text()
- text/get_text():可以獲取某一個標簽中所有的文本內容
- string:只可以獲取該標簽下面直系的文本內容
獲取標簽中屬性的值
- 直接寫屬性名稱。eg:soup.a['href']
bs4實戰(zhàn)訓練——爬取三國演義所有章節(jié)的標題與內容
設計思路:先使用通用爬蟲,將三國演義的頁面中的所有文本標題進行獲取,再將每一個標題所對應的詳情頁進行獲取。每一個標題可以點擊意味著這里每一個標題都對應著鏈接的地址,對這個地址發(fā)起求就可以獲取到詳情頁。
首先對首頁的頁面數(shù)據(jù)進行爬?。ㄖ付╱rl),再使用requests發(fā)送請求并設置UA反爬。
import requests from bs4 import BeautifulSoup import time url = "https://www.shicimingju.com/book/sanguoyanyi.html" headers = {"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36"} response = requests.get(url, headers = headers)
接下來在首頁中解析出章節(jié)的標題和詳情頁的url,首先實例化BeautifulSoup對象,需要將頁面源代碼加載到該對象當中。
soup=BeautifulSoup(page_text.content,'lxml')
在獲取標題的時候我們要保證獲取的是復數(shù),而find獲取的是單數(shù),因此可以選擇的是select和find_all,這里用的是select層級選擇器。
li_list = soup.select('.book-mulu > ul >li')
通過開發(fā)者人員工具可以看到網(wǎng)址中的標題名在book-mulu屬性下的ul下的li,這樣就能將a標簽所處的li標簽全部選取,然后通過循環(huán),以find的方式將a標簽總的標題和鏈接取出來,此時要注意的是取出來的href值不是完整的,需要進行字符串的拼接。
fp = open('./sanguoyanyi.txt','w',encoding='utf-8') for li in li_list: title = li.a.string detail_url = 'http://www.shicimingju.com'+li.a['href'] detail_page_text = requests.get(url=detail_url, headers=headers).text
而下一步我們要做的就是對詳情頁發(fā)起請求,解析出章節(jié)內容。并且解析出詳情頁相關的章節(jié)內容。我們之前實例化的soup只能解析出首頁的內容,不能實例化詳情頁的內容,因此我們要再實例化一個。每一段的詳情頁內容都在div屬性為chapter_content里,直接用find即可,此刻我們就解析到了章節(jié)的內容。
detail_soup = BeautifulSoup(detail_page_text,'lxml') div_tag = detail_soup.find('div',class_='chapter_content').text content = div_tag fp.write(title+':'+content+'\n') print(title,'爬取成功?。?!')
最后就可以做持久化存儲了,注意with open不能像寫到循環(huán)里,這個文本文件只需要被打開一次次,因此要先創(chuàng)建一個文本文檔命名任意,編碼格式為utf-8的前提下,寫入標題和內容,標題后面對應的是冒號,冒號后對應的是內容,最后面再加上一個\n的分隔符。結束!

Xpath解析:最常用且最便捷高效的一種解析方式。
Xpath解析原理:
- 實例化一個etree的對象,且需要將被解析的頁面源碼數(shù)據(jù)加載到該對象中。
- 調用etree對象中的xpath方法結合著xpath表達式實現(xiàn)標簽的定位和內容的捕獲。
lxml是一種解析器,在xpath爬取前安裝。
如何實例化一個etree對象:
- 將本地的html文檔中的源碼數(shù)據(jù)加載到etree對象中。etree.parse(filePath)
- 可以將從互聯(lián)網(wǎng)上獲取的掩碼數(shù)據(jù)加載到對象當中。etree.HTML('page_text')
xpath是根據(jù)層級關系去做的標簽定位。從根節(jié)點(根目錄:最外層標簽的節(jié)點)逐步遞歸找到我們所要查找的內容。最終所返回的將是一個列表,列表當中存儲的是我們所查找的節(jié)點,返回的并不是文檔內容,而是一個Element對象當中進行的一個存儲。而如果當我們所查找的標簽為多個時,所返回的內容也是多個Element類型的對象,想要獲取文本就在后面加上/text()
xpath表達式:
- /:表示的是從根節(jié)點開始定位,表示的是一個層級。
- //:表示的是多個層級,表示從任意位置開始定位。
- 屬性定位://div[@class=’song‘] 格式為:tag[@attrName='value']
- 索引定位://div[@class='song']/p[3] 。索引是從1開始的。
- 取文本
- /text() 獲取的是標簽中直系的文本內容
- //text()獲取的是非直系的文本內容(所有文本內容)
- 取屬性:/@attrName:直接/@加上屬性名稱(eg:img/@src)
xpath實戰(zhàn)——58二手房
在獲取完網(wǎng)頁源碼之后進行數(shù)據(jù)解析,隨后為了獲取標題,通過觀察網(wǎng)絡源碼可以發(fā)現(xiàn)標題存在于li標簽之下,因此我們先定位到li標簽后進行循環(huán),在此循環(huán)下再進行xpath表達式,在這個li循環(huán)中的xpath表達式下我們要注意,在最前面要用./來代表li所指向的局部的內容。
if __name__ == "__main__": # 爬取到頁面源碼數(shù)據(jù) url = 'https://bj.58.com/ershoufang/' headers = {"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36"} page_text = requests.get(url=url,headers=headers).text # 解析數(shù)據(jù) tree = etree.HTML(page_text) # 存儲的就是li標簽對象 li_list=tree.xpath('//ul[@class="house-list-wrap"]/li') fp = open('58.txt','w',encoding='utf-8') for li in li_list: title = li.xpath('./div[2]/h2/a/text()')[0] print(title) fp.write(title+'\n')
實戰(zhàn)——使用xpath爬取城市名稱
if __name__ == "__main__": # 爬取到頁面源碼數(shù)據(jù) url = 'https://www.aqistudy.cn/historydata/' headers = {"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36"} page_text = requests.get(url=url,headers=headers).text # 解析數(shù)據(jù) tree = etree.HTML(page_text) host_li_list = tree.xpath('//div[@class="bottom"]/ul/li') all_city_names=[] for li in host_li_list: hot_city_name = li.xpath('./a/text()')[0] all_city_names.append(hot_city_name) city_names_list = tree.xpath('//div[@class="bottom"]/ul/div[2]/li') for li in city_names_list: city_names = li.xpath('./a/text()')[0] all_city_names.append(city_names) print(all_city_names,len(all_city_names))
同時xpath也可以先定位到我們要獲得的文本的位置,然后再通過循環(huán)獲取其位置下的文本,而倘若我們要獲取的內容為多個,可以使用|運算符來解決。
if __name__ == "__main__": # 爬取到頁面源碼數(shù)據(jù) url = 'https://www.aqistudy.cn/historydata/' headers = {"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36"} page_text = requests.get(url=url,headers=headers).text # 解析數(shù)據(jù) tree = etree.HTML(page_text) # '//div[@class="bottom"]/ul/li' # '//div[@class="bottom"]/ul/div[2]/li' a_list = tree.xpath('//div[@class="bottom"]/ul/li/a | //div[@class="bottom"]/ul/div[2]/li/a') all_city_names = [] for a in a_list: city_name = a.xpath('./text()')[0] all_city_names.append(city_name) print(all_city_names,len(all_city_names))
實戰(zhàn)——使用xpath爬取4k圖片
我們要爬取的圖片在a標簽下的src的屬性值。對圖片進行請求后再對其保存,同時也要對alt的屬性值進行提取,作為src的名字。
if __name__ == "__main__": # 爬取到頁面源碼數(shù)據(jù) url = 'http://pic.netbian.com/4kfengjing/' headers = {"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36"} response = requests.get(url=url,headers=headers) #手動設定響應數(shù)據(jù)的編碼格式 # response.encoding = 'utf-8' page_text = response.text # 解析數(shù)據(jù):src的屬性值 alt屬性 tree = etree.HTML(page_text) li_list = tree.xpath('//div[@class="slist"]/ul/li') # 創(chuàng)建一個文件夾 if not os.path.exists('./picLibs'): os.mkdir('./picLibs') for li in li_list: img_src = 'http://pic.netbian.com'+li.xpath('./a/img/@src')[0] img_name = li.xpath('./a/img/@alt')[0]+'.jpg' # 通用處理中文亂碼的解決方案 img_name = img_name.encode('iso-8859-1').decode('gbk') # print(img_name,img_src) # 請求圖片進行持久化存儲 img_data = requests.get(url=url,headers=headers).content img_path = 'picLibs/'+img_name with open(img_path,'wb') as fp: fp.write(img_data) print(img_name,'下載成功?。?!')
在發(fā)生亂碼之后我們首先可以對其進行指定編碼格式的操作,若無效課對其使用通用的處理中文亂碼的解決方案。
驗證碼識別
反爬機制:驗證碼。
有一些網(wǎng)站在使用前需要登陸,登陸就離不開賬號密碼和驗證碼,此時的驗證碼就是一種門戶網(wǎng)站的反爬機制。登陸操作我們可以使用requests模塊進行模擬,但驗證碼需要我們取識別這個圖片,用于模擬登陸操作。
識別驗證碼的操作
- 人工肉眼識別(不推薦)
- 第三方自動識別(推薦)
- 云達碼
- 超級鷹
- ......
代理
- 某某網(wǎng)站會采取一些反爬措施,在單位時間內某一個ip請求過于頻繁以至于超過了正常訪問頻率,就會進行ip的封禁。
- 破解封IP這種反爬機制
什么是代理?
- 代理服務器:網(wǎng)絡信息中的中轉站(將請求發(fā)送給web服務器,然后該服務器轉發(fā)給我們要訪問的網(wǎng)站)
代理的作用?
- 突破自身IP訪問的限制
- 隱藏真實的IP受到攻擊,隱藏自身真的IP
代理相關的網(wǎng)站:
- 快代理
- 西祠代理
- www.goubanjia.com
代理IP的類型
- http:應用到http協(xié)議對應的url中
- https:應用到https協(xié)議對應的url中
# 代理報錯的,試著將proxies中的"https"改成"https://",或者是將"http"改成"http://" url = 'https://www.baidu.com/s?wd=ip' headers = { "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36"} response = requests.get(url=url, headers=headers,proxies={"http":"47.101.44.122"}).text with open('ip.html','w',encoding='utf-8') as fp: fp.write(response)
代理IP的匿名度:
- 透明:服務器知道該次請求使用了代理,也知道請求對應的真是ip。
- 匿名:知道使用了代理,不知道真實ip。
- 高匿:不知道使用了代理,更不知道其真實的ip地址。
高性能異步爬蟲
目的:在爬蟲中使用異步實現(xiàn)爬取高性能的數(shù)據(jù)爬取操作。
urls = ['https://www.baidu.com/s?wd=ip','https://www.bilibili.com/';] headers = { "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36"} def get_content(url): print('正在爬取:',url) response = requests.get(url=url, headers=headers) if response.status_code == 200: return response.content def parse_content(content): print('響應數(shù)據(jù)的長度為:',len(content)) for url in urls: content = get_content(url) parse_content(content)
通過以上代碼我們可以看出get方法是一個阻塞的方法,此時兩個url就已經很慢了,那如果是20或者200個那就會更慢,這是因為我們的程序是單線程,只有拿到相應數(shù)據(jù)之后才會執(zhí)行其它的方法,響應數(shù)據(jù)效率很慢。這時就需要使用異步操作去解決。
異步爬蟲的兩種方式:
- 多線程,多進程:
- 好處:可以為相關阻塞的操作單獨開啟線程或者進程,阻塞操作就可以異步執(zhí)行。
- 弊端:無法無限制的開啟多線程或者多進程(耗費cpu)。
- 線程池、進程池:
- 好處:我們可以降低系統(tǒng)對進程或者線程創(chuàng)建和銷毀的一個頻率,從而很好的降低系統(tǒng)的開銷。
- 弊端:池中線程或進程的數(shù)量是有上限的(有上限就會出現(xiàn)當阻塞操作超過線程數(shù)量時會變慢)。
之前我們在采集化妝品生產許可信息管理系統(tǒng)的時候會發(fā)現(xiàn)要爬取的內容為動態(tài)加載內容,我們就無法直接對url發(fā)請求去拿到的,驗證其是否為動態(tài)加載數(shù)據(jù)的方式就是打開抓包工具,定位到network,刷新之后,打開對應大當前網(wǎng)頁headers里url的response查看源碼,搜索當前頁面所顯示的任意一家企業(yè)名稱(搜索的快捷鍵為ctrl+f),發(fā)現(xiàn)并沒有對應的企業(yè)名稱,所以可以判斷其為動態(tài)加載數(shù)據(jù)。
那么如果想知道當前頁面下的動態(tài)加載數(shù)據(jù)是哪里來的,只需要在network下按ctrl+f進行全局搜索,與上面不同的是這次搜索的是全部的內容,就會找到我們要查詢的動態(tài)加載數(shù)據(jù)的位置。把參數(shù)結合著url發(fā)布一個post請求就可以拿到企業(yè)名稱(動態(tài)加載數(shù)據(jù)),方法很麻煩,因此就需要用到selenium來解決,同時也可以通過selenium實現(xiàn)模擬登陸。
What is selenuim?
- 基于瀏覽器自動化的一個模塊
- 瀏覽器自動化:我們可以通過編寫相關的python代碼,讓這些python代碼表示一些行為動作,讓這些行為動作出發(fā)到瀏覽器當中,讓瀏覽器實現(xiàn)自動化的操作。
# 能不能讓我的程序連接到瀏覽器 . 讓瀏覽器來完成各種復雜的操作, 我們只接受最終的結果 # selenium: 自動化測試工具 # 可以: 打開瀏覽器. 然后像人一樣去操作瀏覽器 # 程序員可以從selenium中直接提取網(wǎng)頁上的各種信息 # 環(huán)境搭建: # pip install selenium -i 清華源 # 下載瀏覽器驅動:https://npm.taobao.org/mirrors/chromedriver # 把解壓縮的瀏覽器驅動 chromedriver 放在python解釋器所在的文件夾
實戰(zhàn)——爬取藥監(jiān)總局的動態(tài)加載數(shù)據(jù)
from selenium import webdriver from lxml import etree from time import sleep # 實例化一個瀏覽器對象(前提要傳出瀏覽器的驅動程序) bro = webdriver.Chrome(executable_path='D:\python\Scripts\chromedriver.exe') bro.get('http://scxk.nmpa.gov.cn:81/xk/') # 獲取瀏覽器當前頁面的頁面遠嗎數(shù)據(jù) page_text = bro.page_source # 解析企業(yè)名稱 tree = etree.HTML(page_text) li_list = tree.xpath('//ul[@id="gzlist"]/li') for li in li_list: name = li.xpath('./dl/@title')[0] print(name) sleep(5) bro.quit()
實戰(zhàn)——登錄qq空間
from selenium import webdriver import time web = webdriver.Chrome() web.get('https://qzone.qq.com/') web.switch_to.frame('login_frame') #切換到新窗口 web.find_element_by_id("switcher_plogin").click() userName_tag = web.find_element_by_id('u').send_keys('342954562') #輸入賬號 password_tag = web.find_element_by_id('p').send_keys('aedc22') #輸入密碼 time.sleep(1) web.find_element_by_id('login_button').click() time.sleep(10) #暫停10s web.close() #關閉當前窗口
協(xié)程
- 協(xié)程不是計算機提供的,是程序員任偉創(chuàng)造的。
- 協(xié)程(Coroutine),也可以被稱為微線程,是一種用戶態(tài)的上下文切換技術。簡而言之,其實就是通過一個線程實現(xiàn)代碼塊相互切換執(zhí)行。
實現(xiàn)協(xié)程有這么幾種方式:
- greenlet(早期模塊)
- ?02 協(xié)程~1 P84 - 08:25?
- yield關鍵字
- ?02 協(xié)程~1 P84 - 11:19?
- asyncio裝飾器(python3.4引入的)
- ?02 協(xié)程~1 P84 - 15:19?
- async&await關鍵字(python3.5引入的)【推薦的】
- ?02 協(xié)程~1 P84 - 26:12?
實戰(zhàn)——爬取梨視頻
#需求:爬取梨視頻的視頻鏈接,并下載 #url="https://www.pearvideo.com/video_1763204" url="https://www.pearvideo.com/video_1763507" video_id = url.split("_")[1] print(video_id) #1、爬取源代碼,看源代碼中是否有視頻鏈接 # 結果:搜索".mp4"關鍵字,沒有搜到,視頻鏈接不在源代碼中; #2、通過查看Network面板-> XHR -> 發(fā)送視頻的鏈接地址 video_url = "https://www.pearvideo.com/videoStatus.jsp?contId="+video_id #3. 向url發(fā)送get請求,得到視頻的信息 import requests response = requests.get(video_url, #游戲機的詳細信息,詳情頁 headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36", #設置useragent "Referer": url #防盜鏈 京東首頁 }) print(response.json()) #返回視頻的信息, #4、獲取了視頻的鏈接 srcUrl = response.json()['videoInfo']['videos']['srcUrl'] print(srcUrl) #404錯誤 # 將爬取的鏈接中的systemTime,替換成cont-video_id systemTime = response.json()['systemTime'] print("系統(tǒng)時間:",systemTime) newUrl = srcUrl.replace(systemTime, "cont-"+video_id) print("新的鏈接:", newUrl) # 爬取的鏈接: https://video.pearvideo.com/mp4/adshort/20220524/1653618968522-15884803_adpkg-ad_hd.mp4 # 可以播放的鏈接:https://video.pearvideo.com/mp4/adshort/20220524/cont-1763204-15884803_adpkg-ad_hd.mp4 #5、進行視頻的下載 video_content = requests.get(newUrl).content #視頻的內容 with open("vido.mp4",mode="wb") as f: f.write(video_content) print("結束!")