python - example - cómo filtrar solicitudes duplicadas basadas en url en scrapy
web crawler linux (4)
Estoy escribiendo un rastreador para un sitio web usando scrapy con CrawlSpider.
Scrapy proporciona un filtro incorporado de solicitud de duplicados que filtra solicitudes duplicadas basadas en urls. Además, puedo filtrar las solicitudes usando las reglas miembro de CrawlSpider.
Lo que quiero hacer es filtrar solicitudes como:
http:://www.abc.com/p/xyz.html?id=1234&refer=5678
Si ya he visitado
http:://www.abc.com/p/xyz.html?id=1234&refer=4567
NOTA: referir es un parámetro que no afecta la respuesta que recibo, por lo que no me importa si el valor de ese parámetro cambia.
Ahora, si tengo un conjunto que acumula todos los identificadores , podría ignorarlo en mi función de devolución de llamada parse_item (esa es mi función de devolución de llamada) para lograr esta funcionalidad.
Pero eso significa que todavía estoy por lo menos buscando esa página, cuando no es necesario.
Entonces, ¿cuál es la forma en que puedo decirle a scrapy que no debe enviar una solicitud particular basada en la url?
Aquí está mi base de filtro personalizada en scrapy 0.24.6.
En este filtro, solo le importa el ID en la url. por ejemplo
http://www.example.com/products/cat1/1000.html?p=1
http://www.example.com/products/cat2/1000.html?p=2
se tratan como la misma url. Pero
http://www.example.com/products/cat2/all.html
no lo hará
import re
import os
from scrapy.dupefilter import RFPDupeFilter
class MyCustomURLFilter(RFPDupeFilter):
def _get_id(self, url):
m = re.search(r''(/d+)/.html'', url)
return None if m is None else m.group(1)
def request_fingerprint(self, request):
style_id = self._get_id(request.url)
return style_id
Puede escribir middleware personalizado para la eliminación duplicada y agregarlo en la configuración
import os
from scrapy.dupefilter import RFPDupeFilter
from scrapy.utils.request import request_fingerprint
class CustomFilter(RFPDupeFilter):
"""A dupe filter that considers specific ids in the url"""
def __getid(self, url):
mm = url.split("&refer")[0] #or something like that
return mm
def request_seen(self, request):
fp = self.__getid(request.url)
if fp in self.fingerprints:
return True
self.fingerprints.add(fp)
if self.file:
self.file.write(fp + os.linesep)
Luego debe configurar el DUPFILTER_CLASS correcto en settings.py
DUPEFILTER_CLASS = ''scraper.duplicate_filter.CustomFilter''
Después de eso debería de funcionar
Siguiendo el ejemplo de ytomar, escribí este filtro que filtra basado puramente en URL que ya se han visto al verificar un conjunto en memoria. Soy un novato de Python, así que avíseme si arruiné algo, pero parece funcionar bien:
from scrapy.dupefilter import RFPDupeFilter
class SeenURLFilter(RFPDupeFilter):
"""A dupe filter that considers the URL"""
def __init__(self, path=None):
self.urls_seen = set()
RFPDupeFilter.__init__(self, path)
def request_seen(self, request):
if request.url in self.urls_seen:
return True
else:
self.urls_seen.add(request.url)
Como ya mencionó, asegúrese de agregar la constante DUPEFILTER_CLASS
a settings.py
:
DUPEFILTER_CLASS = ''scraper.custom_filters.SeenURLFilter''
https://github.com/scrapinghub/scrapylib/blob/master/scrapylib/deltafetch.py
Este archivo puede ayudarte. Este archivo crea una base de datos de la clave única delta fetch desde la url, un pase de usuario en un scrapy.Reqeust (meta = {''deltafetch_key'': uniqe_url_key}). Esto le permite evitar solicitudes duplicadas que ya ha visitado en el pasado.
Una implementación de ejemplo de mongodb utilizando deltafetch.py
if isinstance(r, Request):
key = self._get_key(r)
key = key+spider.name
if self.db[''your_collection_to_store_deltafetch_key''].find_one({"_id":key}):
spider.log("Ignoring already visited: %s" % r, level=log.INFO)
continue
elif isinstance(r, BaseItem):
key = self._get_key(response.request)
key = key+spider.name
try:
self.db[''your_collection_to_store_deltafetch_key''].insert({"_id":key,"time":datetime.now()})
except:
spider.log("Ignoring already visited: %s" % key, level=log.ERROR)
yield r
p.ej. id = 345 scrapy.Request (url, meta = {deltafetch_key: 345}, callback = análisis sintáctico)