1. 程式人生 > 程式設計 >關於python scrapy中新增cookie踩坑記錄

關於python scrapy中新增cookie踩坑記錄

問題發現:

前段時間專案中,為了防止被封號(提供的可用賬號太少),對於能不登入就可以抓取的內容採用不帶cookie的策略,只有必要的內容才帶上cookie去訪問。

本來想著很簡單:在每個丟擲來的Request的meta中帶上一個標誌位,通過在CookieMiddleware中檢視這個標誌位,決定是否是給這個Request是否裝上Cookie。

實現的程式碼大致如下:

class CookieMiddleware(object):
  """
  每次請求都隨機從賬號池中選擇一個賬號去訪問
  """

  def __init__(self):
    client = pymongo.MongoClient(MONGO_URI)
    self.account_collection = client[MONGO_DATABASE][ACCOUNT_COLLECTION]

  def process_request(self,request,spider):
    if 'target' in request.meta: 
      logging.debug('進入到process_request了')
      flag = request.meta['target']
      if flag != 'no':
        all_count = self.account_collection.find({'status': 'success'}).count()
        if all_count == 0:
          raise Exception('當前賬號池為空')
        random_index = random.randint(0,all_count - 1)
        random_account = self.account_collection.find({'status': 'success'})[random_index]
        
        request.cookies = json.loads(random_account['cookie'])
      else:
        logging.debug('對XXX的請求不做處理')
    else:
      all_count = self.account_collection.find({'status': 'success'}).count()
      if all_count == 0:
        raise Exception('當前賬號池為空')
      random_index = random.randint(0,all_count - 1)
      random_account = self.account_collection.find({'status': 'success'})[random_index]
      
      request.cookies = json.loads(random_account['cookie'])

在settings.py中的配置如下:

DOWNLOADER_MIDDLEWARES = {
  'eyny.middlewares.CookieMiddleware': 550,}

到這裡可能有些大佬已經能夠看出端倪了,和我一樣認為這麼寫沒啥問題的同志們繼續往下看。

在這麼編寫完之後,我正常開啟了專案,還適當調高了併發量,然後第二天發現賬號被封了。在debug過程中看到在抓取不需要攜帶cookie的url的時候,依然攜帶了cookie,並且cookie是被放在了header中,經過我花費了兩個多小時檢視框架原始碼之後,終於發現了原因。

原因&解決方案:

在scrapy的settings目錄下的default_settings.py檔案中,初始聲明瞭一些DOWNLOADER_MIDDLEWARES_BASE,這些middlewares的宣告如下:

DOWNLOADER_MIDDLEWARES_BASE = {
  # Engine side
  'scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware': 100,'scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware': 300,'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware': 350,'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware': 400,'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': 500,'scrapy.downloadermiddlewares.retry.RetryMiddleware': 550,'scrapy.downloadermiddlewares.ajaxcrawl.AjaxCrawlMiddleware': 560,'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware': 580,'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 590,'scrapy.downloadermiddlewares.redirect.RedirectMiddleware': 600,'scrapy.downloadermiddlewares.cookies.CookiesMiddleware': 700,'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 750,'scrapy.downloadermiddlewares.stats.DownloaderStats': 850,'scrapy.downloadermiddlewares.httpcache.HttpCacheMiddleware': 900,# Downloader side
}

可以看到在DOWNLOADER_MIDDLEWARES_BASE中也聲明瞭一個CookiesMiddleware,而且是700,也就是說比我們寫的CookieMiddleware(500)要靠後執行,而且在debug過程中也看到,在執行完我們編寫的CookieMiddleware之後,header中沒有攜帶cookie,但是在執行完scrapy.downloadermiddlewares.cookies.CookiesMiddleware: 700之後,在header中看到了cookie,這說明cookie是scrapy幫我們自動加了。

我們開啟scrapy.downloadermiddlewares.cookies.CookiesMiddleware的實現原始碼,主要關注process_request方法:

class CookiesMiddleware(object):
  """This middleware enables working with sites that need cookies"""

  def __init__(self,debug=False):
    self.jars = defaultdict(CookieJar)
    self.debug = debug

  @classmethod
  def from_crawler(cls,crawler):
    if not crawler.settings.getbool('COOKIES_ENABLED'):
      raise NotConfigured
    return cls(crawler.settings.getbool('COOKIES_DEBUG'))

  def process_request(self,spider):
    if request.meta.get('dont_merge_cookies',False):
      return

    cookiejarkey = request.meta.get("cookiejar")
    jar = self.jars[cookiejarkey]
    cookies = self._get_request_cookies(jar,request)
    for cookie in cookies:
      jar.set_cookie_if_ok(cookie,request)

    # set Cookie header
    request.headers.pop('Cookie',None)
    jar.add_cookie_header(request)
    self._debug_cookie(request,spider)

	def process_response(self,response,False):
      return response

    # extract cookies from Set-Cookie and drop invalid/expired cookies
    cookiejarkey = request.meta.get("cookiejar")
    jar = self.jars[cookiejarkey]
    jar.extract_cookies(response,request)
    self._debug_set_cookie(response,spider)

    return response

在上面的程式碼中,最中要的是process_request方法中的內容,可以看到首先從request.meta中檢視有沒有dont_merge_cookies屬性,如果沒有或者為false,就不執行剩下的方法,臥槽,這就是我們要找的方法呀!是不是好簡單…

特別注意如果要使用dont_merge_cookies=true,那麼需要我們自己將cookie加入到header中,通過**request.cookies = json.loads(random_account[‘cookie'])**方式新增的cookie,scrapy也不再會幫我們合併到header 中了。

解決方案我們的解決方法就是在request的meta中加入dont_merge_cookies屬性,並設定為true,在CookieMiddleware中,我們將cookie新增在header中,而不是賦值給request.cookies

問題解決了,但是這麼簡單是不是很不爽,所以就繼續想看看是為什麼scrapy可以自動給我們加上cookie,這個接下來就需要讀下面的程式碼了。

總結

到此這篇關於關於python scrapy中新增cookie踩坑記錄的文章就介紹到這了,更多相關scrapy cookie問題內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!