#python #scrapy
#python #шершавый
Вопрос:
Я пытаюсь извлечь данные с этого сайта: https://inform.alabama.gov/employeesearch.aspx . Когда я запускаю этот код в настоящее время, он просто повторно извлекает данные из «Страницы $ 1». На самом деле цикл, похоже, не выполняет итерацию запроса. Это мой первый скрипт как на Python, так и на Scrapy, поэтому я уверен, что здесь мне не хватает чего-то фундаментального…
Каков правильный способ получить ответ от одного запроса и передать данные, извлеченные из него, следующему?
На этом сайте также нет кнопки «Далее», поэтому я использовал переменную num_pages для установки максимального значения. Открыт для предложений о том, как сделать это динамичным.
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 = 5 # 661
def parse(self, response):
for i in range(1, self.num_pages):
yield FormRequest(
url='https://inform.alabama.gov/employeesearch.aspx',
method="POST",
dont_filter=True,
headers={
"authority": "inform.alabama.gov",
"cache-control": "max-age=0",
"upgrade-insecure-requests": "1",
"origin": "https://inform.alabama.gov",
"content-type": "application/x-www-form-urlencoded",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36",
"accept": "text/html,application/xhtml xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"sec-fetch-site": "same-origin",
"sec-fetch-mode": "navigate",
"sec-fetch-user": "?1",
"sec-fetch-dest": "document",
"referer": "https://inform.alabama.gov/employeesearch.aspx",
"accept-language": "en-US,en;q=0.9"
},
formdata=self.get_formdata(response, i),
callback=self.parse_results,
errback=self.failure)
def get_formdata(self, response, page_num):
eventargument = 'Page
Ответ №1:
Вам необходимо сначала отправить запрос формы поиска ( __EVENTTARGET=ctl00$ContentPlaceHolder1$btn_Search
), а затем выполнить итерацию страниц результатов ( __EVENTTARGET=ctl00$ContentPlaceHolder1$GridView1
).:
def parse(self, response):
# for i in range(1, 2):
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'):
yield {
'name': employee.xpath('./td[1]/text()').get(),
'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,
cb_kwargs={
'page': i,
},
errback=self.failure)
def get_formdata(self, response, page_num):
eventargument = 'Page
Комментарии:
1. Я подключил эти функции к своему классу spider и запустил его с помощью 'scrapy crawl employees -O employees.csv', но на самом деле это не дало никаких результатов: csv заполняется каждой строкой как ',,,'
2. Что-то не так с вашим предположением. У меня 583 элемента на 12 страницах ( dropbox.com/s/xii191yheft2ao4/employees.csv?dl=0 )
3. Я смог получить данные для возврата, указав все пути в yield use '//' : employee.xpath('.//td[1]//text()').get()
4. но затем он пропустил страницы 2 и 3. Могу ли я просмотреть ваш полный файл, чтобы протестировать его непосредственно на моем конце?
5. Что вы подразумеваете под "полным файлом"?
Ответ №2:
С помощью cb_kwargs или старой meta вы можете передавать информацию из одной функции в следующую https://docs.scrapy.org/en/latest/topics/request-response.html
def start_requests(self):
for url in self.starting_urls:
yield Request(
url,
cb_kwargs={'additional_arguments': dict_map[url]}
)
def parse(self, response, additional_arguments):
# Here you can use that additional_argument
pass
Комментарии:
1. Я не понимаю, как применить это к моему варианту использования. Похоже, что эта функциональность передает статическое содержимое или содержимое запроса в функцию синтаксического анализа. Мне нужно извлекать данные из HTML-ответа страницы после каждого запроса, чтобы сгенерировать данные формы для следующего запроса.
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'):
yield {
'name': employee.xpath('./td[1]/text()').get(),
'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:
Вам необходимо сначала отправить запрос формы поиска ( __EVENTTARGET=ctl00$ContentPlaceHolder1$btn_Search
), а затем выполнить итерацию страниц результатов ( __EVENTTARGET=ctl00$ContentPlaceHolder1$GridView1
).:
Комментарии:
1. Я подключил эти функции к своему классу spider и запустил его с помощью 'scrapy crawl employees -O employees.csv', но на самом деле это не дало никаких результатов: csv заполняется каждой строкой как ',,,'
2. Что-то не так с вашим предположением. У меня 583 элемента на 12 страницах ( dropbox.com/s/xii191yheft2ao4/employees.csv?dl=0 )
3. Я смог получить данные для возврата, указав все пути в yield use '//' : employee.xpath('.//td[1]//text()').get()
4. но затем он пропустил страницы 2 и 3. Могу ли я просмотреть ваш полный файл, чтобы протестировать его непосредственно на моем конце?
5. Что вы подразумеваете под "полным файлом"?
Ответ №2:
С помощью cb_kwargs или старой meta вы можете передавать информацию из одной функции в следующую https://docs.scrapy.org/en/latest/topics/request-response.html
Комментарии:
1. Я не понимаю, как применить это к моему варианту использования. Похоже, что эта функциональность передает статическое содержимое или содержимое запроса в функцию синтаксического анализа. Мне нужно извлекать данные из HTML-ответа страницы после каждого запроса, чтобы сгенерировать данные формы для следующего запроса.
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, page):
# with open(f'Samples/Page_{page}.htm', 'wb') as f:
# f.write(response.body)
for employee in response.xpath('//*[@id="ContentPlaceHolder1_GridView1"]//tr'):
yield {
'name': employee.xpath('./td[1]/text()').get(),
'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(),
}
Комментарии:
1. Я подключил эти функции к своему классу spider и запустил его с помощью ‘scrapy crawl employees -O employees.csv’, но на самом деле это не дало никаких результатов: csv заполняется каждой строкой как ‘,,,’
2. Что-то не так с вашим предположением. У меня 583 элемента на 12 страницах ( dropbox.com/s/xii191yheft2ao4/employees.csv?dl=0 )
3. Я смог получить данные для возврата, указав все пути в yield use ‘//’ : employee.xpath(‘.//td[1]//text()’).get()
4. но затем он пропустил страницы 2 и 3. Могу ли я просмотреть ваш полный файл, чтобы протестировать его непосредственно на моем конце?
5. Что вы подразумеваете под «полным файлом»?
Ответ №2:
С помощью cb_kwargs или старой meta вы можете передавать информацию из одной функции в следующую https://docs.scrapy.org/en/latest/topics/request-response.html
Комментарии:
1. Я не понимаю, как применить это к моему варианту использования. Похоже, что эта функциональность передает статическое содержимое или содержимое запроса в функцию синтаксического анализа. Мне нужно извлекать данные из HTML-ответа страницы после каждого запроса, чтобы сгенерировать данные формы для следующего запроса.
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’):
yield {
‘name’: employee.xpath(‘./td[1]/text()’).get(),
’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:
Вам необходимо сначала отправить запрос формы поиска ( __EVENTTARGET=ctl00$ContentPlaceHolder1$btn_Search
), а затем выполнить итерацию страниц результатов ( __EVENTTARGET=ctl00$ContentPlaceHolder1$GridView1
).:
Комментарии:
1. Я подключил эти функции к своему классу spider и запустил его с помощью ‘scrapy crawl employees -O employees.csv’, но на самом деле это не дало никаких результатов: csv заполняется каждой строкой как ‘,,,’
2. Что-то не так с вашим предположением. У меня 583 элемента на 12 страницах ( dropbox.com/s/xii191yheft2ao4/employees.csv?dl=0 )
3. Я смог получить данные для возврата, указав все пути в yield use ‘//’ : employee.xpath(‘.//td[1]//text()’).get()
4. но затем он пропустил страницы 2 и 3. Могу ли я просмотреть ваш полный файл, чтобы протестировать его непосредственно на моем конце?
5. Что вы подразумеваете под «полным файлом»?
Ответ №2:
С помощью cb_kwargs или старой meta вы можете передавать информацию из одной функции в следующую https://docs.scrapy.org/en/latest/topics/request-response.html
Комментарии:
1. Я не понимаю, как применить это к моему варианту использования. Похоже, что эта функциональность передает статическое содержимое или содержимое запроса в функцию синтаксического анализа. Мне нужно извлекать данные из HTML-ответа страницы после каждого запроса, чтобы сгенерировать данные формы для следующего запроса.