Capturando códigos de estado http con araña scrapy

Soy nuevo en scrapy. Estoy escribiendo una araña diseñada para verificar una larga lista de direcciones URL para los códigos de estado del servidor y, cuando corresponda, a qué URL se redirigen. Es importante destacar que, si hay una cadena de redirecciones, necesito conocer el código de estado y la URL en cada salto. Estoy usando response.meta [‘redirect_urls’] para capturar las URL, pero no estoy seguro de cómo capturar los códigos de estado; no parece haber una clave de respuesta para eso.

Me doy cuenta de que es posible que deba escribir algunas middlewear personalizadas para exponer estos valores, pero no tengo muy claro cómo registrar los códigos de estado para cada salto, ni cómo acceder a estos valores desde la araña. He echado un vistazo pero no puedo encontrar un ejemplo de alguien que esté haciendo esto. Si alguien me puede orientar en la dirección correcta, sería muy apreciado.

Por ejemplo,

items = [] item = RedirectItem() item['url'] = response.url item['redirected_urls'] = response.meta['redirect_urls'] item['status_codes'] = #???? items.append(item) 

Edición : en base a los comentarios de warawauk y la ayuda proactiva de los usuarios del canal IRC (freenode #scrappy), he logrado hacer esto. Creo que es un poco hacky, así que cualquier comentario para mejorar es bienvenido:

(1) Desactive el middleware predeterminado en la configuración y agregue el suyo propio:

 DOWNLOADER_MIDDLEWARES = { 'scrapy.contrib.downloadermiddleware.redirect.RedirectMiddleware': None, 'myproject.middlewares.CustomRedirectMiddleware': 100, } 

(2) Cree su CustomRedirectMiddleware en su middlewares.py. Hereda de la clase principal redirectmiddleware y captura la redirección:

 class CustomRedirectMiddleware(RedirectMiddleware): """Handle redirection of requests based on response status and meta-refresh html tag""" def process_response(self, request, response, spider): #Get the redirect status codes request.meta.setdefault('redirect_status', []).append(response.status) if 'dont_redirect' in request.meta: return response if request.method.upper() == 'HEAD': if response.status in [301, 302, 303, 307] and 'Location' in response.headers: redirected_url = urljoin(request.url, response.headers['location']) redirected = request.replace(url=redirected_url) return self._redirect(redirected, request, spider, response.status) else: return response if response.status in [302, 303] and 'Location' in response.headers: redirected_url = urljoin(request.url, response.headers['location']) redirected = self._redirect_request_using_get(request, redirected_url) return self._redirect(redirected, request, spider, response.status) if response.status in [301, 307] and 'Location' in response.headers: redirected_url = urljoin(request.url, response.headers['location']) redirected = request.replace(url=redirected_url) return self._redirect(redirected, request, spider, response.status) if isinstance(response, HtmlResponse): interval, url = get_meta_refresh(response) if url and interval < self.max_metarefresh_delay: redirected = self._redirect_request_using_get(request, url) return self._redirect(redirected, request, spider, 'meta refresh') return response 

(3) Ahora puede acceder a la lista de redirecciones en su araña con

 request.meta['redirect_status'] 

response.meta['redirect_urls' se rellena con RedirectMiddleware . Su callback de la araña nunca recibirá respuestas intermedias, solo la última después de todas las redirecciones.

Si desea controlar el proceso, subclase RedirectMiddleware , deshabilite el original y habilite el suyo. Luego, puede controlar el proceso de redirección, incluido el seguimiento de los estados de respuesta.

Aquí está la implementación original (scrapy.contrib.downloadermiddleware.redirect.RedirectMiddleware):

 class RedirectMiddleware(object):  """Handle redirection of requests based on response status and meta-refresh html tag"""  def _redirect(self, redirected, request, spider, reason): ...      redirected.meta['redirect_urls'] = request.meta.get('redirect_urls', []) + \        [request.url] 

Como ves, el método _redirect que se llama desde diferentes partes crea un meta['redirect_urls']

Y en el método process_response , se return self._redirect(redirected, request, spider, response.status) , lo que significa que la respuesta original no se pasa a la spider.

Creo que está disponible como

 response.status 

Ver http://doc.scrapy.org/en/0.14/topics/request-response.html#scrapy.http.Response

Solución KISS: pensé que era mejor agregar el mínimo estricto de código para capturar el nuevo campo de redireccionamiento, y dejar que RedirectMiddleware haga el rest:

 from scrapy.contrib.downloadermiddleware.redirect import RedirectMiddleware class CustomRedirectMiddleware(RedirectMiddleware): """Handle redirection of requests based on response status and meta-refresh html tag""" def process_response(self, request, response, spider): #Get the redirect status codes request.meta.setdefault('redirect_status', []).append(response.status) response = super(CustomRedirectMiddleware, self).process_response(request, response, spider) return response 

Luego, subclasificando BaseSpider, puede acceder a redirect_status con lo siguiente:

  def parse(self, response): item = ScrapyGoogleindexItem() item['redirections'] = response.meta.get('redirect_times', 0) item['redirect_status'] = response.meta['redirect_status'] return item