Сайт Scrapy и ASPX — не удается извлечь страницы за пределы 11 — 302 ошибок

#python #scrapy

#python #scrapy

Вопрос:

Я пытаюсь извлечь данные с этого сайта: https://inform.alabama.gov/employeesearch.aspx . Текущий запрос, который я привел ниже, работает до страницы 11 включительно. Я думаю, что проблема связана с элементом формы «__VIEWSTATE». Похоже, что он не меняется с каждым запросом. Он должен представлять текущую страницу ответа в цикле, чтобы сервер знал, как интерпретировать последующий ответ. Кажется, что он отображает только значение, присутствующее в первом ответе, поэтому я считаю, что сервер отклоняет его, потому что страница 12 не является допустимым путем со страниц 1-10. Если вы посмотрите на нумерацию страниц, она переходит от 1 к …, где … отображает страницу 11. Когда страница 11 отображается, она изменяет нумерацию страниц на: 11 на …, где … отображает страницу 21.

Обратите внимание, что num_pages определяет общее количество страниц. В настоящее время установлено значение 15, он обрабатывает страницы 1-11 и возвращает 302 ошибки для других страниц.

Как это следует изменить, чтобы получить результаты для всех 661 страниц?

 from scrapy import FormRequest, Spider
from scrapy.spidermiddlewares.httperror import HttpError
from twisted.internet.error import DNSLookupError
from twisted.internet.error import TimeoutError, TCPTimedOutError


class EmployeesSpider(Spider):
    name = 'employees'
    start_urls = ['https://inform.alabama.gov/employeesearch.aspx']
    num_pages = 15  # 661
    name_excludes = ['', ' ', '1']

    def parse(self, response):
        formdata = self.get_formdata(response, 0)
        formdata['__EVENTTARGET'] = 'ctl00$ContentPlaceHolder1$btn_Search'
        formdata['__EVENTARGUMENT'] = ''
        yield FormRequest(
            url='https://inform.alabama.gov/employeesearch.aspx',
            method="POST",
            dont_filter=True,
            formdata=formdata,
            callback=self.perform_search,
            errback=self.failure)

    def perform_search(self, response):
        for employee in response.xpath('//*[@id="ContentPlaceHolder1_GridView1"]//tr'):
            emp_name = employee.xpath('.//td[1]//text()').get()
            if emp_name is not None and emp_name not in self.name_excludes:
                final_name = emp_name.strip()
                yield {
                    'name': final_name,
                    'email': employee.xpath('.//td[1]//span//a//text()').get(),
                    'org': employee.xpath('.//td[2]//text()').get(),
                    'phone': employee.xpath('.//td[3]//span//a//text()').get(),
                }

        # Download search pages starting from #2
        for i in range(2, self.num_pages):
            formdata = self.get_formdata(response, i)
            yield FormRequest(
                url='https://inform.alabama.gov/employeesearch.aspx',
                method="POST",
                dont_filter=True,
                formdata=formdata,
                callback=self.parse_results,
                errback=self.failure)

    def get_formdata(self, response, page_num):
        eventargument = 'Page


Ответ №1:

Во-первых, вам нужно получить общее количество страниц. Нажмите Поиск, перейдите на последнюю страницу и сохраните 661 num_pages .

Начните с самого начала, теперь зная, сколько страниц у вас есть:

 from scrapy import FormRequest, Spider


