2017-01-13 6 views
1

Я пытаюсь отменить информацию о расписании самолета на веб-сайте www.flightradar24.com для исследовательского проекта.Множественный вложенный запрос с помощью scrapy

Иерархия JSon файла я хочу, чтобы получить что-то вроде этого:

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

Country и Airport хранятся с использованием элементов, и, как вы можете видеть на JSON файл в CountryItem (ссылка, имя атрибута), наконец, магазин множественный AirportItem (code_total, ссылки, широта, долгота, название, графика):

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

Вот мой код SCRAPY AirportsSpider сделать:

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) 

Объяснение:

  • В моем первом разборе, я называю запрос по каждой ссылке страны я нашел в CountryItem ти, созданном с помощью meta={'my_country_item':item}. Каждый из этих запросов обратного вызова self.parse_airports

  • На втором уровне синтаксического анализа parse_airports, я поймать CountryItem, созданный с помощью item = response.meta['my_country_item'] и я создать новый элемент iAirport = AirportItem() для каждого аэропорта я нашел в этой стране страницы. Теперь я хочу получить schedule информация для каждого AirportItem создана и сохранена в списке airports.

  • Во втором уровне синтаксического анализа parse_airports, я запустить цикл на airports, чтобы поймать schedule информации с использованием нового запроса. Поскольку я хочу включить эту информацию о расписании в свой идентификатор AirportItem, я включаю этот элемент в метаинформацию meta={'airport_item': airport}. Обратный вызов этого запроса запуска parse_schedule

  • В третьем уровне синтаксического анализа parse_schedule, я впрыснуть информацию расписания, собранные Scrapy в AirportItem ранее созданный с помощью response.request.meta['airport_item']

Но у меня есть проблема в моем источнике код, scrapy правильно отбросить всю информацию (страну, аэропорты, расписание), но мое понимание вложенного элемента кажется неправильным. Как вы можете видеть JSON я произведенный содержать country > list of (airport), но не country > list of (airport > schedule)

enter image description here

Мой код на GitHub: https://github.com/IDEES-Rouen/Flight-Scrapping

ответ

2

Вопрос заключается в том, что вы раскошелиться ваш деталь, где согласно вашей логике вы только хотите 1 пункт для каждой страны, так что вы не можете дать несколько пунктов в любой момент после разбора страны. То, что вы хотите сделать, это собрать все из них в один элемент.
Для этого вам нужно создать цикл разбора:

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

Привет Granitosaurus, я пытаюсь код после некоторой незначительной коррекции опечатки, но любой товара написанного по трубопроводу. – reyman64

+0

@ reyman64 эй, я исправил некоторые опечатки и протестировал этот код, и это действительно работает. Это результаты для одного элемента страны: http: //pastebin.ubuntu.com/23798326/У меня есть. – Granitosaurus

+0

Это плохое решение. Снижает эффективность скремблирования, без доказательств исключений, не может использоваться при наличии большого количества дочерних элементов, и вы планируете вставлять в БД. Подробнее читайте в [моем вопросе] (https://stackoverflow.com/questions/46383499/scrapy-how-to-populate-hierarchic-items-with-multipel-requests) – frenzy