1. 程式人生 > >爬蟲入門到放棄系列05:從程式模組設計到代理IP池

爬蟲入門到放棄系列05:從程式模組設計到代理IP池

## 前言 上篇文章吧啦吧啦講了一些有的沒的,現在還是回到主題寫點技術相關的。本篇文章作為基礎爬蟲知識的最後一篇,將以爬蟲程式的模組設計來完結。 在我漫(liang)長(nian)的爬蟲開發生涯中,我通常將爬蟲程式分為四大模組。 ![程式模組設計](https://img-blog.csdnimg.cn/20210303142759679.jpg) 如圖,除了代理模組是根據所需引入程式,請求、解析、儲存模組是必不可少的。 ## 代理模組 代理模組主要是構建代理IP池。在第三篇中講過為什麼需要代理IP,因為很多網站是通過請求頻率來識別爬蟲,即記錄一個IP在一段時間內的請求次數,所以可以通過更換代理IP來提高爬取效率。 ### 概念 *什麼是代理IP池?* 和執行緒池、連線池的理念一樣,預先將多個代理IP放入一個公共區域供多個爬蟲使用,每次用完之後再放回。 *為什麼需要代理池?* 正常情況下,我們在程式中是這樣新增代理IP的。 ```python proxies = { 'https': 'https://183.220.xxx.xx:80' } response = requests.get(url, proxies=proxies) ``` 這樣我們就只能使用一個IP,這時候可能有人就會說: ![](https://img-blog.csdnimg.cn/20210308122338130.jpg) 就算使用集合可以存放多個代理IP,但是如果IP失效需要刪除,或者新增新的IP時,還是一樣需要終止程式修改程式碼。我在初學程式設計的時候,老師就經常說這麼一句話: ![](https://img-blog.csdnimg.cn/20210308123507350.jpg) 直到現在,這句話也時常在耳邊縈繞。而代理模組就是提供了**靈活增刪代理IP、驗證IP有效性**的功能。 ### 實現 目前,一般使用MySQL來存放代理IP。先看一下代理池的表設計。 ```mysql CREATE TABLE `proxy` ( `ip` varchar(100) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ``` 我的表結構設計比較簡單粗暴,只有一個欄位。在開發中可以根據自己的需要來進行細分。 再看一下表裡面的資料: ![](https://img-blog.csdnimg.cn/20210308135336514.jpg) 從圖中看著,代理IP是由**支援的協議 + IP + port**組成。 最後,代理池程式碼奉上: ```python import requests import pymysql class proxyPool: # 初始化資料連線 def __init__(self, host, db, user, password, port): self.conn = pymysql.connect(host=host, database=db, user=user, password=password, port=port, charset='utf8') # 從資料庫獲取ip def get_ip(self): cursor = self.conn.cursor() cursor.execute('select ip from proxy order by rand() limit 1') ip = cursor.fetchone() # 如果代理ip表中有資料,則進行判斷,沒有則返回空 if ip: judge = self.judge_ip(ip[0]) # 如果ip可用直接將其放回,不可用再重新呼叫此方法從資料庫獲取一個IP if judge: return ip[0] else: self.get_ip() else: return '' # 判斷ip是否可用 def judge_ip(self, ip): http_url = 'https://www.baidu.com' try: proxy_dict = { "http": ip, } response = requests.get(http_url, proxies=proxy_dict) except Exception: self.delete_ip(ip) return False else: code = response.status_code if code in (200, 299): return True else: self.delete_ip(ip) return False # 從資料庫中刪除無效的ip def delete_ip(self, ip): delete_sql = f"delete from proxy where ip='{ip}'" cursor = self.conn.cursor() cursor.execute(delete_sql) self.conn.commit() ``` 代理池工作流程主要分為兩部分: 1. 從資料中獲取IP。如果資料庫沒有可用IP,則表示不使用代理,返回空;如果有IP,則進入下一步 2. 對IP進行有效性驗證。如果IP無效,刪除IP並重復第一步;如果IP有效,則返回IP ### 使用 代理池最終的目的還是**提供有效代理IP**。玩的比較花的可以將代理池與爬蟲程式分離,將代理池獨立成一個web介面,通過url來獲取代理IP,需要使用Flask或者Django來搭建一個web服務。 我一般就是直接放在爬蟲程式中。樣例程式碼如下: ```python pool = proxyPool('47.102.xxx.xxx', 'test', 'root', 'root', 3306) proxy_ip = pool.get_ip() url = 'https://v.qq.com/detail/m/m441e3rjq9kwpsc.html' proxies = { 'http': proxy_ip } if proxy_ip: response = requests.get(url, proxies=proxies) else: response = requests.get(url) print(response.text) ``` ### 代理IP的來源 這個之前也講過,代理IP可以付費購買或者從網上使用免費的。因為免費IP存活率低,所以代理池主要是面向於免費IP。 一般都是單獨開發一個爬蟲程式來爬取免費的IP,並放入到資料庫中,然後驗證可用性。 ## 請求/解析模組 在前幾篇寫的爬蟲樣例中,都是對單個url進行的爬取。而爬蟲程式往往都是以網站為單位進行的爬取。歸根結底,都是基於請求模組和解析模組來設計實現的。 如果想爬取整個網站,首先必須確定一個**網站入口**,即爬蟲程式第一個訪問的url。然後接著對返回的網頁進行解析,獲取資料或者獲取下一層url繼續請求。 這裡就拿騰訊視訊舉個栗子,我們來**爬取動漫的資訊*。 ### 1. 選擇網站入口 分析需求,選取網站入口。此時,需要明確的是:*動漫頻道url就是網站入口*。 ![動漫首頁](https://img-blog.csdnimg.cn/20210308220757238.jpg) 我們對網站入口,即動漫頻道進行請求後,解析返回的網頁內容。我們從頁面中可以發現,動漫頻道下有國漫、日漫、戰鬥等分類。 檢視網頁原始碼: ![分類URL](https://img-blog.csdnimg.cn/20210308221143516.jpg) 如上圖,我們可以從動漫首頁解析出來各個*分類的url*。 ### 2.分類請求 在獲取到各個分類的url之後,繼續發起請求。這裡首先對國漫的url進行請求,返回的網頁內容如下: ![國漫](https://img-blog.csdnimg.cn/20210308221919623.jpg) 如圖,都是國漫分類下的動漫列表。在瀏覽器中,我們點選哪個動漫就能進入它的播放頁,所以在這個頁面上我們可以解析到這些國漫的*播放頁連結*。 我們檢視此頁面的網頁原始碼: ![](https://img-blog.csdnimg.cn/20210308222626205.jpg) 如圖,我們可以獲取到各個*國漫播放頁的url*。 ### 3.定向到資訊頁 以第一個國漫斗羅大陸為例,我們獲取到它的播放頁url,進行請求並返回播放頁內容。 ![播放頁](https://img-blog.csdnimg.cn/20210308223526643.jpg) 我們發現,點選右上角的斗羅大陸就會進入詳情頁。所以我們需要解析右上角*詳情頁的url*進行請求,來獲取詳情頁的網頁內容。 ![詳情頁](https://img-blog.csdnimg.cn/20210308224223256.jpg) ### 4.獲取資料 對詳情頁的網頁內容進行解析,得出自己想要的資料,具體程式碼在第一篇文章的樣例中。 從上面的四個步驟來看,爬蟲對網站的爬取就是層層遞進,逐級訪問。**我們要找準網站入口,明確想要獲取的資料內容,規劃好網站入口到獲取資料的路徑**。 當然其中還是有很多可以優化的地方,例如從第二步可以略過第三步,直接請求第四步的詳情頁。我們比對一下播放頁和詳情頁的url。 ``` # 斗羅大陸的播放頁和詳情頁 https://v.qq.com/x/cover/m441e3rjq9kwpsc.html https://v.qq.com/detail/m/m441e3rjq9kwpsc.html # 狐妖小紅娘的播放頁和詳情頁 https://v.qq.com/x/cover/0sdnyl7h86atoyt.html https://v.qq.com/detail/0/0sdnyl7h86atoyt.html ``` 從上面兩對url中我們不難看出其中的規律。所以我們在第二步解析出國漫播放頁的url之後,經過處理,就可以直接得到詳情頁的url。 *備註*:上面對騰訊視訊的爬取分析僅做流程參考,實際開發可能涉及非同步請求等方面的知識。 ## 儲存模組 *爬取的資料只有儲存下來,爬蟲才變得更有意義。* 通常爬取資料格式有文字、圖片等,這裡先看圖片如何下載並儲存到本地目錄。 ### 圖片下載 之前我用scrapy內建的ImagesPipeline下載GIF動圖的時候,折騰了老半天,下載下來的還不是動圖。於是迴歸原始,終成功,一行程式碼慰平生。 程式碼如下: ```python urllib.request.urlretrieve(imageUrl, filename) ``` 所以,不論以後你用原生爬蟲還是scrapy的時候,下載圖片就記住一行程式碼就行了! 找個圖片連結測試一下: 右鍵圖片,選擇拷貝影象地址。 ![](https://img-blog.csdnimg.cn/20210308172042285.jpg) 將圖片地址放入程式中,程式碼如下: ```python import urllib.request urllib.request.urlretrieve('http://puui.qpic.cn/vcover_vt_pic/0/m441e3rjq9kwpsc1607693898908/0', './1.jpg') ``` 我將圖片下來,儲存到當前目錄並命名為1.jpg,執行程式。 ![](https://img-blog.csdnimg.cn/20210308172422311.jpg) ### 文字資料 1. 存放於檔案中 ```python with open("/path/file.txt", 'a', encoding='utf-8') as f: f.write(data + '\n') ``` 2. 使用**pymsql**模組將資料存放到MySQL的資料表中 ![](https://img-blog.csdnimg.cn/20210308231345907.jpg) 3. 使用**pandas**或者**xlwt**模組將資料存放到excel中 ## 結語 本篇文章主要寫了一下自己對爬蟲程式模組設計的理解,也是對爬蟲基礎知識的一個總結和收尾。期待下一次相遇。
--- 寫的都是日常工作中的親身實踐,置身自己的角度從0寫到1,保證能夠真正讓大家看懂。 文章會在公眾號 [**入門到放棄之路**] 首發,期待你的關注。 ![感謝每一份關注](https://img-blog.csdnimg.cn/202103041404307