Невозможно очистить коды статистики 301 и 302 с помощью скрапа - PullRequest
0 голосов
/ 29 сентября 2019

Добрый день, все

Я столкнулся с небольшой проблемой, пытаясь очистить данные с сайта публикации вакансий, я также немного новичок в Python и scrapy в целом.

У меня есть скрипт, который я запускаю для извлечения данных из различных действительно сообщений. Похоже, что сканер завершает работу без ошибок, но не извлекает данные из сайтов, которые отвечают кодом ошибки 301 или 302.

Я вставил скрипт и журнал внизу

Любая помощьбыл бы признателен

import scrapy
from scrapy import Request

class JobsSpider(scrapy.Spider):
    name = "jobs"
    allowed_domains = ["indeed.com"]
    start_urls = ["https://www.indeed.com/jobs?q=%22owner+operator%22&l=dallas"]

    def parse(self, response):
        handle_httpstatus_list = [True]
        jobs = response.xpath('//div[@class="title"]')

        for job in jobs:
            title = job.xpath('a//@title').extract_first()
            posting_link = job.xpath('a//@href').extract_first()
            posting_url = "https://indeed.com" + posting_link
            job_location = job.xpath('div//@data-rc-loc').extract_first()


            yield Request(posting_url, callback=self.parse_page, meta={'title': title, 'posting_url':posting_url, 'job_location':job_location})

        relative_next_url = response.xpath('//link[@rel="next"]/@href').extract_first()
        absolute_next_url = "https://indeed.com" + relative_next_url

        yield Request(absolute_next_url, callback=self.parse)


    def parse_page(self, response):
        posting_url = response.meta.get('posting_url')
        title = response.meta.get('title')
        job_location = response.meta.get('job_location')

        job_name= response.xpath('//*[@class="icl-u-xs-mb--xs icl-u-xs-mt--none  jobsearch-JobInfoHeader-title"]/text()').extract_first()
        job_description_1=response.xpath('//div[@class="jobsearch-jobDescriptionText"]/ul').extract()
        posted_on_date= response.xpath('//div[@class="jobsearch-JobMetadataFooter"]/text()').extract_first()
        job_location=response.xpath('//*[@class="jobsearch-InlineCompanyRating icl-u-xs-mt--xs  jobsearch-DesktopStickyContainer-companyrating"]/div[3]/text()').extract_first()
        job_description_2=response.xpath('//div[@class="jobsearch-JobComponent-description  icl-u-xs-mt--md  "]/text()').extract_first()

        yield{'title':title,
            'posting_url':posting_url,
            'job_name':job_name,
            'job_location': job_location,
            'job_description_1':job_description_1,
            'posted_on_date':posted_on_date,
            'job_description_2':job_description_2,
            'job_location':job_location
             }

2019-09-29 12:37:53 [scrapy.core.engine] INFO: Closing spider (finished)
2019-09-29 12:37:53 [scrapy.statscollectors] INFO: Dumping Scrapy stats:
{'downloader/request_bytes': 1860897,
 'downloader/request_count': 1616,
 'downloader/request_method_count/GET': 1616,
 'downloader/response_bytes': 13605809,
 'downloader/response_count': 1616,
 'downloader/response_status_count/200': 360,
 'downloader/response_status_count/301': 758,
 'downloader/response_status_count/302': 498,
 'dupefilter/filtered': 9,
 'elapsed_time_seconds': 28.657843,
 'finish_reason': 'finished',
 'finish_time': datetime.datetime(2019, 9, 29, 19, 37, 53, 776779),
 'item_scraped_count': 337,
 'log_count/DEBUG': 1954,
 'log_count/ERROR': 1,
 'log_count/INFO': 10,
 'memusage/max': 54546432,
 'memusage/startup': 54546432,
 'request_depth_max': 20,
 'response_received_count': 360,
 'robotstxt/request_count': 3,
 'robotstxt/response_count': 3,
 'robotstxt/response_status_count/200': 3,
 'scheduler/dequeued': 1612,
 'scheduler/dequeued/memory': 1612,
 'scheduler/enqueued': 1612,
 'scheduler/enqueued/memory': 1612,
 'spider_exceptions/TypeError': 1,
 'start_time': datetime.datetime(2019, 9, 29, 19, 37, 25, 118936)}
2019-09-29 12:37:53 [scrapy.core.engine] INFO: Spider closed (finished)


  [1]: https://i.stack.imgur.com/6MOMC.png

Ответы [ 2 ]

1 голос
/ 30 сентября 2019

В соответствии с документацией RedirectMiddleware у вас есть несколько различных способов выхода из этой ситуации:

  • настройка dont_redirect=True в конкретном Request.meta
  • установка handle_httpstatus_all=True в определенном Request.meta
  • добавление handle_httpstatus_list в качестве атрибута Spider, содержимое которого представляет собой числовые коды HTTP, для которых Паук желает обработатьфактическое перенаправление Response
  • или, конечно, отключение RedirectMiddleware в вашем settings.py с помощью REDIRECT_ENABLED = False, что заставит каждого Паука отвечать за свою собственную обработку перенаправления
0 голосов
/ 30 сентября 2019

Я только что провел быструю проверку вашего скребка, и мне кажется, что он на самом деле работает так, как должен.

РЕДАКТИРОВАТЬ: В попытке сделать мое объяснение более четким;Вы не можете очистить 301 или 302 перенаправления, потому что они - только это;перенаправляет. Если вы запрашиваете URL-адрес, который перенаправляется, Scrapy автоматически обрабатывает его и удаляет данные со страницы, на которую вы перенаправлены. Это конечный пункт назначения из перенаправления, который даст вам ответ 200.

