常見爬蟲技術

1.降低請求頻率

對於Scrapy框架來說,在配置文件settings.py中設置DOWNLOAD_DELAY即可。以下代碼設置下載延遲時間爲3秒,即兩次請求間隔3秒。

DOWNLOAD_DELAY = 3

爲了防止請求過於規律,可以使用RANDOMIZE_DOWNLOAD_DELAY設置一個介於0.5 *DOWNLOAD_DELAY和1.5 *DOWNLOAD_DELAY之間的隨機延遲時間。

RANDOMIZE_DOWNLOAD_DELAY = True

2. 修改請求頭

網站可能會對HTTP請求頭的每個屬性做檢查。HTTP定義了十多個請求頭類型,不過大多數都不常用,只有幾個字段被大多數瀏覽器用來初始化所有的網絡請求,如下表所示。其中最重要的參數是User-Agent,我們使用它來僞裝成瀏覽器。如果你正在處理一個警覺性非常高的網站,就要注意那些經常用卻很少檢查的請求頭,如Accept-Language屬性。
在這裏插入圖片描述

3. 禁用Cookie

有些網站會通過Cookie來發現爬蟲的軌跡。因此,如果不是特殊需要,可以禁用Cookie,這樣網站就無法通過Cookie來偵測到爬蟲了。Scrapy中禁止Cookie功能也非常簡單,在配置文件settings.py中做如下設置:

COOKIES_ENABLED = False

4. 僞裝成隨機瀏覽器

我們都是通過User-Agent將爬蟲僞裝成固定瀏覽器,但是對於警覺性高的網站,會偵測到這一反常現象,即持續訪問網站的是同一種瀏覽器。因此,每次請求時,可以隨機僞裝成不同類型的瀏覽器。Scrapy中的中間件UserAgentMiddleware就是專門用於設置User-Agent的,在爬蟲運行時,會自動將User-Agent添加到HTTP請求中,並且可以設置多個瀏覽器,請求時可以隨機添加不同的瀏覽器。要實現此功能,只需要完成以下3步:

1)設定瀏覽器列表

  MY_USER_AGENT = [
 "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
 "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)",
 "Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.35; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
 "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)",
 "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)",
 "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)",
 "Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)",
 "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)",
 "Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6",
 "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1",
 "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0",
 "Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5",
 "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Fedora/1.9.0.8-1.fc10 Kazehakase/0.5.6",
 "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11",
 "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.20 (KHTML, like Gecko) Chrome/19.0.1036.7 Safari/535.20",
 "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; fr) Presto/2.9.168 Version/11.52",
 "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.11 TaoBrowser/2.0 Safari/536.11",
 "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER",
 "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; LBBROWSER)",
 "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E; LBBROWSER)",
 "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.84 Safari/535.11 LBBROWSER",
 "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)",
 "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; QQBrowser/7.0.3698.400)",
 "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)",
 "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SV1; QQDownload 732; .NET4.0C; .NET4.0E; 360SE)",
 "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)",
 "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)",
 "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1",
 "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1",
 "Mozilla/5.0 (iPad; U; CPU OS 4_2_1 like Mac OS X; zh-cn) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5",
 "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0b13pre) Gecko/20110307 Firefox/4.0b13pre",
 "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:16.0) Gecko/20100101 Firefox/16.0",
 "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11",
 "Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10",
 "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36",
 ]

將settings.py中設置固定瀏覽器代碼刪除或註釋掉,添加上面內容。

2)在中間件UserAgentMiddleware中從瀏覽器列表中隨機獲取一個瀏覽器

在middlewares.py中定義基於UserAgentMiddleware的類,實現對User-Agent的隨機設置,代碼如下:

#導入UserAgentMiddleware組件模塊
from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware
import random#導入隨機模塊
from xx.settings import MY_USER_AGENT#導入瀏覽器列表
#定義類xxUserAgentMiddleware,用於設置隨機設置user-agent
#繼承於UserAgentMiddleware
class xxUserAgentMiddleware(UserAgentMiddleware):
    #處理Request請求函數
    def process_request(self, request, spider):
        #使用random模塊的choice函數從列表MY_USER_AGENT中隨機獲取一個瀏覽器類型
        agent = random.choice(list(MY_USER_AGENT))
        print("user-agent:",agent)
        #將User-Agent附加到Reqeust對象的headers中
        request.headers.setdefault('User-Agent', agent)

