爬蟲入門到放棄系列05:從程式模組設計到代理IP池
前言
上篇文章吧啦吧啦講了一些有的沒的,現在還是回到主題寫點技術相關的。本篇文章作為基礎爬蟲知識的最後一篇,將以爬蟲程式的模組設計來完結。
在我漫(liang)長(nian)的爬蟲開發生涯中,我通常將爬蟲程式分為四大模組。
如圖,除了代理模組是根據所需引入程式,請求、解析、儲存模組是必不可少的。
代理模組
代理模組主要是構建代理IP池。在第三篇中講過為什麼需要代理IP,因為很多網站是通過請求頻率來識別爬蟲,即記錄一個IP在一段時間內的請求次數,所以可以通過更換代理IP來提高爬取效率。
概念
什麼是代理IP池?
和執行緒池、連接池的理念一樣,預先將多個代理IP放入一個公共區域供多個爬蟲使用,每次用完之後再放回。
為什麼需要代理池?
正常情況下,我們在程式中是這樣添加代理IP的。
proxies = {
'https': '//183.220.xxx.xx:80'
}
response = requests.get(url, proxies=proxies)
這樣我們就只能使用一個IP,這時候可能有人就會說:
就算使用集合可以存放多個代理IP,但是如果IP失效需要刪除,或者添加新的IP時,還是一樣需要終止程式修改程式碼。我在初學編程的時候,老師就經常說這麼一句話:
直到現在,這句話也時常在耳邊縈繞。而代理模組就是提供了靈活增刪代理IP、驗證IP有效性的功能。
實現
目前,一般使用MySQL來存放代理IP。先看一下代理池的表設計。
CREATE TABLE `proxy` (
`ip` varchar(100) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
我的表結構設計比較簡單粗暴,只有一個欄位。在開發中可以根據自己的需要來進行細分。
再看一下表裡面的數據:
從圖中看著,代理IP是由支援的協議 + IP + port組成。
最後,代理池程式碼奉上:
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 = '//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()
代理池工作流程主要分為兩部分:
- 從數據中獲取IP。如果資料庫沒有可用IP,則表示不使用代理,返回空;如果有IP,則進入下一步
- 對IP進行有效性驗證。如果IP無效,刪除IP並重複第一步;如果IP有效,則返回IP
使用
代理池最終的目的還是提供有效代理IP。玩的比較花的可以將代理池與爬蟲程式分離,將代理池獨立成一個web介面,通過url來獲取代理IP,需要使用Flask或者Django來搭建一個web服務。
我一般就是直接放在爬蟲程式中。樣例程式碼如下:
pool = proxyPool('47.102.xxx.xxx', 'test', 'root', 'root', 3306)
proxy_ip = pool.get_ip()
url = '//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就是網站入口。
我們對網站入口,即動漫頻道進行請求後,解析返回的網頁內容。我們從頁面中可以發現,動漫頻道下有國漫、日漫、戰鬥等分類。
查看網頁源碼:
如上圖,我們可以從動漫首頁解析出來各個分類的url。
2.分類請求
在獲取到各個分類的url之後,繼續發起請求。這裡首先對國漫的url進行請求,返回的網頁內容如下:
如圖,都是國漫分類下的動漫列表。在瀏覽器中,我們點擊哪個動漫就能進入它的播放頁,所以在這個頁面上我們可以解析到這些國漫的播放頁鏈接。
我們查看此頁面的網頁源碼:
如圖,我們可以獲取到各個國漫播放頁的url。
3.定向到資訊頁
以第一個國漫斗羅大陸為例,我們獲取到它的播放頁url,進行請求並返回播放頁內容。
我們發現,點擊右上角的斗羅大陸就會進入詳情頁。所以我們需要解析右上角詳情頁的url進行請求,來獲取詳情頁的網頁內容。
4.獲取數據
對詳情頁的網頁內容進行解析,得出自己想要的數據,具體程式碼在第一篇文章的樣例中。
從上面的四個步驟來看,爬蟲對網站的爬取就是層層遞進,逐級訪問。我們要找准網站入口,明確想要獲取的數據內容,規劃好網站入口到獲取數據的路徑。
當然其中還是有很多可以優化的地方,例如從第二步可以略過第三步,直接請求第四步的詳情頁。我們比對一下播放頁和詳情頁的url。
# 斗羅大陸的播放頁和詳情頁
//v.qq.com/x/cover/m441e3rjq9kwpsc.html
//v.qq.com/detail/m/m441e3rjq9kwpsc.html
# 狐妖小紅娘的播放頁和詳情頁
//v.qq.com/x/cover/0sdnyl7h86atoyt.html
//v.qq.com/detail/0/0sdnyl7h86atoyt.html
從上面兩對url中我們不難看出其中的規律。所以我們在第二步解析出國漫播放頁的url之後,經過處理,就可以直接得到詳情頁的url。
備註:上面對騰訊影片的爬取分析僅做流程參考,實際開發可能涉及非同步請求等方面的知識。
存儲模組
爬取的數據只有存儲下來,爬蟲才變得更有意義。
通常爬取數據格式有文本、圖片等,這裡先看圖片如何下載並保存到本地目錄。
圖片下載
之前我用scrapy內置的ImagesPipeline下載GIF動圖的時候,折騰了老半天,下載下來的還不是動圖。於是回歸原始,終成功,一行程式碼慰平生。
程式碼如下:
urllib.request.urlretrieve(imageUrl, filename)
所以,不論以後你用原生爬蟲還是scrapy的時候,下載圖片就記住一行程式碼就行了!
找個圖片鏈接測試一下:
右鍵圖片,選擇拷貝影像地址。
將圖片地址放入程式中,程式碼如下:
import urllib.request
urllib.request.urlretrieve('//puui.qpic.cn/vcover_vt_pic/0/m441e3rjq9kwpsc1607693898908/0', './1.jpg')
我將圖片下來,保存到當前目錄並命名為1.jpg,運行程式。
文本數據
- 存放於文件中
with open("/path/file.txt", 'a', encoding='utf-8') as f:
f.write(data + '\n')
-
使用pymsql模組將數據存放到MySQL的數據表中
-
使用pandas或者xlwt模組將數據存放到excel中
結語
本篇文章主要寫了一下自己對爬蟲程式模組設計的理解,也是對爬蟲基礎知識的一個總結和收尾。期待下一次相遇。
寫的都是日常工作中的親身實踐,置身自己的角度從0寫到1,保證能夠真正讓大家看懂。
文章會在公眾號 [入門到放棄之路] 首發,期待你的關注。