Если вы будете следовать логике, представленной ниже, вы увидите, что Scrapy запрашивает URL http://www.indeed.com/rc/clk?jk=69995bf12d9f2f9a&fccid=b87e01ade6c824ee&vjs=3,, но перенаправляется на https://www.indeed.com/viewjob?jk=69995bf12d9f2f9a&from=serp&vjs=3. Это последняя страница, которую вы сможете почистить. (Вы можете попробовать это самостоятельно, щелкнув начальный URL-адрес и сравнив его с последним URL-адресом, на котором вы в конечном итоге)

Просто повторюсь, вы не сможете очиститьчто-нибудь из 301 и 302 перенаправлений (там нечего очищать), только последняя страница, которая получает ответ 200.

Я приложил предложенную версию вашего скребка, котораясохраняет как запрошенный URL, так и очищенный URL. Все выглядит хорошо для меня, ваш скребок работает так, как положено. (Тем не менее, обратите внимание, что действительно.com будет обслуживать вас только до 19 страниц результатов поиска, что ограничивает вас 190 записками)

Надеюсь, теперь это имеет смысл.

Вот один пример из вывода, начиная с исходного запроса:

2019-09-30 10:37:06 [scrapy.downloadermiddlewares.redirect] DEBUG: Redirecting (301) to <GET https://www.indeed.com/rc/clk?jk=69995bf12d9f2f9a&fccid=b87e01ade6c824ee&vjs=3> from <GET http://www.indeed.com/rc/clk?jk=69995bf12d9f2f9a&fccid=b87e01ade6c824ee&vjs=3>

Это перенаправляется с 301 перенаправлением на следующую ссылку:

2019-09-30 10:37:06 [scrapy.downloadermiddlewares.redirect] DEBUG: Redirecting (302) to <GET https://www.indeed.com/viewjob?jk=69995bf12d9f2f9a&from=serp&vjs=3> from <GET https://www.indeed.com/rc/clk?jk=69995bf12d9f2f9a&fccid=b87e01ade6c824ee&vjs=3>

Который снова перенаправляется с перенаправлением 302 на следующую ссылку:

2019-09-30 10:37:07 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://www.indeed.com/viewjob?jk=69995bf12d9f2f9a&from=serp&vjs=3> (referer: None)

И, наконец, мы можем очистить данные:

2019-09-30 10:37:07 [scrapy.core.scraper] DEBUG: Scraped from <200 https://www.indeed.com/viewjob?jk=69995bf12d9f2f9a&from=serp&vjs=3> 
{'title': 'General Manager', 'posting_url': 'https://indeed.com/rc/clk?jk=69995bf12d9f2f9a&fccid=b87e01ade6c824ee&vjs=3', 'job_name': 'General Manager', 'job_location': 'Plano, TX 75024', 'job_description_1': [], 'posted_on_date': ' - 30+ days ago', 'job_description_2': None}

Таким образом, данные извлекаются изпоследняя страница, получившая ответ 200. Обратите внимание, что в извлеченном элементе posting_url - это страница, переданная с атрибутом meta, а не фактический очищенный URL. Это может быть то, что вы хотите, но если вы хотите сохранить фактический URL, который был очищен, то вы должны использовать posting_url = response.url вместо этого. РЕДАКТИРОВАТЬ: см. Предлагаемое обновление ниже

Рекомендуемое обновление кода:

import scrapy


class JobsSpider(scrapy.Spider):
    name = "jobs"
    allowed_domains = ["indeed.com"]
    start_urls = ["https://www.indeed.com/jobs?q=%22owner+operator%22&l=dallas"]

    def parse(self, response):
        jobs = response.xpath('//div[@class="title"]')

        for job in jobs:
            title = job.xpath('a//@title').extract_first()
            posting_link = job.xpath('a//@href').extract_first()
            referer_url = "https://indeed.com" + posting_link

            yield scrapy.Request(url=referer_url,
                                 callback=self.parse_page,
                                 meta={'title': title,
                                       'referer_url': referer_url,
                                       }
                                 )

        relative_next_url = response.xpath('//link[@rel="next"]/@href').extract_first()
        if relative_next_url:
            absolute_next_url = "https://indeed.com" + relative_next_url
            yield scrapy.Request(absolute_next_url, callback=self.parse)
        else:
            self.logger.info('No more pages found.')


    def parse_page(self, response):
        referer_url = response.meta.get('referer_url')
        title = response.meta.get('title')
        job_location = response.meta.get('job_location')
        posting_url = response.url

        job_name= response.xpath('//*[@class="icl-u-xs-mb--xs icl-u-xs-mt--none  jobsearch-JobInfoHeader-title"]/text()').extract_first()
        job_description_1=response.xpath('//div[@class="jobsearch-jobDescriptionText"]/ul').extract()
        posted_on_date= response.xpath('//div[@class="jobsearch-JobMetadataFooter"]/text()').extract_first()
        job_location=response.xpath('//*[@class="jobsearch-InlineCompanyRating icl-u-xs-mt--xs  jobsearch-DesktopStickyContainer-companyrating"]/div[3]/text()').extract_first()
        job_description_2=response.xpath('//div[@class="jobsearch-JobComponent-description  icl-u-xs-mt--md  "]/text()').extract_first()

        yield {'title': title,
               'posting_url': posting_url,
               'referer_url': referer_url,
               'job_name': job_name,
               'job_location': job_location,
               'job_description_1': job_description_1,
               'posted_on_date': posted_on_date,
               'job_description_2': job_description_2,
               'job_location': job_location
               }
...