Как фильтровать повторяющиеся запросы на основе url в scrapy
Я пишу сканер для веб-сайта, используя scrapy с CrawlSpider.
Scrapy предоставляет встроенный фильтр дубликатов запросов, который фильтрует повторяющиеся запросы на основе URL-адресов. Кроме того, я могу отфильтровать запросы, используя член правил CrawlSpider.
Что я хочу сделать, это отфильтровать запросы, например:
http:://www.abc.com/p/xyz.html?id=1234&refer=5678
Если я уже посетил
http:://www.abc.com/p/xyz.html?id=1234&refer=4567
ПРИМЕЧАНИЕ: ссылка - это параметр, который не влияет на полученный ответ, поэтому мне все равно, изменяется ли значение этого параметра.
Теперь, если у меня есть набор, который накапливает все идентификаторы, я мог бы игнорировать его в моей функции callback parse_item (что моя функция обратного вызова) для достижения этой функциональности.
Но это означало бы, что я по крайней мере получаю эту страницу, когда мне это не нужно.
Итак, каков способ, которым я могу рассказать, что он не должен отправлять конкретный запрос на основе URL?
Ответы
Ответ 1
Вы можете написать собственное промежуточное программное обеспечение для удаления дубликатов и добавить его в настройки
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)
Затем вам нужно установить правильный DUPFILTER_CLASS в settings.py
DUPEFILTER_CLASS = 'scraper.duplicate_filter.CustomFilter'
После этого он должен работать
Ответ 2
Следуя примеру ytomar, я написал этот фильтр, который фильтруется только по URL-адресам, которые уже были просмотрены путем проверки набора в памяти. Я Python noob, поэтому дайте мне знать, если я что-то прикрутил, но, похоже, все в порядке:
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)
Как упоминалось выше, не забудьте добавить константу DUPEFILTER_CLASS
в settings.py
:
DUPEFILTER_CLASS = 'scraper.custom_filters.SeenURLFilter'
Ответ 3
https://github.com/scrapinghub/scrapylib/blob/master/scrapylib/deltafetch.py
Этот файл может вам помочь. Этот файл создает базу данных уникального ключа delta fetch из url, пользовательский проход в scrapy.Reqeust(meta = {'deltafetch_key': uniqe_url_key}).
Это позволит вам избежать повторных запросов, которые вы уже посещали в прошлом.
Пример реализации mongodb с использованием 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
например. id = 345
scrapy.Request(URL, мета = {deltafetch_key: 345}, обратный вызов = синтаксический анализ)
Ответ 4
Вот моя собственная база фильтров на scrapy 0.24.6.
В этом фильтре он только заботится об идентификаторе в URL-адресе. например
http://www.example.com/products/cat1/1000.html?p=1
http://www.example.com/products/cat2/1000.html?p=2
рассматриваются как один и тот же URL. Но
http://www.example.com/products/cat2/all.html
не будет.
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