Используя js2xml и Scrapy, как я могу выполнить итерацию по объекту json для выбора определенного узла?

#json #scrapy #js2xml

#json #scrapy #js2xml

Вопрос:

Я пытаюсь выполнить итерацию по JSON-ответу со страницы, используя js2xml. У меня вопрос, как мне вызвать узел «stores» и передать только это в качестве моего ответа? JSON выглядит следующим образом:

 <script>
    window.appData = {
        "ressSize": "large",
        "cssPath": "http://css.bbystatic.com/",
        "imgPath": "http://images.bbystatic.com/",
        "jsPath": "http://js.bbystatic.com/",
        "bbyDomain": "http://www.bestbuy.com/",
        "bbySslDomain": "https://www-ssl.bestbuy.com/",
        "isUserLoggedIn": false,
        "zipCode": "46801",
        "stores": [{
            "id": "2727",
            "name": "GLENBROOK SQUARE",
            "addr1": "4201 coldwater rd",
            "addr2": "spc g10",
            "city": "fort wayne",
            "state": "IN",
            "country": "US",
            "zipCode": "46805",
            "phone": "260-482-5230"...
<script> 
  

Мой паук для этого прост, но, похоже, я не могу придумать, что мне нужно для анализа «хранилищ» 9-го узла. Это то, что у меня есть до сих пор:

 def parse(self, response):

js = response.xpath('//script[contains(.,"window.appData")]/text()').extract_first()

jstree = js2xml.parse(js)
jstree.xpath('//assign[left//identifier[@name="appData"]]/right/*')
js2xml.make_dict(jstree.xpath('//assign[left//identifier[@name="appData"]]/right/*')[0])`
  

Ответ на это дает мне:

 <program>
  <assign operator="=">
    <left>
      <dotaccessor>
        <object>
          <identifier name="window"/>
        </object>
        <property>
          <identifier name="appData"/>
        </property>
      </dotaccessor>
    </left>
    <right>
      <object>
        <property name="ressSize">
          <string>large</string>
        </property>
        <property name="cssPath">
          <string>http://css.bbystatic.com/</string>
        </property>
        <property name="imgPath">
          <string>http://images.bbystatic.com/</string>
        </property>
        <property name="jsPath">
          <string>http://js.bbystatic.com/</string>
        </property>
        <property name="bbyDomain">
          <string>http://www.bestbuy.com/</string>
        </property>
        <property name="bbySslDomain">
          <string>https://www-ssl.bestbuy.com/</string>
        </property>
        <property name="isUserLoggedIn">
          <boolean>false</boolean>
        </property>
        <property name="zipCode">
          <string></string>
        </property>
        <property name="stores">
          <array/>
        </property>
        <property name="preferredStores">
          <array/>
        </property>
      </object>
    </right>
  </assign>
</program>

{'bbyDomain': 'http://www.bestbuy.com/',
 'bbySslDomain': 'https://www-ssl.bestbuy.com/',
 'cssPath': 'http://css.bbystatic.com/',
 'imgPath': 'http://images.bbystatic.com/',
 'isUserLoggedIn': False,
 'jsPath': 'http://js.bbystatic.com/',
 'preferredStores': [],
 'ressSize': 'large',
 'stores': [],
 'zipCode': ''}
  

Любые мысли были бы полезны!

Комментарии:

1. Пожалуйста, укажите URL-адрес источника, чтобы код можно было протестировать с реальными данными.

2. bestbuy.com/site/store-locator

3. Значение для ключа «stores» представляет собой пустой массив в исходном коде HTML: <script>window.appData = {"ressSize":"large","cssPath":"http://css.bbystatic.com/","imgPath":"http://images.bbystatic.com/","jsPath":"http://js.bbystatic.com/","bbyDomain":"http://www.bestbuy.com/","bbySslDomain":"https://www-ssl.bestbuy.com/","isUserLoggedIn":false,"zipCode":"","stores":[],"preferredStores":[]};</script>

4. когда я открываю страницу, она не пуста. но я вижу это вживую. Я думаю, что если бы был заполнен почтовый индекс, он генерирует список.

Ответ №1:

Давайте используем Нью-Йорк в качестве местоположения, http://www.bestbuy.com/site/store-locator/11356

 $ scrapy shell http://www.bestbuy.com/site/store-locator/11356
2016-10-10 16:19:07 [scrapy] INFO: Scrapy 1.2.0 started (bot: scrapybot)
(...)
2016-10-10 16:19:08 [scrapy] DEBUG: Crawled (200) <GET http://www.bestbuy.com/site/store-locator/11356> (referer: None)

>>> js = response.xpath('//script[contains(.,"window.appData")]/text()').extract_first()
>>> js[:100]
u'window.appData = {"ressSize":"large","cssPath":"http://css.bbystatic.com/","imgPath":"http://images.'
>>> 
>>> jstree = js2xml.parse(js)
>>> app_data_node = jstree.xpath('//assign[left//identifier[@name="appData"]]/right/*')[0]
>>> app_data = js2xml.make_dict(app_data_node)
>>> app_data.keys()
['ressSize', 'isUserLoggedIn', 'preferredStores', 'jsPath', 'bbyDomain', 'bbySslDomain', 'zipCode', 'imgPath', 'cssPath', 'stores']
>>> len(app_data['stores'])
25
  

Итак, у вас есть 25 магазинов для Нью-Йорка. Вы можете просто выполнить цикл app_data["stores"] .

 >>> from pprint import pprint
>>> for store in app_data['stores']:
...     pprint(store)
... 
{'addPreferredStoreLink': '/site/store-locator/preferred/1115',
 'addr1': '13107 40th rd',
 'addr2': 'ste c300',
 'city': 'flushing',
 'country': 'US',
 'hours': [{'close': '20:00', 'date': '2016-10-09', 'open': '11:00'},
           {'close': '21:00',
            'closeTime': '9:00 PM',
            'date': '2016-10-10',
            'open': '10:00',
            'openTime': '10:00 AM'},
           {'close': '21:00', 'date': '2016-10-11', 'open': '10:00'},
           {'close': '21:00', 'date': '2016-10-12', 'open': '10:00'},
           {'close': '21:00', 'date': '2016-10-13', 'open': '10:00'},
           {'close': '22:00', 'date': '2016-10-14', 'open': '10:00'},
           {'close': '22:00', 'date': '2016-10-15', 'open': '10:00'},
           {'close': '20:00', 'date': '2016-10-16', 'open': '11:00'},
           {'close': '21:00', 'date': '2016-10-17', 'open': '10:00'},
           {'close': '21:00', 'date': '2016-10-18', 'open': '10:00'},
           {'close': '21:00', 'date': '2016-10-19', 'open': '10:00'},
           {'close': '21:00', 'date': '2016-10-20', 'open': '10:00'},
           {'close': '22:00', 'date': '2016-10-21', 'open': '10:00'},
           {'close': '22:00', 'date': '2016-10-22', 'open': '10:00'}],
 'hoursDisplay': {'close': '21:00',
                  'closeTime': '9:00 PM',
                  'date': '2016-10-10',
                  'open': '10:00',
                  'openTime': '10:00 AM'},
 'id': '1115',
 'isPreferredStore': False,
 'latitude': '40.75662',
 'locationSubType': 'Big Box Store',
 'locationType': 'Store',
 'longitude': '-73.83698',
 'name': 'FLUSHING NY',
 'phone': '718-888-3629',
 'removePreferredStoreLink': '/site/store-locator/preferred/1115',
 'services': ['Geek Squad Services',
              'Best Buy Mobile',
              'Best Buy For Business',
              'Apple Shop',
              'Electronics Recycling',
              u'Hablamos Espaxf1ol',
              'Car amp; GPS Installation Services',
              'Samsung Experience Shop',
              'Windows Store'],
 'state': 'NY',
 'zipCode': '11354'}
(...)
{'addPreferredStoreLink': '/site/store-locator/preferred/374',
 'addr1': '2478 central park ave',
 'city': 'yonkers',
 'country': 'US',
 'hours': [{'close': '20:00', 'date': '2016-10-09', 'open': '11:00'},
           {'close': '21:00',
            'closeTime': '9:00 PM',
            'date': '2016-10-10',
            'open': '10:00',
            'openTime': '10:00 AM'},
           {'close': '21:00', 'date': '2016-10-11', 'open': '10:00'},
           {'close': '21:00', 'date': '2016-10-12', 'open': '10:00'},
           {'close': '21:00', 'date': '2016-10-13', 'open': '10:00'},
           {'close': '21:00', 'date': '2016-10-14', 'open': '10:00'},
           {'close': '21:00', 'date': '2016-10-15', 'open': '10:00'},
           {'close': '20:00', 'date': '2016-10-16', 'open': '11:00'},
           {'close': '21:00', 'date': '2016-10-17', 'open': '10:00'},
           {'close': '21:00', 'date': '2016-10-18', 'open': '10:00'},
           {'close': '21:00', 'date': '2016-10-19', 'open': '10:00'},
           {'close': '21:00', 'date': '2016-10-20', 'open': '10:00'},
           {'close': '21:00', 'date': '2016-10-21', 'open': '10:00'},
           {'close': '21:00', 'date': '2016-10-22', 'open': '10:00'}],
 'hoursDisplay': {'close': '21:00',
                  'closeTime': '9:00 PM',
                  'date': '2016-10-10',
                  'open': '10:00',
                  'openTime': '10:00 AM'},
 'id': '374',
 'isPreferredStore': False,
 'latitude': '40.9814',
 'locationSubType': 'Big Box Store',
 'locationType': 'Store',
 'longitude': '-73.8277',
 'name': 'YONKERS NY',
 'phone': '914-337-4077',
 'removePreferredStoreLink': '/site/store-locator/preferred/374',
 'services': ['Windows Store',
              'Geek Squad Services',
              'Best Buy Mobile',
              'Best Buy For Business',
              'Apple Shop',
              'Electronics Recycling',
              u'Hablamos Espaxf1ol',
              'Samsung Experience',
              'LG Experience ',
              'Sony Experience ',
              'Car amp; GPS Installation Services'],
 'state': 'NY',
 'zipCode': '10710'}
>>> 
  

В вашем обратном вызове Scrapy вы можете перевести это следующим образом:

 def parse(self, response):

    js = response.xpath('//script[contains(.,"window.appData")]/text()').extract_first()
    jstree = js2xml.parse(js)
    app_data_node = jstree.xpath('//assign[left//identifier[@name="appData"]]/right/*')[0]
    app_data = js2xml.make_dict(app_data_node)

    for store in app_data['stores']:
        yield store
  

Комментарии:

1. Большое спасибо за помощь!