scrapy redis原始碼解讀之傳送POST請求

2021-09-26 07:29:06 字數 4419 閱讀 1294

這段時間在研究美團爬蟲,用的是scrapy-redis分布式爬蟲框架,奈何scrapy-redis與scrapy框架不同,預設只傳送get請求,換句話說,不能直接傳送post請求,而美團的資料請求方式是post,網上找了一圈,發現關於scrapy-redis傳送post的資料寥寥無幾,只能自己剛原始碼了。

先來說一說需求,也就是說美團post請求形式。我們以獲取某個地理座標下,所有店鋪類別列表請求為例。獲取所有店鋪類別列表時,我們需要構造乙個包含位置座標經緯度等資訊的表單資料,以及為了向下一層parse方法傳遞的一些必要資料,即meta,然後發起乙個post請求。

url:

url = ''
url最後面的13位數字是時間戳,實際應用時用time模組生成一下就好了。

表單資料:

'''

'''form_data =

meta資料:

meta資料不是必須的,但是,如果你在傳送請求時,有一些資料需要向下一層parse方法(解析爬蟲返回的response的方法)中傳遞的話,就可以構造這一資料,然後作為引數傳遞進request中。

meta =
採集店鋪類別列表時需要傳送怎樣乙個post請求在上面已經說明了,那麼,在scrapy-redis框架中,這個post該如何來傳送呢?我相信,開啟我這篇博文的讀者都是用過scrapy的,用scrapy傳送post肯定沒問題(重寫start_requests方法即可),但scrapy-redis不同,scrapy-redis框架只會從配置好的redis資料庫中讀取起始url,所以,在scrapy-redis中,就算重寫start_requests方法也沒用。怎麼辦呢?我們看看原始碼。

我們知道,scrapy-redis與scrapy的乙個很大區別就是,scrapy-redis不再繼承spider類,而是繼承redisspider類的,所以,redisspider類原始碼將是我們分析的重點,我們開啟redisspider類,看看有沒有類似於scrapy框架中的start_requests、make_requests_from_url這樣的方法。redisspider原始碼如下:

'''

'''class redisspider(redismixin, spider):

@classmethod

def from_crawler(self, crawler, *args, **kwargs):

obj = super(redisspider, self).from_crawler(crawler, *args, **kwargs)

obj.setup_redis(crawler)

return obj

def start_requests(self):

return self.next_requests()

呵,真簡潔,直接把所有任務丟給next_requests方法,繼續:

'''

'''def next_requests(self):

"""returns a request to be scheduled or none."""

use_set = self.settings.getbool('redis_start_urls_as_set', defaults.start_urls_as_set)

fetch_one = self.server.spop if use_set else self.server.lpop

# ***: do we need to use a timeout here?

found = 0

# todo: use redis pipeline execution.

while found < self.redis_batch_size: # 每次讀取的量

data = fetch_one(self.redis_key) # 從redis中讀取一條記錄

if not data:

# queue empty.

break

req = self.make_request_from_data(data) # 根據從redis中讀取的記錄,例項化乙個request

if req:

yield req

found += 1

else:

self.logger.debug("request not made from data: %r", data)

if found:

self.logger.debug("read %s requests from '%s'", found, self.redis_key)

上面next_requests方法中,關鍵的就是那個while迴圈,每一次迴圈都呼叫了乙個make_request_from_data方法,從函式名可以函式,這個方法就是根據從redis中讀取從來的資料,例項化乙個request,那不就是我們要找的方法嗎?進入make_request_from_data方法一**竟:

def make_request_from_data(self, data):

url = bytes_to_str(data, self.redis_encoding)

return self.make_requests_from_url(url) # 這是重點,圈起來,要考

因為scrapy-redis預設值傳送get請求,所以,在這個make_request_from_data方法中認為data只包含乙個url,但如果我們要傳送post請求,這個data包含的東西可就多了,我們上面美團post請求說明中就說到,至少要包含url、form_data。所以,如果我們要傳送post請求,這裡必須改,make_request_from_data方法最後呼叫的make_requests_from_url是scrapy中的spider中的方法,不過,我們也不需要繼續往下看下去了,我想諸位都也清楚了,要傳送post請求,重寫這個make_request_from_data方法,根據傳入的data,例項化乙個request返回就好了。

明白上面這些東西後,就可以開始寫**了。修改原始碼嗎?不,不存在的,改原始碼可不是好習慣。我們直接在我們自己的spider類中重寫make_request_from_data方法就好了:

'''

'''from scrapy import formrequest

from scrapy_redis.spiders import redisspider

class meituanspider(redisspider):

"""此處省略若干行

"""def make_request_from_data(self, data):

"""重寫make_request_from_data方法,data是scrapy-redis讀取redis中的[url,form_data,meta],然後傳送post請求

:param data: redis中都去的請求資料,是乙個list

:return: 乙個formrequest物件

"""data = json.loads(data)

url = data.get('url')

form_data = data.get('form_data')

meta = data.get('meta')

return formrequest(url=url, formdata=form_data, meta=meta, callback=self.parse)

def parse(self, response):

pass

搞清楚原理之後,就是這麼簡單。萬事俱備,只欠東風——將url,form_data,meta儲存到redis中。另外新建乙個模組實現這一部分功能:

def push_start_url_data(request_data):

"""將乙個完整的request_data推送到redis的start_url列表中

:param request_data:

:return:

"""r.lpush('meituan:start_urls', request_data)

if __name__ == '__main__':

url = ''

form_data =

meta =

request_data =

push_start_url_data(json.dumps(request_data))

在啟動scrapy-redis之前,執行一下這一模組即可。如果有很多poi(地理位置興趣點),迴圈遍歷每乙個poi,生成request_data,push到redis中。這一迴圈功能就你自己寫吧。

沒有什麼是擼一遍原始碼解決不了的,如果有,就再擼一遍!

scrapy redis原始碼困惑記錄

原始碼可從github上獲取 可以把原始碼轉殖下來,執行如下命令 git clone r 字串 採用repr 的顯示 s 字串 採用str 的顯示 區別 str和repr str即以人類理解的方式轉化為字串,repr是以機器理解的方式直接為資料加上單引號,其他不變。pipe self.server....

azkaban web server原始碼解析

azkaban主要用於hadoop相關job任務的排程,但也可以應用任何需要排程管理的任務,可以完全代替crontab。azkaban主要分為web server 任務上傳,管理,排程 executor server 接受web server的排程指令,進行任務執行 1.資料表 projects 工...

JDK LinkedHashMap原始碼解析

今天來分析一下jdk linkedhashmap的源 public class linkedhashmapextends hashmapimplements map可以看到,linkedhashmap繼承自hashmap,並且也實現了map介面,所以linkedhashmap沿用了hashmap的大...