Scrapy, raspando datos dentro de un Javascript

Estoy utilizando scrapy para filtrar los datos de un sitio web. Sin embargo, los datos que quería no estaban dentro del html, sino que son de un javascript. Entonces, mi pregunta es:

¿Cómo obtener los valores (valores de texto) de tales casos?

Este es el sitio que trato de rastrear: https://www.mcdonalds.com.sg/locate-us/

Atributos que estoy tratando de obtener: Dirección, Contacto, Horas de operación.

Si hace un “clic derecho”, “ve la fuente” dentro de un navegador Chrome, verá que tales valores no están disponibles en el HTML.


Editar

Sry paul, hice lo que me dijiste, encontré el admin-ajax.php y vi el cuerpo pero, ahora estoy realmente atascado.

¿Cómo recupero los valores del objeto json y los almaceno en un campo variable propio? Sería bueno si pudieras compartir cómo hacer solo un atributo para el público y para aquellos que también comenzaron a usar scrapy.

Aquí está mi código hasta ahora

Items.py

 class McDonaldsItem(Item): name = Field() address = Field() postal = Field() hours = Field() 

McDonalds.py

 from scrapy.spider import BaseSpider from scrapy.selector import HtmlXPathSelector import re from fastfood.items import McDonaldsItem class McDonaldSpider(BaseSpider): name = "mcdonalds" allowed_domains = ["mcdonalds.com.sg"] start_urls = ["https://www.mcdonalds.com.sg/locate-us/"] def parse_json(self, response): js = json.loads(response.body) pprint.pprint(js) 

Sry por edición larga, así que, en resumen, ¿cómo almaceno el valor json en mi atributo? por ejemplo

*** elemento [‘dirección’] = * cómo recuperar ****

PS, no estoy seguro de si esto ayuda, pero ejecuto estos scripts en la línea cmd usando

scrapy crawl mcdonalds -o McDonalds.json -t json (para guardar todos mis datos en un archivo json)

No puedo enfatizar lo suficiente en lo agradecido que me siento. Sé que es un poco irrazonable preguntarte esto, estaré totalmente bien incluso si no tienes tiempo para esto.

( scrapy-users esto en la lista de correo de los scrapy-users pero por sugerencia de Paul, lo estoy publicando aquí ya que complementa la respuesta con la interacción del comando de shell ).

En general, los sitios web que utilizan un servicio de terceros para representar la visualización de algunos datos (mapa, tabla, etc.) deben enviar los datos de alguna manera, y en la mayoría de los casos se puede acceder a estos datos desde el navegador.

Para este caso, una inspección (es decir, explorando las solicitudes realizadas por el navegador) muestra que los datos se cargan desde una solicitud POST a https://www.mcdonalds.com.sg/wp-admin/admin-ajax.php

Entonces, básicamente tienes todos los datos que deseas en un formato json agradable listo para consumir.

Scrapy proporciona el comando de shell que es muy conveniente para el pensador con el sitio web antes de escribir la araña:

 $ scrapy shell https://www.mcdonalds.com.sg/locate-us/ 2013-09-27 00:44:14-0400 [scrapy] INFO: Scrapy 0.16.5 started (bot: scrapybot) ... In [1]: from scrapy.http import FormRequest In [2]: url = 'https://www.mcdonalds.com.sg/wp-admin/admin-ajax.php' In [3]: payload = {'action': 'ws_search_store_location', 'store_name':'0', 'store_area':'0', 'store_type':'0'} In [4]: req = FormRequest(url, formdata=payload) In [5]: fetch(req) 2013-09-27 00:45:13-0400 [default] DEBUG: Crawled (200)  (referer: None) ... In [6]: import json In [7]: data = json.loads(response.body) In [8]: len(data['stores']['listing']) Out[8]: 127 In [9]: data['stores']['listing'][0] Out[9]: {u'address': u'678A Woodlands Avenue 6
