nested - span - select scrapy
Solicitud anidada mĂșltiple con scrapy (1)
El problema es que bifurca su artículo, donde de acuerdo con su lógica, solo desea 1 artículo por país, por lo que no puede entregar múltiples artículos en ningún momento después de analizar el país. Lo que quiere hacer es apilarlos todos en un solo elemento.
Para hacer eso, necesitas crear un ciclo de análisis:
def parse_airports(self, response):
item = response.meta[''my_country_item'']
item[''airports''] = []
for airport in response.xpath(''//a[@data-iata]''):
url = airport.xpath(''./@href'').extract()
iata = airport.xpath(''./@data-iata'').extract()
iatabis = airport.xpath(''./small/text()'').extract()
name = ''''.join(airport.xpath(''./text()'').extract()).strip()
lat = airport.xpath("./@data-lat").extract()
lon = airport.xpath("./@data-lon").extract()
iAirport = dict()
iAirport[''name''] = ''foobar''
iAirport[''link''] = url[0]
iAirport[''lat''] = lat[0]
iAirport[''lon''] = lon[0]
iAirport[''code_little''] = iata[0]
iAirport[''code_total''] = iatabis[0]
item[''airports''].append(iAirport)
urls = []
for airport in item[''airports'']:
json_url = ''https://api.flightradar24.com/common/v1/airport.json?code={code}&plugin/[/]=&plugin-setting/[schedule/]/[mode/]=&plugin-setting/[schedule/]/[timestamp/]={timestamp}&page=1&limit=50&token=''.format(
code=airport[''code_little''], timestamp="1484150483")
urls.append(json_url)
if not urls:
return item
# start with first url
next_url = urls.pop()
return Request(next_url, self.parse_schedule,
meta={''airport_item'': item, ''airport_urls'': urls, ''i'': 0})
def parse_schedule(self, response):
"""we want to loop this continuously for every schedule item"""
item = response.meta[''airport_item'']
i = response.meta[''i'']
urls = response.meta[''airport_urls'']
jsonload = json.loads(response.body_as_unicode())
item[''airports''][i][''schedule''] = ''foobar''
# now do next schedule items
if not urls:
yield item
return
url = urls.pop()
yield Request(url, self.parse_schedule,
meta={''airport_item'': item, ''airport_urls'': urls, ''i'': i + 1})
Intento eliminar información de horarios de aviones en el sitio web www.flightradar24.com para un proyecto de investigación.
La jerarquía del archivo json que quiero obtener es algo así:
Object ID
- country
- link
- name
- airports
- airport0
- code_total
- link
- lat
- lon
- name
- schedule
- ...
- ...
- airport1
- code_total
- link
- lat
- lon
- name
- schedule
- ...
- ...
Country
y el Airport
se almacenan utilizando elementos, y como puede ver en el archivo json, CountryItem
(enlace, atributo de nombre) finalmente almacena varios elementos del aeropuerto (code_total, link, lat, lon, name, schedule):
class CountryItem(scrapy.Item):
name = scrapy.Field()
link = scrapy.Field()
airports = scrapy.Field()
other_url= scrapy.Field()
last_updated = scrapy.Field(serializer=str)
class AirportItem(scrapy.Item):
name = scrapy.Field()
code_little = scrapy.Field()
code_total = scrapy.Field()
lat = scrapy.Field()
lon = scrapy.Field()
link = scrapy.Field()
schedule = scrapy.Field()
Aquí mi código de acceso AirportsSpider
para hacer eso:
class AirportsSpider(scrapy.Spider):
name = "airports"
start_urls = [''https://www.flightradar24.com/data/airports'']
allowed_domains = [''flightradar24.com'']
def clean_html(self, html_text):
soup = BeautifulSoup(html_text, ''html.parser'')
return soup.get_text()
rules = [
# Extract links matching ''item.php'' and parse them with the spider''s method parse_item
Rule(LxmlLinkExtractor(allow=(''data/airports/'',)), callback=''parse'')
]
def parse(self, response):
count_country = 0
countries = []
for country in response.xpath(''//a[@data-country]''):
if count_country > 5:
break
item = CountryItem()
url = country.xpath(''./@href'').extract()
name = country.xpath(''./@title'').extract()
item[''link''] = url[0]
item[''name''] = name[0]
count_country += 1
countries.append(item)
yield scrapy.Request(url[0],meta={''my_country_item'':item}, callback=self.parse_airports)
def parse_airports(self,response):
item = response.meta[''my_country_item'']
airports = []
for airport in response.xpath(''//a[@data-iata]''):
url = airport.xpath(''./@href'').extract()
iata = airport.xpath(''./@data-iata'').extract()
iatabis = airport.xpath(''./small/text()'').extract()
name = ''''.join(airport.xpath(''./text()'').extract()).strip()
lat = airport.xpath("./@data-lat").extract()
lon = airport.xpath("./@data-lon").extract()
iAirport = AirportItem()
iAirport[''name''] = self.clean_html(name)
iAirport[''link''] = url[0]
iAirport[''lat''] = lat[0]
iAirport[''lon''] = lon[0]
iAirport[''code_little''] = iata[0]
iAirport[''code_total''] = iatabis[0]
airports.append(iAirport)
for airport in airports:
json_url = ''https://api.flightradar24.com/common/v1/airport.json?code={code}&plugin/[/]=&plugin-setting/[schedule/]/[mode/]=&plugin-setting/[schedule/]/[timestamp/]={timestamp}&page=1&limit=50&token=''.format(code=airport[''code_little''], timestamp="1484150483")
yield scrapy.Request(json_url, meta={''airport_item'': airport}, callback=self.parse_schedule)
item[''airports''] = airports
yield {"country" : item}
def parse_schedule(self,response):
item = response.request.meta[''airport_item'']
jsonload = json.loads(response.body_as_unicode())
json_expression = jmespath.compile("result.response.airport.pluginData.schedule")
item[''schedule''] = json_expression.search(jsonload)
Explicación
En mi primer análisis, invoco una solicitud para cada enlace de país que encontré con el elemento de país creado a través de
meta={''my_country_item'':item}
. Cada una de estas solicitudes de devolución de llamadaself.parse_airports
En mi segundo nivel de análisis
parse_airports
,parse_airports
CountryItem
creado usandoitem = response.meta[''my_country_item'']
y creo un nuevo elementoiAirport = AirportItem()
para cada aeropuerto que encontré en esta página de país. Ahora quiero obtener información delschedule
para cadaAirportItem
creado y almacenado en la lista deairports
.En el segundo nivel de analizar
parse_airports
, ejecuto un bucle for en losairports
para capturar información deschedule
utilizando una nueva Solicitud. Como quiero incluir esta información de cronograma en mimeta={''airport_item'': airport}
este ítem en metainformaciónmeta={''airport_item'': airport}
. La devolución de llamada de esta solicitud ejecutaparse_schedule
En el tercer nivel de
parse_schedule
de parse,parse_schedule
la información de horario recolectada por scrapy en el AirportItem previamente creado usandoresponse.request.meta[''airport_item'']
Pero tengo un problema en mi código fuente, scrapy borró correctamente todas las informaciones (país, aeropuertos, calendario), pero mi comprensión del elemento anidado parece no ser correcta. Como puede ver, el json que produje contiene una country > list of (airport)
, pero no un country > list of (airport > schedule )
Mi código está en github: https://github.com/IDEES-Rouen/Flight-Scrapping