Передача аргументов в allowed_domains в Scrapy

Я создаю краулер, который принимает вводимые пользователем данные и просматривает все ссылки на сайте. Однако мне нужно ограничить сканирование и извлечение ссылок только на ссылки из этого домена, а не на внешние домены. Я добрался туда, где мне нужно, с точки зрения краулера. Моя проблема в том, что для моей функции allowed_domains я, похоже, не могу передать параметр scrapy, введенный с помощью команды. Bellow - это первый запускаемый скрипт:

# First Script
import os

def userInput():
    user_input = raw_input("Please enter URL. Please do not include http://: ")
    os.system("scrapy runspider -a user_input='http://" + user_input + "' crawler_prod.py")

userInput()

Сценарий, который он запускает, является поисковым роботом, и он будет сканировать указанный домен. Вот код сканера:

#Crawler
from scrapy.contrib.spiders import CrawlSpider, Rule
from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor
from scrapy.selector import HtmlXPathSelector
from scrapy.item import Item
from scrapy.spider import BaseSpider
from scrapy import Request
from scrapy.http import Request

class InputSpider(CrawlSpider):
        name = "Input"
        #allowed_domains = ["example.com"]

        def allowed_domains(self):
            self.allowed_domains = user_input

        def start_requests(self):
            yield Request(url=self.user_input)

        rules = [
        Rule(SgmlLinkExtractor(allow=()), follow=True, callback='parse_item')
        ]

        def parse_item(self, response):
            x = HtmlXPathSelector(response)
            filename = "output.txt"
            open(filename, 'ab').write(response.url + "\n")

Я попытался передать запрос, отправленный через команду терминала, однако это привело к сбою поискового робота. Как у меня сейчас тоже краулер вылетает. Я также пробовал просто вставить allowed_domains=[user_input], и он сообщает мне, что он не определен. Я играю с библиотекой запросов от Scrapy, чтобы заставить ее работать, но безуспешно. Есть ли лучший способ ограничить сканирование за пределами данного домена?

Редактировать:

Вот мой новый код:

from scrapy.contrib.spiders import CrawlSpider, Rule
from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor
from scrapy.selector import HtmlXPathSelector
from scrapy.item import Item
from scrapy.spiders import BaseSpider
from scrapy import Request
from scrapy.http import Request
from scrapy.utils.httpobj import urlparse
#from run_first import *

class InputSpider(CrawlSpider):
        name = "Input"
        #allowed_domains = ["example.com"]

        #def allowed_domains(self):
            #self.allowed_domains = user_input

        #def start_requests(self):
            #yield Request(url=self.user_input)

        def __init__(self, *args, **kwargs):
            inputs = kwargs.get('urls', '').split(',') or []
            self.allowed_domains = [urlparse(d).netloc for d in inputs]
            # self.start_urls = [urlparse(c).netloc for c in inputs] # For start_urls

        rules = [
        Rule(SgmlLinkExtractor(allow=()), follow=True, callback='parse_item')
        ]

        def parse_item(self, response):
            x = HtmlXPathSelector(response)
            filename = "output.txt"
            open(filename, 'ab').write(response.url + "\n")

Это выходной журнал для нового кода

