當前位置:
首頁 > 知識 > Scrapy 爬蟲(2):聽說你的爬蟲又被封了?

Scrapy 爬蟲(2):聽說你的爬蟲又被封了?

(給

Python開發者

加星標,提升Python技能


作者:

Zone

(本文來自作者投稿)



前言


上一篇文章《

爬蟲利器初體驗(1)

》中,我們舉了個簡單的栗子,但是在真實的開發中這樣的爬蟲代碼很容易就會被封掉。那麼怎麼樣才能避免這些事發生呢?這一這篇文章我們一起來學習,如何健壯我們的爬蟲代碼。


Spider


當 start_urls 未被指定,會調用 start_requests() ,該方法可以用於在爬取數據之前,先進行模擬登陸。

import

 scrapy

from

 scrapy.http 

import

 Request

from

 scrapy.selector 

import

 Selector

from

 urllib.parse 

import

 urljoin

from

 doubanTop250.items 

import

 Doubantop250Item

class

 

DoubanTop250Spider

(scrapy.spiders.Spider)

:


    

# 此處為上面留下的小坑


    name = 

"douban"


    

# 設置允許爬取的域名


    allowed_domains = [

"douban.com"

]
    

# header信息


    my_header = {
        

"Host"

"www.douban.com"

,
        

"Referer"

"https://movie.douban.com"

,
    }
    

# 表單需要提交的數據


    form_data = {

"user"

"zone"

"pass"

"zone7"

}
    

# 自定義信息,向下層響應(response)傳遞下去


    customer_data = {

"key1"

"value1"

"key2"

"value2"

}
    

def

 

start_requests

(self)

:


        

return

 [scrapy.FormRequest(

"https://movie.douban.com/login"

,
                                   formdata=self.form_data,  

# 表單提交的數據


                                   headers=self.my_header,
                                   method=

"POST"

,  

# GET or POST


                                   meta=self.customer_data,  

# 自定義,向response傳遞數據


                                   errback=self.error_handle,
                                   callback=self.logged_in,
                                   

# 如果需要多次提交表單,且url一樣,那麼就必須加此參數 dont_filter,防止被當成重複網頁過濾掉了


                                   dont_filter=

True


                                   )]

    

def

 

logged_in

(self, response)

:


        

# 解析模擬登陸數據


        

pass

    

def

 

parse

(self, response)

:


        

# 默認回調函數


        

pass

    

def

 

close

(self,reson)

:


        

# 關閉時調用


        

pass



scrapy.FormRequest 部分參數解析


parse(self, response)


當 start_requests 沒有指定回調函數時,默認回調 parse(self,response) 方法。parse 負責處理 response 並返回處理的數據以及(/或)跟進的 URL。

該方法及其他的 Request 回調函數必須返回一個包含 Request、dict 或 Item 的可迭代的對象。簡單的來說,所謂的回調函數,其實就是告訴spider,在拿到了網站的 response 以後,交給誰來進行處理後面的頁面的解析工作。這個看上一篇文章的代碼就可以知道。


Middleware


這裡說一下,關於自定義 middleware 需要重寫的幾個方法,可能你看完這一段長篇大論之後,還是懵逼的,沒關係,我也是這樣。不過,當你繼續往下看,看一個實例之後,你就會明白。


process_request(request, spider)


當每個request通過下載中間件時,該方法被調用。


process_request() 必須返回其中之一: 返回 None 、返回一個 Response 對象、返回一個 Request 對象或 raise IgnoreRequest 。




  1. 如果其返回 None ,Scrapy將繼續處理該request,執行其他的中間件的相應方法,直到合適的下載器處理函數(download handler)被調用, 該request被執行(其response被下載)。



  2. 如果其返回 Response 對象,Scrapy將不會調用 任何 其他的 process_request() 或 process_exception() 方法,或相應地下載函數; 其將返回該 response。 已安裝的中間件的 process_response() 方法則會在每個 response 返回時被調用。



  3. 如果其返回 Request 對象,Scrapy則停止調用 process_request方法並重新調度返回的 request。當新返回的 request 被執行後, 相應地中間件鏈將會根據下載的 response 被調用。


如果其 raise 一個 IgnoreRequest 異常,則安裝的下載中間件的 process_exception() 方法會被調用。如果沒有任何一個方法處理該異常, 則 request 的errback(Request.errback) 方法會被調用。如果沒有代碼處理拋出的異常, 則該異常被忽略且不記錄(不同於其他異常那樣)。


參數:




  1. request(Request 對象)–處理的 request



  2. spider(Spider 對象)–該 request 對應的 spider


process_response(request, response, spider)


process_request() 必須返回以下之一: 返回一個 Response 對象、 返回一個 Request 對象或 raise 一個 IgnoreRequest 異常。




  1. 如果其返回一個 Response (可以與傳入的response相同,也可以是全新的對象), 該response會被在鏈中的其他中間件的 process_response() 方法處理。



  2. 如果其返回一個 Request 對象,則中間件鏈停止, 返回的request會被重新調度下載。處理類似於 process_request() 返回request所做的那樣。



  3. 如果其拋出一個 IgnoreRequest 異常,則調用 request 的 errback( Request.errback)。 如果沒有代碼處理拋出的異常,則該異常被忽略且不記錄(不同於其他異常那樣)。


參數:




  1. request (Request 對象) – response 所對應的 request



  2. response (Response 對象) – 被處理的 response



  3. spider (Spider 對象) – response 所對應的 spider


process_exception(request, exception, spider)


當下載處理器(download handler)或 process_request() (下載中間件)拋出異常(包括IgnoreRequest異常)時,Scrapy調用 process_exception() 。


process_exception() 應該返回以下之一: 返回 None 、 一個 Response 對象、或者一個 Request 對象。




  1. 如果其返回 None ,Scrapy將會繼續處理該異常,接著調用已安裝的其他中間件的 process_exception() 方法,直到所有中間件都被調用完畢,則調用默認的異常處理。



  2. 如果其返回一個 Response 對象,則已安裝的中間件鏈的 process_response() 方法被調用。Scrapy將不會調用任何其他中間件的 process_exception() 方法。



  3. 如果其返回一個 Request 對象, 則返回的request將會被重新調用下載。這將停止中間件的 process_exception() 方法執行,就如返回一個response的那樣。


參數:




  1. request (是 Request 對象) – 產生異常的request



  2. exception (Exception 對象) – 拋出的異常



  3. spider (Spider 對象) – request對應的spider


UserAgentMiddleware


多數情況下,網站都會通過 user-agent 來識別訪問的用戶是否為一台機器,就像爬取豆瓣一樣,如果你沒有設置 user-agent 的話,就會被拒絕訪問。因此,我們只使用一個 user-agent 顯然是不夠的,因此,我們要想個辦法來設置隨機的 user-agent。於是,我們便可以自定義 UserAgentMiddleware 來解決這個問題。


首先,在 setting.py 中添加 user-agent ,如下:

MY_USER_AGENT = [
    

"MSIE (MSIE 6.0; X11; Linux; i686) Opera 7.23",


    

"iTunes/9.0.3 (Macintosh; U; Intel Mac OS X 10_6_2; en-ca)",


    

"Opera/9.20 (Macintosh; Intel Mac OS X; U; en)",


    

"Opera/9.0 (Macintosh; PPC Mac OS X; U; en)",


    

"Mozilla/4.76 [en_jp] (X11; U; SunOS 5.8 sun4u)",


    

"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 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5",


    

"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER",


    

"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",


    

"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 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11",


    

"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)",


    

"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11",


    

"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 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 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.20 (KHTML, like Gecko) Chrome/19.0.1036.7 Safari/535.20",


    

"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 (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 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER",


    

"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1",


    

"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/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.12) Gecko/20070731 Ubuntu/dapper-security Firefox/1.5.0.12",


    

"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/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1",


    

"Mozilla/4.8 [en] (X11; U; SunOS; 5.7 sun4u)"


]

然後在 middlewares.py 中添加如下代碼:

class

 

MyUserAgentMiddleware

(

UserAgentMiddleware

):


    

def

 

__init__

(

self

, user_agent)

:
        

self

.user_agent = user_agent

    @classmethod
    

def

 

from_crawler

(cls, crawler)

:
        

return

 cls(
            user_agent=crawler.settings.get(

"MY_USER_AGENT"

)
        )

    

def

 

process_request

(

self

, request, spider)

:
        agent = random.choice(

self

.user_agent)
        request.headers[

"User-Agent"

] = agent

最後啟用這個 middleware ,在 setting.py 中修改 MyUserAgentMiddleware 的優先順序,如下:

DOWNLOADER_MIDDLEWARES = {
    

# 首先禁止其原有的 UserAgentMiddleware


    

"scrapy.downloadermiddleware.useragent.UserAgentMiddleware"

None

,
    

"doubanTop250.middlewares.MyUserAgentMiddleware"

300

,
    

"doubanTop250.middlewares.ProxyMiddleware"

543

,
}

ProxyMiddleware


除了要更改 user-agent 之外,我們還要使用 ip 代理來健壯我們的爬蟲程序。那麼,在 scrapy 中如何來設置我們的 ip 代理呢?


首先,在 setting.py 中添加如下代碼,為代理 ip:

PROXIES = [
    

"194.186.20.62:21231"


    , 

"66.153.222.162:54684"


    , 

"219.234.5.128:3128"


    , 

"117.114.149.66:53281"


    , 

"117.114.149.66:53281"


           ]

ip 來自西刺代理,不過好像好多沒用的代理。


然後在 middlewares.py 中添加如下代碼:

from scrapy import signals
import random

class

 

ProxyMiddleware

(

object

):


    

def

 

__init__

(

self

, ip)

:
        

self

.ip = ip

    @classmethod
    

def

 

from_crawler

(cls, crawler)

:
        

return

 cls(ip=crawler.settings.get(

"PROXIES"

))

    

def

 

process_request

(

self

, request, spider)

:
        ip = random.choice(

self

.ip)
        request.meta[

"proxy"

] = ip

最後啟用這個 middleware ,在 setting.py 中修改 ProxyMiddleware 的優先順序,如下:

DOWNLOADER_MIDDLEWARES = {
    

# 首先禁止其原有的 UserAgentMiddleware


    

"scrapy.downloadermiddleware.useragent.UserAgentMiddleware"

None

,
    

"doubanTop250.middlewares.MyUserAgentMiddleware"

300

,
    

"doubanTop250.middlewares.ProxyMiddleware"

543

,
}

瞎比比


經過這些操作之後,你的代碼等級就提升了一個高度了。說了那麼多,我們好像還沒有將我們抓取的數據保存下來,那麼下一篇文章將會說說如何保存我們的數據。



【本文作者】

Zone:一隻愛折騰的後端攻城獅,喜歡寫文分享。個人公眾號:zone7




推薦閱讀


(點擊標題可跳轉閱讀)


用 Python 全自動下載抖音小姐姐視頻


爬取虎嗅 5 萬篇文章告訴你怎麼樣取標題


爬蟲進階:反反爬蟲技巧

覺得本文對你有幫助?請分享給更多人


關注「Python開發者」加星標,提升Python技能



喜歡就點一下「好看」唄~

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Python開發者 的精彩文章:

Python 之父退位後,最高決策權花落誰家?
人人都能讀懂的編譯器原理

TAG:Python開發者 |