class EmployeesSpider(Spider):
    name = 'employees'
    start_urls = ['https://inform.alabama.gov/employeesearch.aspx']
    num_pages = None
    name_excludes = ['', ' ', '1', None]

    def parse(self, response):
        yield FormRequest.from_response(
            response,
            dont_filter=True,
            formdata={
                '__EVENTTARGET': 'ctl00$ContentPlaceHolder1$btn_Search',
                '__EVENTARGUMENT': ''
            },
            callback=self.perform_search
        )

    def perform_search(self, response):
        if not self.num_pages:
            # first get the total number of pages
            yield FormRequest.from_response(
                response,
                dont_filter=True,
                formdata={
                    '__EVENTTARGET': 'ctl00$ContentPlaceHolder1$GridView1',
                    '__EVENTARGUMENT': 'Page$Last'
                },
                callback=self.parse_total_pages
            )
        else:
            for employee in response.xpath(
                '//table[@id="ContentPlaceHolder1_GridView1"]//'
                'tr[position() < last()]'
            ):
                emp_name = employee.xpath('.//td[1]//text()').get()
                if emp_name not in self.name_excludes:
                    final_name = emp_name.strip()
                    yield {
                        'name': final_name,
                        'email': employee.xpath('.//td[1]//span//a//text()').get(),
                        'org': employee.xpath('.//td[2]//text()').get(),
                        'phone': employee.xpath('.//td[3]//span//a//text()').get(),
                    }

            # go to the next page
            current_page = response.xpath(
                '//tr[@class="employeeSearchPagerStye"]/td//span/text()'
            ).get()
            current_page = int(current_page)
            if current_page < self.num_pages:
                yield FormRequest.from_response(
                    response,
                    dont_filter=True,
                    formdata={
                        '__EVENTTARGET': 'ctl00$ContentPlaceHolder1$GridView1',
                        '__EVENTARGUMENT': f'Page${current_page   1}'
                    },
                    callback=self.perform_search
                )

    def parse_total_pages(self, response):
        total_pages = response.xpath(
            '//tr[@class="employeeSearchPagerStye"]/td//span/text()'
        ).get()
        self.num_pages = int(total_pages)

        # back to search
        yield FormRequest.from_response(
            response,
            dont_filter=True,
            formdata={
                '__EVENTTARGET': 'ctl00$ContentPlaceHolder1$btn_Search',
                '__EVENTARGUMENT': ''
            },
            callback=self.perform_search
        )

 

str(page_num)

viewstate = response.css(
'input#__VIEWSTATE::attr(value)').get()
if viewstate is None:
viewstate = ''

viewstategen = response.css(
'input#__VIEWSTATEGENERATOR::attr(value)').get()
if viewstategen is None:
viewstategen = ''

eventvalidation = response.css(
'input#__EVENTVALIDATION::attr(value)').get()
if eventvalidation is None:
eventvalidation = ''

formdata = {
'__EVENTTARGET': 'ctl00$ContentPlaceHolder1$GridView1',
'__EVENTARGUMENT': eventargument,
'__VIEWSTATE': viewstate,
'__VIEWSTATEGENERATOR': viewstategen,
'__EVENTVALIDATION': eventvalidation,
'ctl00$ContentPlaceHolder1$txt_FirstName': '',
'ctl00$ContentPlaceHolder1$txt_LastName': '',
'ctl00$ContentPlaceHolder1$ddl_Agency': 'Not Selected',
'ctl00$ContentPlaceHolder1$txt_Phone': '',
}
return formdata

def parse_results(self, response):
for employee in response.xpath('//*[@id="ContentPlaceHolder1_GridView1"]//tr'):
emp_name = employee.xpath('.//td[1]//text()').get()
if emp_name is not None and emp_name not in self.name_excludes:
final_name = emp_name.strip()
yield {
'name': final_name,
'email': employee.xpath('.//td[1]//span//a//text()').get(),
'org': employee.xpath('.//td[2]//text()').get(),
'phone': employee.xpath('.//td[3]//span//a//text()').get(),
}

def failure(self, failure):
# log all failures
self.logger.error(repr(failure))

# in case you want to do something special for some errors,
# you may need the failure's type:

if failure.check(HttpError):
# these exceptions come from HttpError spider middleware
# you can get the non-200 response
response = failure.value.response
self.logger.error('HttpError on %s', response.url)

elif failure.check(DNSLookupError):
# this is the original request
request = failure.request
self.logger.error('DNSLookupError on %s', request.url)

elif failure.check(TimeoutError, TCPTimedOutError):
request = failure.request
self.logger.error('TimeoutError on %s', request.url)

Ответ №1:

Во-первых, вам нужно получить общее количество страниц. Нажмите Поиск, перейдите на последнюю страницу и сохраните 661 num_pages .

Начните с самого начала, теперь зная, сколько страниц у вас есть: