Scrapy crawling no funciona en el sitio web ASPX

Estoy raspando el sitio web de la Asamblea de Madrid, construido en aspx, y no tengo idea de cómo simular los clics en los enlaces donde necesito obtener los políticos correspondientes. Intenté esto:

import scrapy class AsambleaMadrid(scrapy.Spider): name = "Asamblea_Madrid" start_urls = ['http://www.asambleamadrid.es/ES/QueEsLaAsamblea/ComposiciondelaAsamblea/LosDiputados/Paginas/RelacionAlfabeticaDiputados.aspx'] def parse(self, response): for id in response.css('div#moduloBusqueda div.sangria div.sangria ul li a::attr(id)'): target = id.extract() url = "http://www.asambleamadrid.es/ES/QueEsLaAsamblea/ComposiciondelaAsamblea/LosDiputados/Paginas/RelacionAlfabeticaDiputados.aspx" formdata= {'__EVENTTARGET': target, '__VIEWSTATE': '/', '__EVENTVALIDATION': '/wEWCALIhqvYAwKh2YVvAuDF1KUDAqCK1bUOAqCKybkPAqCKnbQCAqCKsZEJAvejv84Dtkx5dCFr3QGqQD2wsFQh8nP3iq8', '__VIEWSTATEGENERATOR': 'BAB98CB3', '__REQUESTDIGEST': '0x476239970DCFDABDBBDF638A1F9B026BD43022A10D1D757B05F1071FF3104459B4666F96A47B4845D625BCB2BE0D88C6E150945E8F5D82C189B56A0DA4BC859D'} yield scrapy.FormRequest(url=url, formdata= formdata, callback=self.takeEachParty) def takeEachParty(self, response): print response.css('ul.listadoVert02 ul li::text').extract() 

Al ingresar al código fuente del sitio web, puedo ver cómo se ven los enlaces y cómo envían la consulta de JavaScript. Este es uno de los enlaces a los que necesito acceder:

 Grupo Parlamentario Popular de la Asamblea de Madrid 

He estado leyendo muchos artículos sobre, pero probablemente el problema sea mi ignorancia con respecto.

Gracias por adelantado.

EDITADO:

SOLUCIÓN: ¡Finalmente lo hice! Traducir el muy útil código de Padraic Cunningham a Scrapy way. Como especifiqué el problema para Scrapy, quiero publicar el resultado en caso de que alguien tenga el mismo problema que yo.

Así que aquí va:

 import scrapy import js2xml class AsambleaMadrid(scrapy.Spider): name = "AsambleaMadrid" start_urls = ['http://www.asambleamadrid.es/ES/QueEsLaAsamblea/ComposiciondelaAsamblea/LosDiputados/Paginas/RelacionAlfabeticaDiputados.aspx'] def parse(self, response): source = response hrefs = response.xpath("//*[@id='moduloBusqueda']//div[@class='sangria']/ul/li/a/@href").extract() form_data = self.validate(source) for ref in hrefs: # js2xml allows us to parse the JS function and params, and so to grab the __EVENTTARGET js_xml = js2xml.parse(ref) _id = js_xml.xpath( "//identifier[@name='WebForm_PostBackOptions']/following-sibling::arguments/string[starts-with(.,'ctl')]")[0] form_data["__EVENTTARGET"] = _id.text url_diputado = 'http://www.asambleamadrid.es/ES/QueEsLaAsamblea/ComposiciondelaAsamblea/LosDiputados/Paginas/RelacionAlfabeticaDiputados.aspx' # The proper way to send a POST in scrapy is by using the FormRequest yield scrapy.FormRequest(url=url_diputado, formdata=form_data, callback=self.extract_parties, method='POST') def validate(self, source): # these fields are the minimum required as cannot be hardcoded data = {"__VIEWSTATEGENERATOR": source.xpath("//*[@id='__VIEWSTATEGENERATOR']/@value")[0].extract(), "__EVENTVALIDATION": source.xpath("//*[@id='__EVENTVALIDATION']/@value")[0].extract(), "__VIEWSTATE": source.xpath("//*[@id='__VIEWSTATE']/@value")[0].extract(), " __REQUESTDIGEST": source.xpath("//*[@id='__REQUESTDIGEST']/@value")[0].extract()} return data def extract_parties(self, response): source = response name = source.xpath("//ul[@class='listadoVert02']/ul/li/a/text()").extract() print name 

Espero que quede claro. Gracias a todos, otra vez!