2017-04-18 18:18:01 [scrapy] INFO: Scrapy 1.0.3 started (bot: scrapybot)
2017-04-18 18:18:01 [scrapy] INFO: Optional features available: ssl, http11, boto
2017-04-18 18:18:01 [scrapy] INFO: Overridden settings: {'LOG_FILE': 'output.log'}
2017-04-18 18:18:43 [scrapy] INFO: Scrapy 1.0.3 started (bot: scrapybot)
2017-04-18 18:18:43 [scrapy] INFO: Optional features available: ssl, http11, boto
2017-04-18 18:18:43 [scrapy] INFO: Overridden settings: {'LOG_FILE': 'output.log'}
2017-04-18 18:18:43 [py.warnings] WARNING: /home/****-you/Python_Projects/Network-Multitool/crawler/crawler_prod.py:1: ScrapyDeprecationWarning: Module `scrapy.contrib.spiders` is deprecated, use `scrapy.spiders` instead
  from scrapy.contrib.spiders import CrawlSpider, Rule

2017-04-18 18:18:43 [py.warnings] WARNING: /home/****-you/Python_Projects/Network-Multitool/crawler/crawler_prod.py:2: ScrapyDeprecationWarning: Module `scrapy.contrib.linkextractors` is deprecated, use `scrapy.linkextractors` instead
  from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor

2017-04-18 18:18:43 [py.warnings] WARNING: /home/****-you/Python_Projects/Network-Multitool/crawler/crawler_prod.py:2: ScrapyDeprecationWarning: Module `scrapy.contrib.linkextractors.sgml` is deprecated, use `scrapy.linkextractors.sgml` instead
  from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor

2017-04-18 18:18:43 [py.warnings] WARNING: /home/****-you/Python_Projects/Network-Multitool/crawler/crawler_prod.py:27: ScrapyDeprecationWarning: SgmlLinkExtractor is deprecated and will be removed in future releases. Please use scrapy.linkextractors.LinkExtractor
  Rule(SgmlLinkExtractor(allow=()), follow=True, callback='parse_item')

2017-04-18 18:18:43 [scrapy] INFO: Enabled extensions: CloseSpider, TelnetConsole, LogStats, CoreStats, SpiderState
2017-04-18 18:18:43 [boto] DEBUG: Retrieving credentials from metadata server.
2017-04-18 18:18:44 [boto] ERROR: Caught exception reading instance data
Traceback (most recent call last):
  File "/usr/lib/python2.7/dist-packages/boto/utils.py", line 210, in retry_url
    r = opener.open(req, timeout=timeout)
  File "/usr/lib/python2.7/urllib2.py", line 429, in open
    response = self._open(req, data)
  File "/usr/lib/python2.7/urllib2.py", line 447, in _open
    '_open', req)
  File "/usr/lib/python2.7/urllib2.py", line 407, in _call_chain
    result = func(*args)
  File "/usr/lib/python2.7/urllib2.py", line 1228, in http_open
    return self.do_open(httplib.HTTPConnection, req)
  File "/usr/lib/python2.7/urllib2.py", line 1198, in do_open
    raise URLError(err)
URLError: <urlopen error timed out>
2017-04-18 18:18:44 [boto] ERROR: Unable to read instance data, giving up
2017-04-18 18:18:44 [scrapy] INFO: Enabled downloader middlewares: HttpAuthMiddleware, DownloadTimeoutMiddleware, UserAgentMiddleware, RetryMiddleware, DefaultHeadersMiddleware, MetaRefreshMiddleware, HttpCompressionMiddleware, RedirectMiddleware, CookiesMiddleware, ChunkedTransferMiddleware, DownloaderStats
2017-04-18 18:18:44 [scrapy] INFO: Enabled spider middlewares: HttpErrorMiddleware, OffsiteMiddleware, RefererMiddleware, UrlLengthMiddleware, DepthMiddleware
2017-04-18 18:18:44 [scrapy] INFO: Enabled item pipelines: 
2017-04-18 18:18:44 [scrapy] INFO: Spider opened
2017-04-18 18:18:44 [scrapy] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2017-04-18 18:18:44 [scrapy] DEBUG: Telnet console listening on 127.0.0.1:6023
2017-04-18 18:18:44 [scrapy] ERROR: Error while obtaining start requests
Traceback (most recent call last):
  File "/usr/lib/python2.7/dist-packages/scrapy/core/engine.py", line 110, in _next_request
    request = next(slot.start_requests)
  File "/usr/lib/python2.7/dist-packages/scrapy/spiders/__init__.py", line 70, in start_requests
    yield self.make_requests_from_url(url)
  File "/usr/lib/python2.7/dist-packages/scrapy/spiders/__init__.py", line 73, in make_requests_from_url
    return Request(url, dont_filter=True)
  File "/usr/lib/python2.7/dist-packages/scrapy/http/request/__init__.py", line 24, in __init__
    self._set_url(url)
  File "/usr/lib/python2.7/dist-packages/scrapy/http/request/__init__.py", line 59, in _set_url
    raise ValueError('Missing scheme in request url: %s' % self._url)
ValueError: Missing scheme in request url: 
2017-04-18 18:18:44 [scrapy] INFO: Closing spider (finished)
2017-04-18 18:18:44 [scrapy] INFO: Dumping Scrapy stats:
{'finish_reason': 'finished',
 'finish_time': datetime.datetime(2017, 4, 18, 22, 18, 44, 794155),
 'log_count/DEBUG': 2,
 'log_count/ERROR': 3,
 'log_count/INFO': 7,
 'start_time': datetime.datetime(2017, 4, 18, 22, 18, 44, 790331)}
2017-04-18 18:18:44 [scrapy] INFO: Spider closed (finished)

Редактировать:

Я смог найти ответ на мою проблему, просмотрев ответы и перечитав документы. Ниже показано, что я добавил в сценарий краулера, чтобы он заработал.

def __init__(self, url=None, *args, **kwargs):
    super(InputSpider, self).__init__(*args, **kwargs)
    self.allowed_domains = [url]
    self.start_urls = ["http://" + url]

person George    schedule 11.04.2017    source источник
comment
Я предлагаю вам изучить этот тестовый паук: он устанавливает allowed_domains из входного аргумента. Это не CrawlSpider, но вы, вероятно, можете использовать его в качестве основы для своего варианта использования.   -  person paul trmbrth    schedule 11.04.2017


Ответы (1)


Здесь есть кое-что, чего вам не хватает.

  1. Первые запросы, поступающие с start_urls, не фильтруются.
  2. Вы не можете отменить allowed_domains после запуска цикла.

Чтобы справиться с этими проблемами, вам необходимо написать свое собственное межплатформенное программное обеспечение или, по крайней мере, изменить существующее с учетом необходимых вам изменений.

Происходит OffsiteMiddleware, который обрабатывает allowed_domains, преобразует allowed_domains значение в строку регулярных выражений после того, как паук открывается, а затем этот параметр больше никогда не используется.

Добавьте к себе что-то вроде этого middlewares.py:

from scrapy.spidermiddlewares.offsite import OffsiteMiddleware
from scrapy.utils.httpobj import urlparse_cached
class MyOffsiteMiddleware(OffsiteMiddleware):

    def should_follow(self, request, spider):
        """Return bool whether to follow a request"""
        # hostname can be None for wrong urls (like javascript links)
        host = urlparse_cached(request).hostname or ''
        if host in spider.allowed_domains:
            return True
        return False

Активируйте его в setting.py:

SPIDER_MIDDLEWARES = {
    # enable our middleware
    'myspider.middlewares.MyOffsiteMiddleware': 500,
    # disable old middleware
    'scrapy.spidermiddlewares.offsite.OffsiteMiddleware': None, 

}

Теперь ваш паук должен следовать за всем, что у вас есть в allowed_domains, даже если вы измените его в середине.

Изменить: для вашего случая:

from scrapy.utils.httpobj import urlparse
class MySpider(Spider):
    def __init__(self, *args, **kwargs):
        input = kwargs.get('urls', '').split(',') or []
        self.allowed_domains = [urlparse(d).netloc for d in input]

И теперь можно запускать:

scrapy crawl myspider -a "urls=foo.com,bar.com"
person Granitosaurus    schedule 11.04.2017
comment
Похоже, это хорошее решение для добавления значения allowed_domains перед вызовом команды scrapy в терминале. Моя проблема в том, что я настраиваю его так, чтобы пользователь мог войти в домен, и этот домен будет сканироваться. Моя проблема заключается в передаче как начального URL-адреса, так и allowed_domain в скрипт scrapy. Итак, я ввел начальный URL-адрес scrapy runspider -a user_input="http://quotes.toscrape.com" crawler.py. У меня просто проблемы с ограничением разрешенных доменов доменом, введенным пользователем, и не переходить к каким-либо внешним ссылкам. - person George; 11.04.2017
comment
@George Вы можете просто изменить self.allowed_domains, чтобы он содержал netloc этих URL. Смотрите мою правку. - person Granitosaurus; 11.04.2017
comment
Я играю с этим сейчас, запускаю команду scrapy runspider crawler_prod.py -a urls="http://quotes.toscrape.com, quotes.toscrape.com" и добавляю вашу правку выше. Я все еще играю с ним, но получаю URLError: <urlopen error timed out> ошибки. Какие-либо предложения? - person George; 18.04.2017
comment
@ Джордж, не могли бы вы опубликовать журнал сканирования? Без контекста сложно сказать, что означает эта ошибка. Для вывода журнала вы можете использовать scrapy crawl spider -s LOG_FILE=output.log или scrapy crawl spider &> output.log - person Granitosaurus; 18.04.2017
comment
Извините за все правки. Я отредактировал свой вопрос выше, чтобы отразить журналы и новый код. - person George; 19.04.2017
comment
@George похоже, что вы где-то используете пакет boto для Amazon, и он не может подключиться. Эта проблема не связана с текущим вопросом, но вы можете проверить этот ответ: stackoverflow.com/questions/31048130/ - person Granitosaurus; 19.04.2017
comment
Я действительно понял это. Я опубликую свои правки выше, но вы указали мне правильное направление. Большое спасибо за Вашу помощь! - person George; 19.04.2017