Дождитесь функции обратного вызова Scapy

#python #asynchronous #callback #scrapy

#python #асинхронный #обратный вызов #scrapy

Вопрос:

Я новичок в Scrapy и Python в целом.

Вот код:

 

import scrapy
import json

class MOOCSpider(scrapy.Spider):
    name = 'mooc'
    start_urls = ['https://www.plurk.com/search?q=italy']
    custom_settings = {
        'DUPEFILTER_CLASS': 'scrapy.dupefilters.BaseDupeFilter',
    }
    global_id = 1458122036

    def parse(self, response):
        

        url = 'https://www.plurk.com/Search/search2'

        headers = {
         ...omitted...
          }


        for i in range(1,10):
            formdata = {
            "after_id": str(self.global_id)
            }
            yield scrapy.FormRequest(url, callback=self.parse_api, formdata=formdata, headers=headers)


    def parse_api(self, response):
        raw = response.body
        data = json.loads(raw)
        posts = data["plurks"]
        users = data["users"]


        l = len(posts)
        i = 0
        for post in posts:
            i = i   1
            if (i == l):
                self.global_id = post["plurk_id"]
            
            ...omitted code...
            
            yield {
                'Author': user_name,
                'Body': post['content'],
                'app': 'plurk'
            }



 

Проблема, с которой я сталкиваюсь, заключается в том, что Scrapy сначала выполняет все запросы в цикле for, а затем выполняет код в parse_api .
Что я хотел бы сделать, так это позволить scrapy выполнить одну итерацию цикла for, вызвать функцию обратного вызова, дождаться ее возврата, а затем выполнить еще одну итерацию.

Это потому, что идентификатор, который мне нужен для следующего запроса, будет установлен в переменной global_id функцией обратного вызова.

Ответ №1:

Вы не можете достичь этого, планируя запросы в цикле.
Вы можете реализовать это, только если вы запланируете только один (следующий) запрос на вызов метода parse / parse_api:

 class MOOCSpider(scrapy.Spider):
    name = 'mooc'
    start_urls = ['https://www.plurk.com/search?q=italy']
    custom_settings = {
        'DUPEFILTER_CLASS': 'scrapy.dupefilters.BaseDupeFilter',
        'DOWNLOAD_DELAY':5,
        "USER_AGENT": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36",
    }

    def parse(self, response):
        # schedule only first request (withour loop)
        formdata = {
            "query": 'italy',
            "start_date": "2019/12",
            "end_date": "2020/12",
            "after_id": '1458122036', #<- your initial global_id
        }
        yield scrapy.FormRequest('https://www.plurk.com/Search/search2', callback=self.parse_api, formdata=formdata)

    def parse_api(self, response):
        data = json.loads(response.body)
        after_id = None
        for post in data["plurks"]:
            after_id = post["plurk_id"]
            yield {
                'Author': data["users"][str(post["owner_id"])]["nick_name"],  #  instead of user_id?
                'Body': post["content"],
                'app': 'plurk'
            }
        # after end of this loop - after_id should contain required data for next request

        # instead of separate loop variable response.meta["depth"] used to limit number requests
        if response.meta["depth"] <=11 and after_id:  # schedule next request
            formdata = {
                "query": 'italy',
                "start_date": "2019/12",
                "end_date": "2020/12",
                "after_id": str(after_id),
            }
            yield scrapy.FormRequest('https://www.plurk.com/Search/search2', callback=self.parse_api, formdata=formdata)
 

Ответ №2:

Отвечая на мой собственный вопрос:

Теперь метод parse выполняет только один запрос и один раз вызывает метод parse_api . Parse_api обрабатывает ответ и устанавливает переменную global_id. После завершения обработки собственного ответа он выполняет другой запрос, передавая себя в качестве функции обратного вызова. Делая это, вы гарантируете, что переменная global_id будет установлена правильно, поскольку новый запрос будет выполнен только после завершения выполнения parse_api.

request.cb_kwargs["loop_l"] используется для передачи дополнительного аргумента функции обратного вызова. На этот раз это счетчик, который контролирует количество запросов, которые мы хотим сделать. Когда счетчик равен 100, мы останавливаем сканирование

 import scrapy
import json

plurk_id = []
class MOOCSpider(scrapy.Spider):
    name = 'mooc'
    start_urls = ['https://www.plurk.com/search?q=']
    custom_settings = {
        'DUPEFILTER_CLASS': 'scrapy.dupefilters.BaseDupeFilter',
    }
    global_id = 1455890167

    url = 'https://www.plurk.com/Search/search2'

    headers = {
       ...OMITTED...
          }


    def parse(self, response):
        
        formdata = {
            "after_id": str(self.global_id)
        }
        request = scrapy.FormRequest(self.url, callback=self.parse_api, formdata=formdata, headers=self.headers)
        request.cb_kwargs["loop_l"] = str(0)
        yield request

    def parse_api(self, response, loop_l):
        int_loop_l = int(loop_l)
        int_loop_l = int_loop_l   1
     
        if (int_loop_l == 200):
            return
        raw = response.body
        data = json.loads(raw)

       ...omitted code...
       ... GET AND SET THE NEW global_id FROM THE RESPONSE ...
        
        # make another request with the new id
        formdata = {
            "after_id": str(self.global_id)
            }
        request = scrapy.FormRequest(self.url, callback=self.parse_api, formdata=formdata, headers=self.headers)
        request.cb_kwargs["loop_l"] = str(int_loop_l)
        yield request