Scrapy使用隨機User-Agent爬取網站

weixin_34365417發表於2018-08-31
5728077-fdabdf948e82e2eb.jpg
小哈.jpg

在爬蟲爬取過程中,我們常常會使用各種各樣的偽裝來降低被目標網站反爬的概率,其中隨機更換User-Agent就是一種手段。
在scrapy中,其實已經內建了User-Agent中介軟體,

class UserAgentMiddleware(object):
    """This middleware allows spiders to override the user_agent"""

    def __init__(self, user_agent='Scrapy'):
        self.user_agent = user_agent

    @classmethod
    def from_crawler(cls, crawler):
        o = cls(crawler.settings['USER_AGENT'])
        crawler.signals.connect(o.spider_opened, signal=signals.spider_opened)
        return o

    def spider_opened(self, spider):
        self.user_agent = getattr(spider, 'user_agent', self.user_agent)

    def process_request(self, request, spider):
        if self.user_agent:
            request.headers.setdefault(b'User-Agent', self.user_agent)

上圖是scrapy自帶的UserAgentMiddleware中介軟體,通過程式碼可以發現,如果我們沒有在setting配置檔案中設定headers的User-Agent,scrapy會把User-Agent設定為"Scrapy"。

原理

當我們通過 spider yield 一個 request 的時候,首先通過 spider middlewares 到達 scrapy engine,然後 engine 將 request 放到 scheduler 的佇列中,通過 scheduler 排程佇列中的 request ,scheduler 選中一個 request 後,將 request 通過 engine 傳遞給 downloader,在這之前,必然會經過 downloader middlewares,downloader 下載好之後,將 response 返回給 engine,engine 在將 response 返回給 spider,我們就可以在 spider 中呼叫 callback 進行解析,簡單的流程大概就是這樣。

那麼,我們在將 request 提交給 downloader 進行下載之前,就需要將 User-Agent 進行變化,也就是每次都需要隨機取一個 User-Agent 提交到 downloader 進行下載。在提交到 downloader 的時候,必然會經過 downloader middlewares,所以我們實現隨機獲取 User-Agent 的邏輯部分,可以在 downloader midllewares 這裡實現。

第一種方法

可以把多個User-Agent作為一個配置在setting檔案中

user_agent_list = [
    "ua1",
    "ua2",
    "ua3",
]

然後再編寫downloader midllewares

class RandomUserAgentMiddleware(object):
    def __init__(self, crawler):
        super(RandomUserAgentMiddleware, self).__init__()
        self.user_agent_list = crawler.get("user_agent_list", [])

    @classmethod
    def from_crawler(cls, crawler):
        return cls(crawler)

    def process_request(self, request, spider):
        #無效的方法
        request.headers.setdefault("User-Agent", random.choice(self.user_agent_list))
        #有效的方法
        request.headers['User-Agent'] = random.choice(self.user_agent_list)
注意:

上圖程式碼在process_request方法中寫了兩種設定User-Agent的方法,其中上邊那種方法經過測試是結果是無效的,所以採用下邊那種方法。因為setdefault這個方法是:如果沒有User-Agent 這個鍵才會設定User-Agent並把User-Agent的value設定為random.choice(self.user_agent_list),但其實程式碼執行到這裡時,User-Agent 這個鍵是存在的,所以使用setdefault不會生效。如果這邊有疑問可以評論提問我。

補充更正:

上邊說的 程式碼在process_request方法中寫了兩種設定User-Agent的方法,其實都可以,之前之所以上邊那行不能實現是因為我在setting中把scrapy自帶的UserAgentMiddleware取消沒有成功,原因是路徑寫錯了,現給出正確配置方法:

DOWNLOADER_MIDDLEWARES = {
    'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware': None,
    'crawl_spider.middlewares.RandomUserAgentMiddleware': 543,
}

先把scrapy自帶的UserAgentMiddleware置為None,再增加我們自己寫的中介軟體便可,

這樣做可以實現切換User-Agent的功能,但是第一需要自己維護一個大的User-Agent list在配置檔案中,第二就是有侷限性,畢竟維護的User-Agent不會有那麼的大而全,所以這裡介紹另一種方法。

第二種方法(推薦)

fake-useragent 這個庫提供了我們隨機選擇useragent的功能。
感興趣的同學可以深入研究下原始碼,原始碼很簡單,這裡只介紹怎麼在scrapy中使用它。
話不多說上程式碼

class RandomUserAgentMiddleware(object):
    def __init__(self, crawler):
        super(RandomUserAgentMiddleware, self).__init__()
        self.ua = UserAgent()
        self.ua_type = crawler.settings.get("RANDOM_UA_TYPE", "random")

    @classmethod
    def from_crawler(cls, crawler):
        return cls(crawler)

    def process_request(self, request, spider):
        def get_ua():
            return getattr(self.ua, self.ua_type)
        request.headers['User-Agent'] = get_ua()

首先我們在setting配置檔案中設定一個變數RANDOM_UA_TYPE,它的功能是可以按照我們自己配置的值來選擇useragent。

# 隨機選擇UA
RANDOM_UA_TYPE = "random"
# 只選擇ie的UA
RANDOM_UA_TYPE = "ie"

當然了,最終我們還要把我們的RandomUserAgentMiddleware中介軟體配置到setting中:

DOWNLOADER_MIDDLEWARES = {
    'crawl_spider.middlewares.RandomUserAgentMiddleware': 543,
}

至此,完成了scrapy加隨機User-Agent的需求。

相關文章