3)啓用中間件UserAgentMiddleware。

在settings.py中,啓用中間件xxtUserAgentMiddleware。

更簡單的辦法: 使用fake_useragent庫實現瀏覽器的隨機獲取

# 導入UserAgentMiddleware組件模塊
from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware
# 導入fake-useragent庫
from fake_useragent import UserAgent

# 定義類xxUserAgentMiddleware,用於設置隨機設置user-agent
class xxUserAgentMiddleware(UserAgentMiddleware):
    #處理Request請求函數
    def process_request(self, request, spider):
	    # 生成UserAgent對象
        ua = UserAgent()
        # 隨機獲取User-Agent
        request.headers['User-Agent'] = ua.random
        print(request.headers['User-Agent'])#打印

5.更換IP地址

有的網站會設置一個IP訪問頻率的閾值,一旦IP訪問頻率超過這個閾值,就會被認定爲機器人程序,進而封殺IP,禁止訪問網站的任何信息。一個很簡單的方法就是設置延時,但這顯然會降低爬蟲的效率,而IP地址又無法僞造。這時,就只能使用HTTP代理服務器了。

HTTP代理服務器(HTTP Proxy Server)其功能就是代理網絡用戶去取得網絡信息,它是客戶端瀏覽器和網站服務器之間的信息中轉站。

可以通過以下幾種方式獲取代理服務器:

  • 自行搭建代理服務器
    可以購買阿里雲或者騰訊雲服務器,自行搭建代理服務器。這種方式的優點是可靠、穩定;缺點是資金、時間和技術成本都比較高。

  • 使用免費代理服務器
    網絡上有許多免費的代理服務器供大家使用,搜索“代理”就能找到不少代理服務平臺,這些平臺一般都會提供免費代理服務器信息。這種方式的優點是免費、省心、省力;缺點是代理服務器有效期短、不穩定、不可控。

  • 購買付費代理服務器
    付費代理服務平衡了上述兩種方案,即在花費較少資金的情況下,提供可靠、穩定、時效較長的代理服務器。以下爲部分免費和付費代理服務平臺:

    • 西刺代理:提供免費代理,不提供付費代理服務

    • 快代理:提供免費代理和付費代理服務

    • 訊代理:只提供付費代理服務。

Scrapy設置代理服務器非常簡單,只需在Request請求中將代理服務器的URL賦給meta參數的鍵proxy。

Request(url,meta={"proxy": 'http://119.101.117.163:99999', "download_timeout": 10})

通過爬蟲,將西刺代理中的高匿代理服務器的URL爬取下來,經過驗證後,將有效的URL持久化到Redis數據庫中,提供給後續的爬蟲項目使用,主要代碼如下:

  1. 創建Scrapy項目
scrapy startproject xici_proxy
  1. 使用Item封裝數據
class XiciItem(scrapy.Item):
    url = scrapy.Field()#url
    cryptonym = scrapy.Field()#是否高匿名
  1. 創建Spider文件及Spider類
from scrapy import Request
from scrapy.spiders import Spider
from xx.items import XiciItem
from twisted.internet.error import DNSLookupError,TimeoutError, TCPTimedOutError#導入錯誤模塊

