爬蟲入門到放棄系列05:從程式模組設計到代理IP池
阿新 • • 發佈:2021-03-09
## 前言
上篇文章吧啦吧啦講了一些有的沒的,現在還是回到主題寫點技術相關的。本篇文章作為基礎爬蟲知識的最後一篇,將以爬蟲程式的模組設計來完結。
在我漫(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
--- 寫的都是日常工作中的親身實踐,置身自己的角度從0寫到1,保證能夠真正讓大家看懂。 文章會在公眾號 [**入門到放棄之路**] 首發,期待你的關注。 ![感謝每一份關注](https://img-blog.csdnimg.cn/202103041404307