scrapyd несколько пауков, записывающих элементы в один и тот же файл

У меня есть сервер scrapyd с несколькими пауками, работающими одновременно, я запускаю пауков один за другим, используя конечную точку schedule.json. Все пауки записывают содержимое в общий файл, используя конвейер

class JsonWriterPipeline(object):

def __init__(self, json_filename):
    # self.json_filepath = json_filepath
    self.json_filename = json_filename
    self.file = open(self.json_filename, 'wb')

@classmethod
def from_crawler(cls, crawler):
    save_path='/tmp/'
    json_filename=crawler.settings.get('json_filename', 'FM_raw_export.json')
    completeName = os.path.join(save_path, json_filename) 
    return cls(
        completeName
    )

def process_item(self, item, spider):
    line = json.dumps(dict(item)) + "\n"
    self.file.write(line)
    return item

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

Я также заметил, что только один паук одновременно пишет в файл.


person silvestrelosada    schedule 23.03.2016    source источник


Ответы (1)


Я не вижу веских причин делать то, что вы делаете :) Вы можете изменить настройку json_filename, задав аргументы в своем запросе schedule.json в scrapyd. Затем вы можете заставить каждого паука генерировать немного разные файлы, которые вы объединяете при постобработке или во время запроса. Вы также можете писать файлы JSON, аналогичные вашим, просто установив значение FEED_URI (пример). Если вы записываете в один файл одновременно из нескольких процессов (особенно при открытии в режиме 'wb'), вы ищете поврежденные данные.

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

Немного лучше поняв, что вам нужно — в данном случае — scrapyd запускает несколько обходов, запуская разных пауков, каждый из которых сканирует разные веб-сайты. Процесс-потребитель постоянно отслеживает один файл.

Есть несколько решений, в том числе:

  • именованные каналы

Относительно прост в реализации и подходит только для очень маленьких элементов (см. здесь)

  • RabbitMQ или какой-либо другой механизм очередей

Отличное решение, но может быть немного излишним

  • База данных, например. Решение на основе SQLite

Красивый и простой, но, вероятно, требует некоторого кодирования (пользовательский потребитель)

  • Хорошее решение для мониторинга файловой системы на основе inotifywait или другого

Красиво и, вероятно, легко реализовать

Последний вариант мне кажется наиболее привлекательным. Когда scrapy crawl завершится (spider_closed signal), переместитесь, скопируйте или создайте программную ссылку для файла FEED_URL в каталог, который вы отслеживаете с помощью сценария, например это. mv или ln - это атомарная операция unix, поэтому все должно быть в порядке. Взломайте сценарий, чтобы добавить новый файл в ваш файл tmp, который вы один раз передаете своей потребительской программе.

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

На extensions.py в том же каталоге, что и settings.py:

from scrapy import signals
from scrapy.exceptions import NotConfigured

class MoveFileOnCloseExtension(object):

    def __init__(self, feed_uri):
        self.feed_uri = feed_uri

    @classmethod
    def from_crawler(cls, crawler):
        # instantiate the extension object
        feed_uri = crawler.settings.get('FEED_URI')
        ext = cls(feed_uri)

        crawler.signals.connect(ext.spider_closed, signal=signals.spider_closed)

        # return the extension object
        return ext

    def spider_closed(self, spider):
        # Move the file to the proper location
        # os.rename(self.feed_uri, ... destination path...)

На вашем settings.py:

EXTENSIONS = {
    'myproject.extensions.MoveFileOnCloseExtension': 500,
}
person neverlastn    schedule 24.03.2016
comment
Спасибо за Ваш ответ. Я думаю, что плохо понимаю, как работают пайплайны в scrapy. Насколько я понимаю, пауки генерируют элементы, элементы всех пауков передаются в конвейер, который является глобальным для всех них, и обрабатывают их. Из вашего ответа я предполагаю, что у каждого паука есть свой собственный конвейер, и есть несколько конвейеров, работающих с одним и тем же файлом, и это вызывает проблемы. Я прав? . Я делаю это таким образом, потому что есть другой процесс, который делает некоторые вещи с содержимым и принимает только один файл. - person silvestrelosada; 25.03.2016
comment
Формулировка не на 100% точна, но то, что вы говорите, верно. В частности, scrapyd запускает несколько процессов scrapy параллельно. Каждый процесс scrapy запускает паука и ваш конвейер. Это означает, что во время обхода вы будете открывать один и тот же файл для записи из многих процессов, что приведет к повреждению. ... принимает только один файл. Я уверен, что есть лучший способ сделать это. Можете ли вы запустить scrapyd, получить, например. Из него 100 файлов, соедините их в один, а затем скормите этому к вашему процессу? Это куда более распространено. - person neverlastn; 26.03.2016
comment
Знаете ли вы какой-нибудь вариант, чтобы поместить результат (элементы) всех пауков в один и тот же файл, используя только отрывки? Спасибо - person silvestrelosada; 28.03.2016
comment
Спасибо, я отправлю письмо, вы можете удалить его. - person silvestrelosada; 28.03.2016
comment
Спасибо за обновленный ответ. Мне нравится то, что вы предлагаете, я соглашусь с вашим предложением или кроличьим решением. Кролик имеет еще одно преимущество: на случай, если мне нужно будет разместить пауков на другом сервере, для масштабирования предложений это легко сделать, и я мог бы это сделать в будущем. Но в любом случае я буду использовать расширения для простоты или кролика для надежности и масштабируемости. - person silvestrelosada; 29.03.2016
comment
Потрясающий! :) Для лучшей производительности с RabbitMQ убедитесь, что вы публикуете сообщения асинхронно (пример - если вы используете механизм подтверждения - метод on_delivery_confirmation()) - person neverlastn; 29.03.2016