Это мой код:
import scrapy
from scrapy import Spider
from scrapy.http import FormRequest
class ProvinciaSpider(Spider):
name = 'provincia'
allowed_domains = ['aduanet.gob.pe']
start_urls = ['http://www.aduanet.gob.pe/cl-ad-itconsmanifiesto/manifiestoITS01Alias?accion=cargaConsultaManifiesto&tipoConsulta=salidaProvincia']
def parse(self, response):
data = { 'accion': 'consultaManifExpProvincia',
'salidaPro': 'YES',
'strMenu': '-',
'strEmpTransTerrestre': '-',
'CMc1_Anno': '2022',
'CMc1_Numero': '96',
'CG_cadu': '046',
'viat': '1'}
yield FormRequest('http://www.aduanet.gob.pe/cl-ad-itconsmanifiesto/manifiestoITS01Alias', formdata=data, callback=self.parse_form_page)
def parse_form_page(self, response):
table = response.xpath('/html/body/form[1]//td[@class = "beta"]/table')
trs = table.xpath('.//tr')[1:]
for tr in trs:
puerto_llegada= tr.xpath('.//td[1]/text()').extract_first().strip()
pais= tr.xpath('.//td[1]/text()').extract_first().strip()
bl= tr.xpath('.//td[3]/text()').extract_first().strip()
peso= tr.xpath('.//td[8]/text()').extract_first().strip()
bultos= tr.xpath('.//td[9]/text()').extract_first().strip()
consignatario= tr.xpath('.//td[12]/text()').extract_first().strip()
embarcador= tr.xpath('.//td[13]/text()').extract_first().strip()
links=tr.xpath('.//td[4]/a/@href')
yield response.follow(links.get(),
callback=self.parse_categories,
meta = {'puerto_llegada': puerto_llegada,
'pais': pais,
'bl': bl,
'peso': float("".join(peso.split(','))),
'bultos': float("".join(bultos.split(','))),
'consignatario': consignatario,
'embarcador': embarcador})
def parse_categories(self, response):
puerto_llegada = response.meta['puerto_llegada']
pais = response.meta['pais']
bl = response.meta['bl']
peso = response.meta['peso']
bultos = response.meta['bultos']
consignatario = response.meta['consignatario']
embarcador = response.meta['embarcador']
tabla_des= response.xpath('/html/body/form//td[@class = "beta"]/table')
trs3= tabla_des.xpath('.//tr')[1:]
for tr3 in trs3:
descripcion= tr.xpath('.//td[7]/text()').extract_first().strip()
yield {'puerto_llegada': puerto_llegada,
'pais': pais,
'bl': bl,
'peso': PROCESOS,
'bultos': bultos,
'consignatario': consignatario,
'embarcador': embarcador,
'descripcion': descripcion}
И я получаю эту ошибку:
ValueError: Отсутствует схема в URL запроса: javascript:jsDetalle2('154');
Каждая ссылка, из которой я хочу извлечь данные, имеет этот формат, поэтому мой код для извлечения данных внутри каждой ссылки не работает.
Формат ссылки похож на javascript:jsDetalle2('154'), только цифры меняются.
Проблема в том, что это не http//........ или /manifyto...... в первом случае нужно просто перейти по ссылке и все, во втором случае нужно соедините вторую часть URL-адреса с URL-адресом первого ответа. Но этот случай никакой, поэтому я не знаю, как заставить его работать.
Как я могу написать это, чтобы работать?
сначала вам нужно будет проверить DevTools
в браузере, что браузер делает, когда вы нажимаете ссылку, подобную этой. Возможно, он только показывает/скрывает элемент на странице. Или, может быть, он загружает данные с URL-адреса, который вы могли бы использовать вместо jsDetalle2()
(и замените только 154
в этом URL-адресе). Или, может быть, вам нужно будет использовать Селен для управления реальным веб-браузером, который может запускать JavaScript.
Я проверил в браузере, и когда я нажимаю на эту ссылку, он отправляет POST на http://www.aduanet.gob.pe/cl-ad-itconsmanifiesto/manifiestoITS01Alias
со многими значениями, и одно из них — CMc2_NumDet: "154"
, так что вам придется сделать то же самое.
Числа случайные, я не буду знать, какие номера будут у страницы (внутри каждой ссылки javascript). Должен ли я использовать всплеск или я могу решить это только с помощью scrapy?
числа НЕ случайны. Ссылка javascript:jsDetalle2('154')
работает POST
со значением 154
- и вы можете получить это значение из javascript:jsDetalle2('154')
или из text()
в <a>
Я имел в виду, что первая ссылка из таблицы 154, а затем может быть 1 или 500. Вот что я имел в виду. Это будет работать с text() в a. Как я должен написать это в коде?
Должен ли я использовать всплеск или я могу решить это только с помощью scrapy? И я не хочу использовать селен. У меня есть точно такая же программа с селеном и красивым супом, но у нее есть проблемы, я искал и обнаружил, что это были ошибки. Итак, я начал изучать скрап, но в этом случае я должен использовать всплеск, верно? или не надо?
вам не нужен Splash, если вы знаете, как он отправляет POST — если вы знаете, какое значение он отправляет, когда вы проверяете его в DevTools. Легко понять, что CMc2_NumDet
нужно значение из ссылки, CMc2_numcon
нужно значение из вашей переменной bl
и т. д.
кажется, есть большая проблема - страница с категориями, вероятно, нуждается в файлах cookie - в этот момент я получаю страницу с результатами поиска, когда пытаюсь получить подробности.
Я проверил эту ссылку в браузере — и когда я нажимаю на ссылку с текстом 154
, она запускается POST
со многими значениями, и одно из них — 'CMc2_NumDet': '154'
— так что я могу получить этот номер по ссылке и использовать в POST
.
В браузере вы можете видеть 'CMc2_Numero': "+++96"
, но в коде вам нужно space
вместо +
, как " 96"
(а в scrapy будет использоваться +
вместо space
), или вы можете удалить все +
, например "96"
.
Кстати: я вставил meta
все значения как item: {...}
, чтобы позже я мог получить все значения, используя одну строку с meta['item']
number = tr.xpath('.//td[4]/a/text()').get()
data = {
'accion': "consultaManifExpProvinciaDetalle",
'CMc2_Anno': "2022",
'CMc2_Numero': "96", # <--- without `+`
'CG_cadu': "046",
'CMc2_viatra': "1",
'CMc2_numcon': "",
'CMc2_NumDet': number, # <---
'tipo_archivo': "",
'reporte': "ExpPro",
'backPage': "ConsulManifExpPro",
}
yield FormRequest('http://www.aduanet.gob.pe/cl-ad-itconsmanifiesto/manifiestoITS01Alias',
formdata=data,
callback=self.parse_categories,
meta = {"item": {'puerto_llegada': puerto_llegada,
'pais': pais,
'bl': bl,
'peso': float("".join(peso.split(','))),
'bultos': float("".join(bultos.split(','))),
'consignatario': consignatario,
'embarcador': embarcador}})
def parse_categories(self, response):
print('[parse_form_page] url:', response.url)
item = response.meta['item']
tabla_des = response.xpath('/html/body/form//td[@class = "beta"]/table')
trs3 = tabla_des.xpath('.//tr')[1:]
for tr3 in trs3: # trs3[:1]: for single result
item['descripcion'] = tr3.xpath('.//td[7]/text()').extract_first().strip()
yield item
Полный рабочий код.
Страница с категориями может иметь много строк в таблице (с разными Peso Bruto
, которые вы не используете), поэтому она может дать много строк в CSV.
Если вам нужна только одна строка, используйте trs3[:1]:
вместо trs3:
Я использовал другой xpath для поиска таблицы с "Descripcion"
, потому что предыдущая версия не проверяла, есть ли в таблице Descripcion
, и могла получить 3 таблицы вместо одной.
import scrapy
from scrapy import Spider
from scrapy.http import FormRequest
class ProvinciaSpider(Spider):
name = 'provincia'
allowed_domains = ['aduanet.gob.pe']
start_urls = ['http://www.aduanet.gob.pe/cl-ad-itconsmanifiesto/manifiestoITS01Alias?accion=cargaConsultaManifiesto&tipoConsulta=salidaProvincia']
def parse(self, response):
payload = {
'accion': 'consultaManifExpProvincia',
'salidaPro': 'YES',
'strMenu': '-',
'strEmpTransTerrestre': '-',
'CMc1_Anno': '2022',
'CMc1_Numero': '96',
'CG_cadu': '046',
'viat': '1'
}
yield FormRequest('http://www.aduanet.gob.pe/cl-ad-itconsmanifiesto/manifiestoITS01Alias',
formdata=payload,
callback=self.parse_form_page)
def parse_form_page(self, response):
print('[parse_form_page] url:', response.url)
table = response.xpath('/html/body/form[1]//td[@class = "beta"]/table')
trs = table.xpath('.//tr')[1:]
for tr in trs:
item = {
'puerto_llegada': tr.xpath('.//td[1]/text()').extract_first().strip(),
'pais': tr.xpath('.//td[1]/text()').extract_first().strip(),
'bl': tr.xpath('.//td[3]/text()').extract_first().strip(),
'peso': tr.xpath('.//td[8]/text()').extract_first().strip().replace(',', ''), # <---
'bultos': tr.xpath('.//td[9]/text()').extract_first().strip().replace(',', ''), # <---
'consignatario': tr.xpath('.//td[12]/text()').extract_first().strip(),
'embarcador': tr.xpath('.//td[13]/text()').extract_first().strip(),
}
number = tr.xpath('.//td[4]/a/text()').get().strip()
print(number.strip())
payload = {
'accion': "consultaManifExpProvinciaDetalle",
'CMc2_Anno': "2022",
'CMc2_Numero': "96", # without `+` or use `space` instead of `+`
'CG_cadu': "046",
'CMc2_viatra': "1",
'CMc2_numcon': "",
'CMc2_NumDet': number, # <---
'tipo_archivo': "",
'reporte': "ExpPro",
'backPage': "ConsulManifExpPro",
}
yield FormRequest('http://www.aduanet.gob.pe/cl-ad-itconsmanifiesto/manifiestoITS01Alias',
formdata=payload,
callback=self.parse_categories,
meta = {"item": item})
def parse_categories(self, response):
print('[parse_form_page] url:', response.url)
item = response.meta['item']
table = response.xpath('//table[./tr/th[contains(text(), "Descripcion")]]')
print('len(table):', len(table))
trs = table.xpath('.//tr')[1:]
print('len(trs):', len(trs))
for tr in trs: # trs[:1]: for single result
item['descripcion'] = tr.xpath('.//td[7]/text()').extract_first().strip()
yield item
# --- run without project and save in `output.csv` ---
from scrapy.crawler import CrawlerProcess
c = CrawlerProcess({
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:98.0) Gecko/20100101 Firefox/98.0',
'FEEDS': {'output.csv': {'format': 'csv'}}, # new in 2.1
})
c.crawl(ProvinciaSpider)
c.start()
Результат (с trs[:1]
)
puerto_llegada,pais,bl,peso,bultos,consignatario,embarcador,descripcion
BEANR,BEANR,MAEU216473186,47420.00,2160,AGROFAIR BENELUX BV,COOPERATIVA AGRARIA APPBOSA,YT GREEN ORGANIC FRESH BANANAS CARTON BOXES AND IN POLYETHYLENE BAGS.
NLRTM,NLRTM,MAEU216473104,83890.00,5280,AGROFAIR BENELUX BV.,TULIPAN NARANJA S.A.C.,FYT GREEN ORGANIC FRESH BANANAS CARTON BOXES AND IN POLYETHYLENE BAGS.
BEANR,BEANR,MAEU216307459,19980.00,285,"Greencof B.V.,",COOPERATIVA AGRARIA RODRIGUEZ DE MENDOZA,285 BAGS OF 69 KG NET OF PERU ORGANIC GREEN COFFEE FAIRTRADE CERTIFIED
JPYOK,JPYOK,MAEU1KT407500,21320.00,709,"HOWA SHOJI CO., LTD",GEALE AGROTRADING E.I.R.L.,GREEN ORGANIC FRESH BANANAS CARTON BOXES AND IN POLYETHYLENE BAGS. BAN
ITCVV,ITCVV,MAEU913779677,66950.00,3240,BATTAGLIO SPA,IREN PERU SOCIEDAD ANONIMA CERRADA - IREN PERU S.A,GREEN ORGANIC FRESH BANANAS CARTON BOXES AND IN POLYETHYLENE BAGS. BAN
NLRTM,NLRTM,MAEU913798070,24700.00,5544,FRUTOS TROPICALES EUROPE B.V.,FRUTOS TROPICALES PERU EXPORT SOCIEDAD ANONIMA CER,"FRESH MANGOES NET WEIGHT: 22,176.00 KG P.A.: 0804.50.20.00 TR.: JKXYA0"
BEANR,BEANR,MAEU216473141,23710.00,1080,AGROFAIR BENELUX BV.,TULIPAN NARANJA S.A.C.,FYT GREEN ORGANIC FRESH BANANAS CARTON BOXES AND IN POLYETHYLENE BAGS.
BEANR,BEANR,MAEU216632137,22270.00,1080,FYFFES INTERNATIONAL,AGRO PACHA S.A.,"GREEN FRESH ORGANIC BANANAS, PACKED IN CARTON BOXES AND POLYETHILENE B"
KRPUS,KRPUS,MAEU913722041,24480.00,1175,TO THE ORDER,PERUPEZ S.A.C.,"NET WEIGHT: 23,500 KG GROSS WEIGHT: 24,480 KG 1,175 SACKS 23,500 KG FR"
NLRTM,NLRTM,MAEU216473211,22520.00,1080,AgroFair Benelux BV,COOPERATIVA AGRARIA DE USUARIOS RIO Y VALLE,ORGANIC FAIRTRADE BANANAS GREEN FRESH CAVENDISH PACKED CARDBOARD BOXES
Ничего, если в первой части я поставлю условные операторы внутри элемента? Для обработки информации. И еще вопрос, я также хочу извлечь второй столбец в следующей таблице (таблица после таблицы «описание». Но я хочу, чтобы она была в одной строке, поэтому мой вопрос: можно ли использовать команду yield для списка?
Также вы можете увидеть часть EDIT моего вопроса?
Я полагаю, я могу сделать это так же, как вы делали это раньше, верно? Запись всех элементов в мета часть
если у вас есть новая проблема, создайте новый вопрос на новой странице.
что касается yield
: он может использовать разные объекты - dict, dataclass, специальный класс Item
в scrapy
, но все они имеют key:value
, и я не знаю, работает ли он со списком - но самый простой ответ: используйте yield со списком и посмотрите, что случится.
просто используйте его и посмотрите, что произойдет. это стандартный метод обучения.
что касается сравнения - я храню его как словарь, так что теперь это так item["puerto_llegada"] == ...
И если у вас есть много элементов для сравнения, вы также можете сохранить словарь rules = {"RULED" :"ST PETERSBURG", ... }
и использовать for
-цикл, чтобы сделать код короче: for key,val in rules.items(): if key in item["puerto_llegada"]: item["puerto_llegada"] = val
Да да я решил это. Но часть «описание» извлекается плохо. Происходит так же, как моя программа selenium+ Beautifulsoup. Он извлекает правильно до определенного момента, а затем начинает извлекать случайным образом. 452 должно соответствовать таблице.
с этой проблемой я могу только предложить print()
все значения и результат (или сохранить в logging
) и позже сравнить с результатами в браузере, когда вы посещаете страницы вручную - возможно, есть какой-то другой элемент, который был пропущен в коде. ИЛИ, возможно, проблема не в коде, а в сервере - возможно, он дает неверные результаты, если у него слишком много запросов за короткое время.
Я уже сравнивал их с print(), думаю это сервер. Но как я могу это решить?
если проблема с сервером, то вы не можете ее решить. Надо было написать админам и попросить исправить. И если проблема в том, что сервер не может ответить на такое количество запросов, просто замедлите скорость, используйте time.sleep()
между запросами.
Я пробовал пользовательские агенты, задержку загрузки, но ничего. Вроде как если извлекать с 3-й страницы(внутри ссылок). Он начинает извлекать все нормально, но в какой-то момент каждый элемент начинает работать со сбоями и извлекается случайным образом. Поэтому я помещаю все с 3-й страницы в качестве комментария и запускаю ее, и она очень хорошо все извлекает. Что вы думаете?
Я понятия не имею, в чем проблема. Ему нужно будет видеть и отслеживать запросы между браузером и сервером и сравнивать с запросами между кодом и сервером - возможно, ему нужно будет использовать локальный прокси-сервер, такой как Чарльз, чтобы увидеть все запросы. И на все это нужно время.
Ну, чувак, я думаю, на этом все. Ты действительно много знаешь. Спасибо за все на самом деле. Не знаю, есть ли у вас Facebook или что-то, чтобы оставаться на связи? Если ты не хочешь, я понимаю. В любом случае удачи и спасибо.
это не обычная ссылка, а код JavaScript - и обычно браузер просто выполняет этот код, когда вы нажимаете на эту ссылку. Но Scrapy не может запускать JavaScript. И добавлять
http://
бесполезно. Требуется работающий браузер с оригинальной страницей, которая имеет загруженную функциюjsDetalle2()