scrapyd 多个蜘蛛将项目写入同一个文件
scrapyd multiple spiders writing items to same file
我的 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 并且蜘蛛可以正常工作,但是抓取的内容不会反映在公共文件中.蜘蛛程序似乎运行良好,但是管道的工作并不顺利,并且没有将数据收集到公共文件中。
我还注意到只有一个蜘蛛在同一时间写入文件。
我看不出有什么好的理由去做你所做的事情 :) 你可以通过在你的 scrapyd schedule.json
请求上设置参数来更改 json_filename
设置。然后你可以让每个蜘蛛生成稍微不同的文件,你用 post-processing 或在查询时合并这些文件。您还可以通过设置 FEED_URI
值 (example) 来编写与现有文件类似的 JSON 文件。如果您同时从多个进程写入单个文件(尤其是当您使用 'wb'
模式打开时),您正在寻找损坏的数据。
编辑:
在更好地了解您的需求之后 - 在这种情况下 - 它是 scrapyd 开始多次抓取 运行 不同的蜘蛛,每个蜘蛛抓取不同的网站。消费者进程正在连续监视单个文件。
有几种解决方案,包括:
- 命名管道
相对容易实施,仅适用于非常小的物品 (see here)
- RabbitMQ 或其他一些队列机制
很好的解决方案,但可能有点矫枉过正
- 一个数据库,例如基于 SQLite 的解决方案
很好很简单,但可能需要一些编码(自定义消费者)
- 一个很好的基于
inotifywait
或其他文件系统监控的解决方案
不错,而且很容易实现
最后一个对我来说似乎是最有吸引力的选择。当 scrapy crawl
完成时(spider_closed signal), move, copy or create a soft link for the FEED_URL
file to a directory that you monitor with a script like this。mv
或 ln
是一个原子 unix 操作,所以你应该没问题。破解脚本以将新文件附加到你的 tmp
文件,您将其提供给您的消费者程序一次。
通过这种方式,您可以使用默认的提要导出器来编写您的文件。最终解决方案非常简单,您不需要管道。一个简单的扩展应该符合要求。
在与 settings.py
相同目录中的 extensions.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,
}
我的 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 并且蜘蛛可以正常工作,但是抓取的内容不会反映在公共文件中.蜘蛛程序似乎运行良好,但是管道的工作并不顺利,并且没有将数据收集到公共文件中。
我还注意到只有一个蜘蛛在同一时间写入文件。
我看不出有什么好的理由去做你所做的事情 :) 你可以通过在你的 scrapyd schedule.json
请求上设置参数来更改 json_filename
设置。然后你可以让每个蜘蛛生成稍微不同的文件,你用 post-processing 或在查询时合并这些文件。您还可以通过设置 FEED_URI
值 (example) 来编写与现有文件类似的 JSON 文件。如果您同时从多个进程写入单个文件(尤其是当您使用 'wb'
模式打开时),您正在寻找损坏的数据。
编辑:
在更好地了解您的需求之后 - 在这种情况下 - 它是 scrapyd 开始多次抓取 运行 不同的蜘蛛,每个蜘蛛抓取不同的网站。消费者进程正在连续监视单个文件。
有几种解决方案,包括:
- 命名管道
相对容易实施,仅适用于非常小的物品 (see here)
- RabbitMQ 或其他一些队列机制
很好的解决方案,但可能有点矫枉过正
- 一个数据库,例如基于 SQLite 的解决方案
很好很简单,但可能需要一些编码(自定义消费者)
- 一个很好的基于
inotifywait
或其他文件系统监控的解决方案
不错,而且很容易实现
最后一个对我来说似乎是最有吸引力的选择。当 scrapy crawl
完成时(spider_closed signal), move, copy or create a soft link for the FEED_URL
file to a directory that you monitor with a script like this。mv
或 ln
是一个原子 unix 操作,所以你应该没问题。破解脚本以将新文件附加到你的 tmp
文件,您将其提供给您的消费者程序一次。
通过这种方式,您可以使用默认的提要导出器来编写您的文件。最终解决方案非常简单,您不需要管道。一个简单的扩展应该符合要求。
在与 settings.py
相同目录中的 extensions.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,
}