Почему мой scrapy webcrawler застревает на некоторых страницах 404 с ошибкой 504, а не на других? - PullRequest
0 голосов
/ 05 августа 2020

Я создаю веб-краулер для очистки критических c видеоигр и оценок пользователей от Metacriti c. Я добавляю данные в список видеоигр и их консолей, который у меня уже есть. Естественно, я ожидаю, что некоторые из них не будут работать, поскольку они могут иметь немного разные названия и т. Д. c.

Он работает довольно хорошо, но, похоже, застревает на определенных URL-адресах, ведущих на страницу 404. Иногда он заходит дальше других попыток, что для меня самое странное. Наибольшее значение, которое я получил, составляет где-то около 200. Кажется, что иногда 404-е обрабатываются отлично с обработкой, которую я для него построил, а в других случаях я получаю сообщение об ошибке:

ERROR: Gave up retrying <GET https://www.metacritic.com/game/playstation-4/god-of-war-(2018) (failed 3 times): 504 Gateway Time-out

с любым URL-адресом ошибки 404, который он пытается захватить (я знаю, что могу очистить это «(2018)» и другие подобные вещи позже, но есть некоторые нормальные заголовки, для которых это тоже происходит.

# The gameNumber variable just tells me where in my list of preset video game titles I am, it increases
# with each crawl in order to move to the next game in the list.

gameNumber = 0

class VGSpider(scrapy.Spider):
    
    # Brings in the aforementioned game "lookupList" and the length of that list, so the crawler knows 
    # when to stop
    global lookupList
    global finalEntry
    
    
    name = "mc_spider"
    start_urls = ["https://www.metacritic.com/game/" + consoleDict[lookupList[0][1]] + "/" + formatTitle(lookupList[0][2])]
    handle_httpstatus_list = [404]
    # Here's where we set the logging settings. 
    custom_settings = {
        'LOG_LEVEL': logging.WARNING,
        'ITEM_PIPELINES': {'__main__.JsonWriterPipeline': 1}, # This uses the functions in the JsonWriterPipeline class to write the 
                                                              # Julia file
        'FEED_FORMAT':'json',                                 # This sets the feed exporter to export as a JSON file
        'FEED_URI': 'mcresult.json',                       # This simply sets the title for said JSON file
        'DOWNLOAD_TIMEOUT' : 180
    }
    


    # I added a user agent in case Metacritic was blocking me from scraping

    headers = {
        'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36'
    }
    
    def start_requests(self):
        yield Request(self.start_urls[0], headers=self.headers)
        
    def parse(self, response):
        global gameNumber
        global consoleDict
        print(gameNumber)
        
        # Here we declare the selector for each piece of data--this tells scrapy where to check for each item
        
        CRITIC_SCORE_SELECTOR = "//*[@id=\"main\"]/div/div[1]/div[1]/div[3]/div/div/div[2]/div[1]/div[1]/div/div/a/div/span/text()"
        CRITIC_REVIEW_COUNT_SELECTOR = "//*[@id=\"main\"]/div/div[1]/div[1]/div[3]/div/div/div[2]/div[1]/div[1]/div/div/div[2]/p/span[2]/a/span/text()"
        PLAYERS_SELECTOR = "//*[@id=\"main\"]/div/div[1]/div[1]/div[3]/div/div/div[2]/div[2]/div[2]/ul/li[3]/span[2]/text()"
        RELEASEDATE_SELECTOR = "//*[@id=\"main\"]/div/div[1]/div[1]/div[1]/div[3]/ul/li[2]/span[2]/text()"
        USER_SCORE_SELECTOR = "//*[@id=\"main\"]/div/div[1]/div[1]/div[3]/div/div/div[2]/div[1]/div[2]/div[1]/div/a/div/text()"
        USER_REVIEW_COUNT_SELECTOR = "//*[@id=\"main\"]/div/div[1]/div[1]/div[3]/div/div/div[2]/div[1]/div[2]/div[1]/div/div[2]/p/span[2]/a/text()"
        RATING_SELECTOR = "//*[@id=\"main\"]/div/div[1]/div[1]/div[3]/div/div/div[2]/div[2]/div[2]/ul/li[5]/span[2]/text()"

        
        # We check the page--if the page doesn't exist, we fill in the data with NaN values, otherwise we grab them off the page
        if (response.status == 404):
            yield {
                'mc_critic_score' : np.nan,
                'mc_critic_count' : np.nan,
                'mc_players' : np.nan,
                'mc_release_date' : np.nan,
                'mc_user_score' : np.nan,
                'mc_user_count' : np.nan,
                'rating' : np.nan,
                'id' : lookupList[gameNumber][0]
                }
        else:
            try:
                yield {
                    'mc_critic_score' : response.xpath(CRITIC_SCORE_SELECTOR).extract(),
                    'mc_critic_count' : response.xpath(CRITIC_REVIEW_COUNT_SELECTOR).extract(),
                    'mc_players' : response.xpath(PLAYERS_SELECTOR).extract(),
                    'mc_release_date' : response.xpath(RELEASEDATE_SELECTOR).extract(),
                    'mc_user_score' : response.xpath(USER_SCORE_SELECTOR).extract(),
                    'mc_user_count' : response.xpath(USER_REVIEW_COUNT_SELECTOR).extract(),
                    'rating' : response.xpath(RATING_SELECTOR).extract(),
                    'id' : lookupList[gameNumber][0]
                    }
                
            except:
                pass
        

        
        
        # Add to the gamenumber and use it to select the information to query the next page
        gameNumber += 1
        next_page = "https://www.metacritic.com/game/" + consoleDict[lookupList[gameNumber][1]] + "/" + formatTitle(lookupList[gameNumber][2])
        if gameNumber == finalEntry:
            return
            
        else: 
            yield scrapy.Request(
            response.urljoin(next_page),
            callback=self.parse
            )
        
                
...