Si observa los datos publicados en el formulario en chrome o firebug, puede ver que hay muchos campos pasados ​​en la solicitud posterior, hay algunos que son esenciales y deben analizarse desde la página original, analizando los identificadores del div.sangria ul li a tags div.sangria ul li a no son suficientes, ya que los datos reales publicados son ligeramente diferentes, lo que se publica está en la función Javascript, WebForm_DoPostBackWithOptions que está en el href no en el atributo id :

 href='javascript:WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions("ctl00$m$g_36ea0310_893d_4a19_9ed1_88a133d06423$ctl00$Repeater1$ctl03$lnk_Grupo", "", true, "", "", false, true))'> 

A veces, todos los guiones bajos se reemplazan con signos de dólar, por lo que es fácil hacer un str.replace para ponerlos en el orden correcto, pero no realmente en este caso, podríamos usar una expresión regular para analizar, pero me gusta el lib js2xml que puede analizar una Función javascript y sus argumentos en un árbol xml.

El siguiente código que utiliza las solicitudes le muestra cómo puede obtener los datos de la solicitud inicial y llegar a todas las páginas que desee:

 import requests from lxml import html import js2xml post = "http://www.asambleamadrid.es/ES/QueEsLaAsamblea/ComposiciondelaAsamblea/LosDiputados/Paginas/RelacionAlfabeticaDiputados.aspx" def validate(xml): # these fields are the minimum required as cannot be hardcoded data = {"__VIEWSTATEGENERATOR": xml.xpath("//*[@id='__VIEWSTATEGENERATOR']/@value")[0], "__EVENTVALIDATION": xml.xpath("//*[@id='__EVENTVALIDATION']/@value")[0], "__VIEWSTATE": xml.xpath("//*[@id='__VIEWSTATE']/@value")[0], " __REQUESTDIGEST": xml.xpath("//*[@id='__REQUESTDIGEST']/@value")[0]} return data with requests.Session() as s: # make initial requests to get the links/hrefs and the from fields r = s.get( "http://www.asambleamadrid.es/ES/QueEsLaAsamblea/ComposiciondelaAsamblea/LosDiputados/Paginas/RelacionAlfabeticaDiputados.aspx") xml = html.fromstring(r.content) hrefs = xml.xpath("//*[@id='moduloBusqueda']//div[@class='sangria']/ul/li/a/@href") form_data = validate(xml) for h in hrefs: js_xml = js2xml.parse(h) _id = js_xml.xpath( "//identifier[@name='WebForm_PostBackOptions']/following-sibling::arguments/string[starts-with(.,'ctl')]")[ 0] form_data["__EVENTTARGET"] = _id.text r = s.post(post, data=form_data) xml = html.fromstring(r.content) print(xml.xpath("//ul[@class='listadoVert02']/ul/li/a/text()")) 

