天天看點

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

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

小哈.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的需求。