#01-05
Singapore 731678', u'city': u'Singapore', u'id': 78, u'lat': u'1.440409', u'lon': u'103.801489', u'name': u"McDonald's Admiralty", u'op_hours': u'24 hours
\r\nDessert Kiosk: 0900-0100', u'phone': u'68940513', u'region': u'north', u'type': [u'24hrs', u'dessert_kiosk'], u'zip': u'731678'}

En resumen: en su araña debe devolver el FormRequest(...) anterior, luego en la callback, cargue el objeto json desde response.body y finalmente para los datos de cada tienda en la lista de data['stores']['listing'] crea un elemento con los valores deseados.

Algo como esto:

 class McDonaldSpider(BaseSpider): name = "mcdonalds" allowed_domains = ["mcdonalds.com.sg"] start_urls = ["https://www.mcdonalds.com.sg/locate-us/"] def parse(self, response): # This receives the response from the start url. But we don't do anything with it. url = 'https://www.mcdonalds.com.sg/wp-admin/admin-ajax.php' payload = {'action': 'ws_search_store_location', 'store_name':'0', 'store_area':'0', 'store_type':'0'} return FormRequest(url, formdata=payload, callback=self.parse_stores) def parse_stores(self, response): data = json.loads(response.body) for store in data['stores']['listing']: yield McDonaldsItem(name=store['name'], address=store['address']) 

Cuando abra https://www.mcdonalds.com.sg/locate-us/ en el navegador de su elección, abra la herramienta “inspeccionar” (es de esperar que tenga una, por ejemplo, Chrome o Firefox) y busque “Red “pestaña.

Puede filtrar aún más los eventos “XHR” (XMLHttpRequest) y verá una solicitud POST a https://www.mcdonalds.com.sg/wp-admin/admin-ajax.php con este cuerpo

 action=ws_search_store_location&store_name=0&store_area=0&store_type=0 

La respuesta a esa solicitud POST es un objeto JSON con toda la información que desea

 import json import pprint ... class MySpider(BaseSpider): ... def parse_json(self, response): js = json.loads(response.body) pprint.pprint(js) 

Esto daría como resultado algo como:

 {u'flagicon': u'https://www.mcdonalds.com.sg/wp-content/themes/mcd/images/storeflag.png', u'stores': {u'listing': [{u'address': u'678A Woodlands Avenue 6
#01-05
Singapore 731678', u'city': u'Singapore', u'id': 78, u'lat': u'1.440409', u'lon': u'103.801489', u'name': u"McDonald's Admiralty", u'op_hours': u'24 hours
\r\nDessert Kiosk: 0900-0100', u'phone': u'68940513', u'region': u'north', u'type': [u'24hrs', u'dessert_kiosk'], u'zip': u'731678'}, {u'address': u'383 Bukit Timah Road
#01-09B
Alocassia Apartments
Singapore 259727', u'city': u'Singapore', u'id': 97, u'lat': u'1.319752', u'lon': u'103.827398', u'name': u"McDonald's Alocassia", u'op_hours': u'Daily: 0630-0100', u'phone': u'68874961', u'region': u'central', u'type': [u'24hrs_weekend', u'drive_thru', u'mccafe'], u'zip': u'259727'}, ... {u'address': u'60 Yishuan Avenue 4
#01-11

Singapore 769027', u'city': u'Singapore', u'id': 1036, u'lat': u'1.423924', u'lon': u'103.840628', u'name': u"McDonald's Yishun Safra", u'op_hours': u'24 hours', u'phone': u'67585632', u'region': u'north', u'type': [u'24hrs', u'drive_thru', u'live_screening', u'mccafe', u'bday_party'], u'zip': u'769027'}], u'region': u'all'}}

Te dejo para que extraigas los campos que quieras.

En FormRequest () que envía con Scrapy, probablemente necesite agregar un encabezado “X-Request-With: XMLHttpRequest” (su navegador lo envía si observa los encabezados de solicitud en la herramienta de inspección)