2017-01-13 5 views
1

Ich versuche, einige Flugplan Informationen auf www.flathradar24.com Website für Forschungsprojekt zu verschrotten.Mehrere verschachtelte Anfrage mit scrapy

Die Hierarchie der JSON-Datei i erhalten wollen, ist so etwas wie das:

Object ID 
- country 
    - link 
    - name 
    - airports 
    - airport0 
     - code_total 
     - link 
     - lat 
     - lon 
     - name 
     - schedule 
      - ... 
      - ... 
     - airport1 
     - code_total 
     - link 
     - lat 
     - lon 
     - name 
     - schedule 
      - ... 
      - ... 

Country und Airport verwenden Elemente gespeichert, und wie Sie auf JSON-Datei die CountryItem (Link, name-Attribut) sehen kann schließlich Laden mehrere AirportItem (code_total, Link, lat, Lon, Name, Zeitplan):

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() 

Hier mein scrapy Code AirportsSpider, das zu tun:

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) 

Erläuterung:

  • In meinem ersten Parse, nenne ich einen Antrag auf für jedes Land Link i whith die CountryItem über meta={'my_country_item':item} erstellt gefunden. Jede dieser Anfrage Rückruf self.parse_airports

  • In meinem zweiten Ebene von Parse parse_airports, fangen i CountryItem mit item = response.meta['my_country_item'] erstellt, und ich erstellen ein neues Objekt iAirport = AirportItem() für jeden Flughafen i in diesem Land Seite gefunden. Jetzt möchte ich schedule Informationen für jede AirportItem erstellt und in airports Liste gespeichert erhalten.

  • In der zweiten Ebene des Parse-parse_airports, i laufen auf airports eine for-Schleife schedule Informationen unter Verwendung einer neuen Anforderung zu fangen. Da ich diese Zeitplaninformationen in mein AirportItem aufnehmen möchte, füge ich dieses Element in Metainformationen meta={'airport_item': airport} ein. Der Rückruf dieser Anfrage Lauf parse_schedule

  • In der dritten Stufe der Parse parse_schedule, spritze ich die Zeitplaninformationen von scrapy in die AirportItem gesammelt zuvor erstellt mit response.request.meta['airport_item']

Aber ich habe ein Problem in meinem Source Code, scrapy korrekt alle Informationen (Land, Flughäfen, Zeitplan) verwerfen, aber mein Verständnis von verschachtelten Artikel scheint nicht korrekt. Wie Sie die json sehen i erzeugt werden, enthalten country > list of (airport), aber nicht country > list of (airport > schedule)

enter image description here

Mein Code ist auf GitHub: https://github.com/IDEES-Rouen/Flight-Scrapping

Antwort

2

Das Problem ist, dass Sie Ihren Artikel Gabel, wo Sie nach Ihrer Logik nur Willst du 1 Artikel pro Land, so kannst du zu einem beliebigen Zeitpunkt nach dem Parsing des Landes keine multiplen Artikel liefern. Was Sie tun möchten, ist alle in einen Gegenstand zu stapeln.
zu tun, dass Sie eine Parsing-Schleife erstellen müssen:

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}) 
+0

Hallo Granitosaurus, ich versuche, den Code nach einige kleinere Tippfehler-Korrektur, aber jeder Punkt wird durch Pipeline geschrieben. – reyman64

+0

@ reyman64 hey, ich habe ein paar Tippfehler korrigiert und diesen Code getestet und das funktioniert. Dies sind die Ergebnisse für einen Ländereintrag: http: //pastebin.ubuntu.com/23798326/Ich habe. – Granitosaurus

+0

Das ist eine schlechte Lösung. Reduziert den Scraping-Effekt, kein Ausnahmen-Beweis, nicht zu ändern, wenn viele untergeordnete Elemente vorhanden sind und Sie beabsichtigen, in DB einzufügen. Lesen Sie mehr in [meine Frage] (https://stackoverflow.com/questions/46383499/scrapy-how-to-populate-hierarchic-items-with-multipel-requests) – frenzy