Si ejecutamos el código anterior, vemos los diferentes resultados de texto de todas las tags de anclaje:

 In [2]: with requests.Session() as s: ...: r = s.get( ...: "http://www.asambleamadrid.es/ES/QueEsLaAsamblea/ComposiciondelaAsamblea/LosDiputados/Paginas/RelacionAlfabeticaDiputados.aspx") ...: xml = html.fromstring(r.content) ...: hrefs = xml.xpath("//*[@id='moduloBusqueda']//div[@class='sangria']/ul/li/a/@href") ...: form_data = validate(xml) ...: for h in hrefs: ...: js_xml = js2xml.parse(h) ...: _id = js_xml.xpath( ...: "//identifier[@name='WebForm_PostBackOptions']/following-sibling::arguments/string[starts-with(.,'ctl')]")[ ...: 0] ...: form_data["__EVENTTARGET"] = _id.text ...: r = s.post(post, data=form_data) ...: xml = html.fromstring(r.content) ...: print(xml.xpath("//ul[@class='listadoVert02']/ul/li/a/text()")) ...: [u'Abo\xedn Abo\xedn, Sonsoles Trinidad', u'Adrados Gautier, M\xaa Paloma', u'Aguado Del Olmo, M\xaa Josefa', u'\xc1lvarez Padilla, M\xaa Nadia', u'Arribas Del Barrio, Jos\xe9 M\xaa', u'Ballar\xedn Valc\xe1rcel, \xc1lvaro C\xe9sar', u'Berrio Fern\xe1ndez-Caballero, M\xaa In\xe9s', u'Berzal Andrade, Jos\xe9 Manuel', u'Cam\xedns Mart\xednez, Ana', u'Carballedo Berlanga, M\xaa Eugenia', 'Cifonts Cuencas, Cristina', u'D\xedaz Ayuso, Isabel Natividad', u'Escudero D\xedaz-Tejeiro, Marta', u'Fermosel D\xedaz, Jes\xfas', u'Fern\xe1ndez-Quejo Del Pozo, Jos\xe9 Luis', u'Garc\xeda De Vinuesa Gardoqui, Ignacio', u'Garc\xeda Mart\xedn, Mar\xeda Bego\xf1a', u'Garrido Garc\xeda, \xc1ngel', u'G\xf3mez Ruiz, Jes\xfas', u'G\xf3mez-Angulo Rodr\xedguez, Juan Antonio', u'Gonz\xe1lez Gonz\xe1lez, Isabel Gema', u'Gonz\xe1lez Jim\xe9nez, Bartolom\xe9', u'Gonz\xe1lez Taboada, Jaime', u'Gonz\xe1lez-Mo\xf1ux V\xe1zquez, Elena', u'Gonzalo L\xf3pez, Rosal\xeda', 'Izquierdo Torres, Carlos', u'Li\xe9bana Montijano, Pilar', u'Mari\xf1o Ortega, Ana Isabel', u'Moraga Valiente, \xc1lvaro', u'Mu\xf1oz Abrines, Pedro', u'N\xfa\xf1ez Guijarro, Jos\xe9 Enrique', u'Olmo Fl\xf3rez, Luis Del', u'Ongil Cores, M\xaa Gador', 'Ortiz Espejo, Daniel', u'Ossorio Crespo, Enrique Mat\xedas', 'Peral Guerra, Luis', u'P\xe9rez Baos, Ana Isabel', u'P\xe9rez Garc\xeda, David', u'Pla\xf1iol De Lacalle, Regina M\xaa', u'Redondo Alcaide, M\xaa Isabel', u'Roll\xe1n Ojeda, Pedro', u'S\xe1nchez Fern\xe1ndez, Alejandro', 'Sanjuanbenito Bonal, Diego', u'Serrano Guio, Jos\xe9 Tom\xe1s', u'Serrano S\xe1nchez-Capuchino, Alfonso Carlos', 'Soler-Espiauba Gallo, Juan', 'Toledo Moreno, Lucila', 'Van-Halen Acedo, Juan'] [u'Andaluz Andaluz, M\xaa Isabel', u'Ardid Jim\xe9nez, M\xaa Isabel', u'Carazo G\xf3mez, M\xf3nica', u'Casares D\xedaz, M\xaa Luc\xeda Inmaculada', u'Cepeda Garc\xeda De Le\xf3n, Jos\xe9 Carmelo', 'Cruz Torrijos, Diego', u'Delgado G\xf3mez, Carla', u'Franco Pardo, Jos\xe9 Manuel', u'Freire Campo, Jos\xe9 Manuel', u'Gabilondo Pujol, \xc1ngel', 'Gallizo Llamas, Mercedes', u"Garc\xeda D'Atri, Ana", u'Garc\xeda-Rojo Garrido, Pedro Pablo', u'G\xf3mez Montoya, Rafael', u'G\xf3mez-Chamorro Torres, Jos\xe9 \xc1ngel', u'Gonz\xe1lez Gonz\xe1lez, M\xf3nica Silvana', u'Leal Fern\xe1ndez, M\xaa Isaura', u'Llop Cuenca, M\xaa Pilar', 'Lobato Gandarias, Juan', u'L\xf3pez Ruiz, M\xaa Carmen', u'Manguan Valdertwig, Eva M\xaa', u'Maroto Illera, M\xaa Reyes', u'Mart\xednez Ten, Carmen', u'Mena Romero, M\xaa Carmen', u'Moreno Navarro, Juan Jos\xe9', u'Moya Nieto, Encarnaci\xf3n', 'Navarro Lanchas, Josefa', 'Nolla Estrada, Modesto', 'Pardo Ortiz, Josefa Dolores', u'Quintana Viar, Jos\xe9', u'Rico Garc\xeda-Hierro, Enrique', u'Rodr\xedguez Garc\xeda, Nicol\xe1s', u'S\xe1nchez Acera, Pilar', u'Sant\xedn Fern\xe1ndez, Pedro', 'Segovia Noriega, Juan', 'Vicente Viondi, Daniel', u'Vinagre Alc\xe1zar, Agust\xedn'] ['Abasolo Pozas, Olga', 'Ardanuy Pizarro, Miguel', u'Beirak Ulanosky, Jazm\xedn', u'Camargo Fern\xe1ndez, Ra\xfal', 'Candela Pokorna, Marco', 'Delgado Orgaz, Emilio', u'D\xedaz Rom\xe1n, Laura', u'Espinar Merino, Ram\xf3n', u'Espinosa De La Llave, Mar\xeda', u'Fern\xe1ndez Rubi\xf1o, Eduardo', u'Garc\xeda G\xf3mez, M\xf3nica', 'Gimeno Reinoso, Beatriz', u'Guti\xe9rrez Benito, Eduardo', 'Huerta Bravo, Raquel', u'L\xf3pez Hern\xe1ndez, Isidro', u'L\xf3pez Rodrigo, Jos\xe9 Manuel', u'Mart\xednez Abarca, Hugo', u'Morano Gonz\xe1lez, Jacinto', u'Ongil L\xf3pez, Miguel', 'Padilla Estrada, Pablo', u'Ruiz-Huerta Garc\xeda De Viedma, Lorena', 'Salazar-Alonso Revuelta, Cecilia', u'San Jos\xe9 P\xe9rez, Carmen', u'S\xe1nchez P\xe9rez, Alejandro', u'Serra S\xe1nchez, Isabel', u'Serra S\xe1nchez, Clara', 'Sevillano De Las Heras, Elena'] [u'Aguado Crespo, Ignacio Jes\xfas', u'\xc1lvarez Cabo, Daniel', u'Gonz\xe1lez Pastor, Dolores', u'Iglesia Vicente, M\xaa Teresa De La', 'Lara Casanova, Francisco', u'Marb\xe1n De Frutos, Marta', u'Marcos Arias, Tom\xe1s', u'Meg\xedas Morales, Jes\xfas Ricardo', u'N\xfa\xf1ez S\xe1nchez, Roberto', 'Reyero Zubiri, Alberto', u'Rodr\xedguez Dur\xe1n, Ana', u'Rubio Ruiz, Juan Ram\xf3n', u'Ruiz Fern\xe1ndez, Esther', u'Sol\xeds P\xe9rez, Susana', 'Trinidad Martos, Juan', 'Veloso Lozano, Enrique', u'Zafra Hern\xe1ndez, C\xe9sar'] 

