scrapy突破反爬的幾種方式(二)
上回說到設定隨機 User-Agent ,這次來一個隨機代理 ip 的設定。
代理ip
在爬蟲中,為了避免網站將我們的 ip 封掉,我們就要使用代理 ip 。雖然說代理 ip 沒有原裝的好,但是有些時候還是要使用代理ip 來獲取資料的。
原理
隨機代理 ip 簡單來說就是爬取網上的免費代理ip ,然後存入資料庫,在資料庫中隨機拿到一個代理ip來用。具體結合到 scrapy 中,我們就要在 Middleware.py 檔案中修改,原理跟上文代理的設定一樣,不懂的可以去上篇文章看一下。
實戰演練
在上文的 scrapydownloadertest 專案基礎上操作。 在 spiders 的同級目錄下,新建一個 Python Package 。還要新建python檔案,具體名稱參考圖片。目錄結構如下:
目錄結構
從西刺網上獲取免費的代理ip 儲存到資料庫中。
import requests from scrapy.selector import Selector import pymysql # 連線資料庫 conn = pymysql.connect(host='',user='',password='',db='proxy',charset='utf8') cursor = conn.cursor() # 爬取西刺網站上的代理ip def crawler_ips(): headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36' } for i in range(100): url = 'http://www.xicidaili.com/nn/'.format(i) r = requests.get(url,headers=headers) selector = Selector(text=r.text) all_ips = selector.css('#ip_list tr') ip_list = [] for tr in all_ips[1:]: # 這裡由於第一個是標題,捨棄 speed_str = tr.css('.bar::attr(title)').extract_first()[0] if speed_str: speed = float(speed_str.split('秒')[0]) all_texts = tr.css('td::text').extract() ip = all_texts[0] port = all_texts[1] proxy_type = all_texts[5] ip_list.append((ip,port,proxy_type,speed)) for ip_info in ip_list: # 插入資料 cursor.execute( "insert proxy_ip(ip,port,speed,proxy_type) VALUES('{0}','{1}',{2},'HTTP')".format( ip_info[0],ip_info[1],ip_info[3] ) ) conn.commit()
資料庫我用的是MySQL,在 navivat for mysql 中連線 MySQL ,新建一個數據庫,名稱隨意,這裡就叫做 proxy 吧,在程式碼中把連線資料庫的程式碼補充完整。 新建一個數據表 proxy_ip 並寫下如下欄位:
proxy_ip
這裡我已經新建好了,並且執行的程式碼。
新建之後,我們執行上述程式碼,這時候會看到資料庫中已經有了資料。接下來就是從資料庫中隨機獲取一個代理 ip ,供我們程式使用。
class GetIP(object): def get_random_ip(self): # 從資料庫中隨機獲取一個可用ip random_sql = 'SELECT ip,port FROM proxy_ip ORDER BY RAND() LIMIT 1' result = cursor.execute(random_sql) for ip_info in cursor.fetchall(): ip = ip_info[0] port = ip_info[1] judge_re = self.judge_ip(ip,port) if judge_re: return 'http://{0}:{1}'.format(ip,port) else: return self.get_random_ip() def judge_ip(self,ip,port): # 判斷ip 是否可用 http_url = 'http://www.baidu.com' proxy_url = 'http://{0}:{1}'.format(ip,port) try: proxy_dict = { 'http':proxy_url } r = requests.get(http_url,proxies=proxy_dict) print('effective ip') return True except Exception as e: print('invalid ip and port 無效') self.delete_ip(ip) return False else: code = r.status_code if code >= 200 and code < 300: print('effective ip') return True else: print('invalid ip and port 無效') self_delete_ip(ip) return False def delete_ip(self,ip): # 刪除無效的ip delete_sql = "delete from proxy_ip where ip = '{0}'".format(ip) cursor.execute(delete_sql) conn.commit() if __name__ == '__main__': get_ip = GetIP() print(get_ip.get_random_ip())
這裡從資料庫中隨機獲取一個代理ip,並測試代理ip是否可用,不可用的刪掉。這裡有個小技巧,如果你爬取的是其他網站,就把百度的連結換成你要爬取的網站,這樣會提高代理ip的可用性。 執行此程式碼會隨機獲取一個可用的ip代理。
接下來在 Middleware.py 中把代理ip 加上:
class RandomProxyMiddleware(object):
# 動態設定ip代理
def process_request(self, request, spider):
get_ip = GetIP()
request.meta['proxy'] = get_ip.get_random_ip()
在 settings.py 檔案中配置一下:
DOWNLOADER_MIDDLEWARES = {
'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware': None,
'scrapydownloadertest.middlewares.RandomUserAgentMiddleware': 543,
'scrapydownloadertest.middlewares.RandomProxyMiddleware': 544,
}
執行spider檔案,就可以在輸出的日誌上面看到代理ip在使用了。
2018-09-16 16:57:46 [urllib3.connectionpool] DEBUG: http://123.249.88.153:9000 "GET http://www.baidu.com/ HTTP/1.1" 200 None
effective ip
總結
-
使用這樣的方法可以用到代理 ip 從而有效的隱藏了自己的真實 ip 。免費從網上獲取的代理通常來說沒有付費得到的 ip 穩定性好。如果有條件,還是建議自費獲取一些代理 ip。
-
自己的代理還是最好用的,我們在爬取的時候可以放慢速度,增加延遲等方式以避免自己的 ip 被封。同時不要讓網站的壓力太大。這樣才能共存。