J'écris un robot pour un site Web utilisant scrapy avec CrawlSpider.
Scrapy fournit un filtre de demande de doublon intégré qui filtre les demandes en double en fonction des URL. En outre, je peux filtrer les demandes en utilisant les règles membre de CrawlSpider.
Ce que je veux faire, c'est filtrer les demandes comme:
http:://www.abc.com/p/xyz.html?id=1234&refer=5678
Si j'ai déjà visité
http:://www.abc.com/p/xyz.html?id=1234&refer=4567
REMARQUE: refer est un paramètre qui n'affecte pas la réponse que j'obtiens, donc je me fiche que la valeur de ce paramètre change.
Maintenant, si j'ai un ensemble qui accumule tous ids je pourrais l'ignorer dans ma fonction de rappel parse_item (c'est ma fonction de rappel) pour obtenir cette fonctionnalité.
Mais cela signifierait que je suis encore au moins en train de récupérer cette page, quand je n'en ai pas besoin.
Alors, quelle est la façon dont je peux dire à scrapy qu'il ne doit pas envoyer une demande particulière basée sur l'URL?
Vous pouvez écrire un middleware personnalisé pour la suppression des doublons et l'ajouter dans les paramètres
import os
from scrapy.dupefilter import RFPDupeFilter
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)
Ensuite, vous devez définir la DUPFILTER_CLASS correcte dans settings.py
DUPEFILTER_CLASS = 'scraper.duplicate_filter.CustomFilter'
Il devrait marcher après ça
En suivant l'exemple d'ytomar, j'ai écrit ce filtre qui filtre uniquement en fonction des URL qui ont déjà été vues en vérifiant un ensemble en mémoire. Je suis un Python noob alors faites-moi savoir si j'ai foiré quelque chose, mais cela semble fonctionner correctement:
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)
Comme ytomar l'a mentionné, assurez-vous d'ajouter le DUPEFILTER_CLASS
constant à settings.py
:
DUPEFILTER_CLASS = 'scraper.custom_filters.SeenURLFilter'
https://github.com/scrapinghub/scrapylib/blob/master/scrapylib/deltafetch.py
Ce fichier pourrait vous aider. Ce fichier crée une base de données de clé d'extraction delta unique à partir de l'URL, un utilisateur passe dans un scrapy.Reqeust (meta = {'deltafetch_key': uniqe_url_key}). Cela vous permet d'éviter les demandes en double que vous avez déjà visitées dans le passé.
Un exemple d'implémentation de mongodb utilisant 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
par exemple. id = 345 scrapy.Request (url, meta = {deltafetch_key: 345}, callback = parse)
Voici ma base de filtre personnalisée sur scrapy 0.24.6.
Dans ce filtre, il ne se soucie que de l'identifiant dans l'url. par exemple
http://www.example.com/products/cat1/1000.html?p=1
http://www.example.com/products/cat2/1000.html?p=2
sont traités comme la même URL. Mais
http://www.example.com/products/cat2/all.html
ne fera pas.
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
Dans la dernière version, nous pouvons utiliser le filtre de duplication par défaut ou l'étendre et en avoir un personnalisé.
définir la configuration ci-dessous dans les paramètres d'araignée
DUPEFILTER_CLASS = 'scrapy.dupefilters.BaseDupeFilter'