Puedes agregar la misma lógica exacta a tu araña, solo usé solicitudes para mostrarte un ejemplo funcional. También debe tener en cuenta que no todos los sitios asp.net se comportan de la misma manera, es posible que tenga que volver a validar cada publicación como en esta respuesta relacionada.

Creo que la respuesta de from_response podría ayudarte mucho (tal vez no sea la mejor opción, pero para eso, pero tendrás la idea), prueba algo como esto:

 import scrapy import urllib from scrapy.http.request.form import FormRequest class AsambleaMadrid(scrapy.Spider): name = "Asamblea_Madrid" start_urls = ['http://www.asambleamadrid.es/ES/QueEsLaAsamblea/ComposiciondelaAsamblea/LosDiputados/Paginas/RelacionAlfabeticaDiputados.aspx'] def parse(self, response): ids_re = r'WebForm_PostBackOptions\(([^,]*)' for id in response.css('#moduloBusqueda li a').re(ids_re): target = urllib.unquote(id).strip('"') formdata = {'__EVENTTARGET': target} request = FormRequest.from_response(response=response, formdata=formdata, callback=self.takeEachParty, dont_click=True) yield request def takeEachParty(self, response): print response.css('.listadoVert02 li a::text').extract() 

De acuerdo con ELRuLL: Firebug es tu mejor amigo mientras raspa. Si desea evitar la simulación JS, necesita reproducir cuidadosamente todos los parámetros / encabezados que se envían.

Por ejemplo, por lo que veo para __EVENTTARGET, está enviando solo id="ctl00_m_g_36ea0310_893d_4a19_9ed1_88a133d06423_ctl00_Repeater2_ctl01_lnk_Diputado")

y a través de Firebug vemos que:

 __EVENTTARGET=ctl00$m$g_36ea0310_893d_4a19_9ed1_88a133d06423$ctl00$Repeater2$ctl01$lnk_Diputado 

Forma en firebug Tal vez sea la razón y tal vez no, solo repetir y probar.

Firebug enlace por si acaso.