Usando um Scrapy Spider para vários sites
-
25-09-2019 - |
Pergunta
Preciso criar um web spider/crawler configurável pelo usuário e estou pensando em usar o Scrapy.Porém, não consigo codificar os domínios e permitir o URL regex:es - isso será configurável em uma GUI.
Como faço para (o mais simples possível) criar um spider ou um conjunto de spiders com Scrapy onde os domínios e URLs regex:es permitidos são configuráveis dinamicamente?Por exemplo.Eu escrevo a configuração em um arquivo e o spider a lê de alguma forma.
Solução
AVISO:Esta resposta foi para Scrapy v0.7, a API do gerenciador de aranha mudou muito desde então.
Substitua a classe SpiderManager padrão, carregue suas regras personalizadas de um banco de dados ou de outro lugar e instancie um spider personalizado com suas próprias regras/regexes e nome de domínio
em mybot/settings.py:
SPIDER_MANAGER_CLASS = 'mybot.spidermanager.MySpiderManager'
em mybot/spidermanager.py:
from mybot.spider import MyParametrizedSpider
class MySpiderManager(object):
loaded = True
def fromdomain(self, name):
start_urls, extra_domain_names, regexes = self._get_spider_info(name)
return MyParametrizedSpider(name, start_urls, extra_domain_names, regexes)
def close_spider(self, spider):
# Put here code you want to run before spiders is closed
pass
def _get_spider_info(self, name):
# query your backend (maybe a sqldb) using `name` as primary key,
# and return start_urls, extra_domains and regexes
...
return (start_urls, extra_domains, regexes)
e agora sua classe spider personalizada, em mybot/spider.py:
from scrapy.spider import BaseSpider
class MyParametrizedSpider(BaseSpider):
def __init__(self, name, start_urls, extra_domain_names, regexes):
self.domain_name = name
self.start_urls = start_urls
self.extra_domain_names = extra_domain_names
self.regexes = regexes
def parse(self, response):
...
Notas:
- Você também pode estender o CrawlSpider se quiser aproveitar as vantagens de seu sistema de regras
- Para executar um spider, use:
./scrapy-ctl.py crawl <name>
, ondename
é passado para SpiderManager.fromdomain e é a chave para recuperar mais informações do spider do sistema backend - Como a solução substitui o SpiderManager padrão, codificar um spider clássico (um módulo python por SPIDER) não funciona, mas acho que isso não é um problema para você.Mais informações sobre o gerenciador de spiders padrão TwistedPluginSpiderManager
Outras dicas
O que você precisa é criar classes spider dinamicamente, subclassificando sua classe spider genérica favorita, conforme fornecido por scrapy
(CrawlSpider
subclasses com seu rules
adicionado, ou XmlFeedSpider
, ou qualquer outra coisa) e adicionando domain_name
, start_urls
, e possivelmente extra_domain_names
(e/ou start_requests()
, etc), conforme você os obtém ou deduz da sua GUI (ou arquivo de configuração, ou qualquer outro).
Python facilita a criação dinâmica de objetos de classe;um exemplo muito simples pode ser:
from scrapy import spider
def makespider(domain_name, start_urls,
basecls=spider.BaseSpider):
return type(domain_name + 'Spider',
(basecls,),
{'domain_name': domain_name,
'start_urls': start_urls})
allspiders = []
for domain, urls in listofdomainurlpairs:
allspiders.append(makespider(domain, urls))
Isso fornece uma lista de classes de aranhas muito básicas - você provavelmente desejará adicionar parse
métodos para eles antes de instanciá-los.Tempere a gosto...;-).
Autopromoção descarada em domo!você precisará instanciar o rastreador conforme fornecido nos exemplos, para o seu projeto.
Além disso, você precisará tornar o rastreador configurável em tempo de execução, o que simplesmente passa a configuração para o rastreador e substitui as configurações em tempo de execução, quando a configuração é alterada.
Agora é extremamente fácil configurar o scrapy para estes fins:
Sobre as primeiras URLs a serem visitadas, você pode passá-las como um atributo na chamada do spider com
-a
, e use ostart_requests
função para configurar como iniciar o spiderVocê não precisa configurar o
allowed_domains
variável para as aranhas.Se você não incluir essa variável de classe, o spider poderá permitir todos os domínios.
Deve terminar em algo como:
class MySpider(Spider):
name = "myspider"
def start_requests(self):
yield Request(self.start_url, callback=self.parse)
def parse(self, response):
...
e você deve chamá-lo com:
scrapy crawl myspider -a start_url="http://example.com"