spider span requests follow extractfirst docs create nested scrapy scrapy-spider

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 llamada self.parse_airports

  • En mi segundo nivel de análisis parse_airports , parse_airports CountryItem creado usando item = response.meta[''my_country_item''] y creo un nuevo elemento iAirport = AirportItem() para cada aeropuerto que encontré en esta página de país. Ahora quiero obtener información del schedule para cada AirportItem creado y almacenado en la lista de airports .

  • En el segundo nivel de analizar parse_airports , ejecuto un bucle for en los airports para capturar información de schedule utilizando una nueva Solicitud. Como quiero incluir esta información de cronograma en mi meta={''airport_item'': airport} este ítem en metainformación meta={''airport_item'': airport} . La devolución de llamada de esta solicitud ejecuta parse_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 usando response.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