class XiciSpider(Spider):
    name = 'xici'
    current_page = 1 # 當前頁
    def __init__(self,url):
        self.test_url = url # 從命令中獲取測試網站的url

    #獲取初始Request
    def start_requests(self):
	    # 西祠代理免費代理的url地址
        url = "https://www.xicidaili.com/nn"
        yield Request(url)
    # 數據解析
    def parse(self, response):
        list_selector = response.xpath("//tr[@class='odd']")
        # 依次讀取每條代理的信息,從中獲取ip、端口、類型
        for one_selector in list_selector:
            item = XiciItem()
            # 獲取ip
            ip = one_selector.xpath("td[2]/text()").extract()[0]
            # 獲取端口
            port =  one_selector.xpath("td[3]/text()").extract()[0]
            # 獲取是否高匿
            cryptonym = one_selector.xpath("td[5]/text()").extract()[0]
            # 獲取類型(http或https)
            http = one_selector.xpath("td[6]/text()").extract()[0]
            # 拼接成完整的代理url
            url = "{}://{}:{}".format(http,ip,port)
            item["url"] = url
            # 一定要設置dont_filter=True不過濾重複請求
            yield Request(self.test_url,#測試網站的url
                          callback=self.test_parse,#回調函數
                          errback=self.error_back,#出錯回調函數
                          meta={"proxy":url,#代理服務器地址
                                "dont_retry":True,#請求不重試
                                "download_timeout":10,#超時時間
                                "item":item},
                          dont_filter=True#不過濾重複請求
                          )
        if self.current_page <= 5:#爬取5頁代理信息
            #獲取下一頁url
            next_url = response.xpath("//a[@class='next_page']/@href").extract()[0]
            next_url = response.urljoin(next_url)
            self.current_page+=1
            yield Request(next_url)

    # 測試網站的數據解析
    def test_parse(self, response):
        yield response.meta["item"]
    #請求失敗的回調函數
    def error_back(self,failure):
        #打印錯誤日誌信息
        self.logger.error(repr(failure))
        #細化出錯原因
        if failure.check(DNSLookupError):# DNS出錯
            # 獲取request
            request = failure.request
            #輸出錯誤日誌信息
            self.logger.error('DNSLookupError on %s', request.url)
        elif failure.check(TimeoutError, TCPTimedOutError):#超時出錯
            # 獲取request
            request = failure.request
            #輸出錯誤日誌信息
            self.logger.error('TimeoutError on %s', request.url)
  1. 使用Pipeline實現數據持久化
import redis  
class XiciProxyPipeline(object):
    # Spider開啓時,獲取數據庫配置信息,連接redis數據庫服務器
    def open_spider(self, spider):
        if spider.name == "xici":
            # 獲取配置文件中redis配置信息
            host = spider.settings.get("REDIS_HOST")  # 主機地址
            port = spider.settings.get("REDIS_PORT",)  # 端口
            db_index = spider.settings.get("REDIS_DB_INDEX")  # 索引
            db_psd = spider.settings.get("REDIS_PASSWORD")  # 密碼
            # 連接redis,得到一個連接對象
            self.db_conn = redis.StrictRedis(host=host, port=port, db=db_index,
                                             password=db_psd, decode_responses=True)
            self.db_conn.delete("ip")

    # 將數據存儲於redis數據庫
    def process_item(self, item, spider):
        if spider.name == "xici":
            # 將item轉換爲字典類型
            item_dict = dict(item)
            # 將item_dict保存於key爲ip的集合中
            self.db_conn.sadd("ip", item_dict["url"])
        return item
  1. settings.py中配置項
# 1 設置robots協議:ROBOTSTXT_OBEY(False爲不遵守協議
ROBOTSTXT_OBEY = False
# 2 設置用戶代理:USER_AGENT
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0;Win64; x64) " \
             "AppleWebKit/537.36 (KHTML, like Gecko) " \
             "Chrome/68.0.3440.106 Safari/537.36"
# 3 啓用管道:XiciProxyPipeline
ITEM_PIPELINES = {
    'xici_proxy.pipelines.XiciProxyPipeline': 300,
}

# 4 設置Redis數據庫地址、端口、索引及密碼信息
REDIS_HOST = "localhost"  # 主機地址
REDIS_PORT = 6379  # 端口
REDIS_DB_INDEX = 0  # 索引
REDIS_PASSWORD = ""  # 密碼

實現隨機代理

將免費可用的代理服務器信息保存到Redis數據庫後,所有的Scrapy爬蟲項目就可以使用它們了。需要注意的是,一個爬蟲項目的所有請求不能委託給固定的一個代理服務器,因爲目標網站依然會監測到同一IP頻繁訪問的異常現象。比較好的做法是每次請求時,隨機指定一個代理服務器,將請求分散到多個代理服務器中。

proxy = self.db_conn.srandmember("ip")  # 隨機獲取一個代理url
print("隨機代理URL:", proxy)
# 根據URL生成Request,使用yield返回給引擎
yield Request(next_url,#目標URL
             callback=self.qidian_parse,  # 回調函數
             errback=self.error_back,  # 異常時調用的函數
             meta={"proxy": proxy,  # 代理服務器URL
                   "download_timeout": 10  # 超時時間
                   }
             )

閱讀資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章