Web Service

Scrapy提供用于监控及控制运行中的爬虫的web服务(service)。服务通过 JSON-RPC 2.0 协议提供大部分的资源,不过也有些(只读)资源仅仅输出JSON数据。

Scrapy为管理Scrapy进程提供了一个可扩展的web服务。您可以通过 WEBSERVICE_ENABLED 来启用服务。服务将会监听 WEBSERVICE_PORT 的端口,并将记录写入到WEBSERVICE_LOGFILE 指定的文件中。

web服务是默认启用的 内置Scrapy扩展 ,不过如果您运行的环境内存紧张的话,也可以关闭该扩展。

Web Service资源(resources)

web service提供多种资源,定义在WEBSERVICE_RESOURCES 设置中。 每个资源提供了不同的功能。参考可用JSON-RPC对象 来查看默认可用的资源。

虽然您可以使用任何协议来实现您的资源,但有两种资源是和Scrapy绑定的:

  • Simple JSON resources - 只读,输出JSON数据
  • JSON-RPC resources - 通过使用 JSON-RPC 2.0 协议支持对一些Scrapy对象的直接访问

可用JSON-RPC对象

Scrapy默认支持以下JSON-RPC资源:

Crawler JSON-RPC资源

class scrapy.contrib.webservice.crawler.CrawlerResource

提供对主Crawler对象的访问,来控制Scrapy进程。

默认访问地址: http://localhost:6080/crawler

状态收集器(Stats Collector)JSON-RPC资源

class scrapy.contrib.webservice.stats.StatsResource

提供对crawler使用的状态收集器(Stats Collector)的访问。

默认访问地址: http://localhost:6080/stats

爬虫管理器(Spider Manager)JSON-RPC资源

您可以通过Crawler JSON-RPC资源 来访问爬虫管理器JSON-RPC资源。地址为:http://localhost:6080/crawler/spiders

扩展管理器(Extension Manager)JSON-RPC资源

您可以通过Crawler JSON-RPC资源 来访问扩展管理器JSON-RPC资源。地址为:

可用JSON资源

Scrapy默认提供下列JSON资源:

引擎状态JSON资源

class scrapy.contrib.webservice.enginestatus.EngineStatusResource

提供了对引擎状态数据的访问

默认访问地址: http://localhost:6080/enginestatus

Web服务设置

您可以通过下列选项来设置web服务:

WEBSERVICE_ENABLED

Default: True

布尔值。确定web服务是否启用(以及说该扩展是否启用)。

WEBSERVICE_LOGFILE

Default: None

记录对该web服务的http请求的文件。如果未设置,则记录将写到标准scrapy的log中。

WEBSERVICE_PORT

Default: [6080, 7030]

web服务的端口范围。如果设置为 None0 ,则使用动态分配的端口。

WEBSERVICE_HOST

Default: '127.0.0.1'

web服务监听的接口(interface)

WEBSERVICE_RESOURCES

Default: {}

您的项目所启用的web服务资源的列表(list)。请参考Web Service资源(resources) 。该列表将会添加/覆盖WEBSERVICE_RESOURCES_BASE 中定义的Scrpay默认启用的资源的值。

WEBSERVICE_RESOURCES_BASE

Default:

  1. {
  2. 'scrapy.contrib.webservice.crawler.CrawlerResource': 1,
  3. 'scrapy.contrib.webservice.enginestatus.EngineStatusResource': 1,
  4. 'scrapy.contrib.webservice.stats.StatsResource': 1,
  5. }

Scrapy默认提供的web服务资源的列表。您不应该对您的项目修改这个设置,而是修改 WEBSERVICE_RESOURCES 。如果您想要关闭某些资源,您可以在WEBSERVICE_RESOURCES 设置其的值为 None

编写web服务资源(resource)

web服务资源的实现采用了Twisted Web API。Twisted web及Twisted web资源的更多详情请参考 Twisted Web guide

编写web服务资源您需要继承 JsonResourceJsonRpcResource并实现 renderGET 方法。

class scrapy.webservice.JsonResource

twisted.web.resource.Resource 的子类,实现了一个JSON web服务资源。参考
wsname

Scrapy web服务的名字,同时也是该资源监听的路径。举个例子,假设Scrapy web服务监听 http://localhost:6080/ws_name'resource1' ,则该资源的URL为:


http://localhost:6080/resource1/


_class scrapy.webservice.JsonRpcResource(crawler, target=None)

JsonResource 的子类,实现了JSON-RPC资源。JSON-RPC资源为Python(Scrapy)对象做了一层JSON-RPC API封装。被封装的资源必须通过get_target() 方法返回。该方法默认返回构造器传入的目标(target)。
get_target()

返回JSON-RPC所封装的对象。默认情况下,返回构造器传入的对象。

web服务资源例子

StatsResource (JSON-RPC resource)

  1. from scrapy.webservice import JsonRpcResource
  2.  
  3. class StatsResource(JsonRpcResource):
  4.  
  5. ws_name = 'stats'
  6.  
  7. def __init__(self, crawler):
  8. JsonRpcResource.__init__(self, crawler, crawler.stats)

EngineStatusResource (JSON resource)

  1. from scrapy.webservice import JsonResource
  2. from scrapy.utils.engine import get_engine_status
  3.  
  4. class EngineStatusResource(JsonResource):
  5.  
  6. ws_name = 'enginestatus'
  7.  
  8. def __init__(self, crawler, spider_name=None):
  9. JsonResource.__init__(self, crawler)
  10. self._spider_name = spider_name
  11. self.isLeaf = spider_name is not None
  12.  
  13. def render_GET(self, txrequest):
  14. status = get_engine_status(self.crawler.engine)
  15. if self._spider_name is None:
  16. return status
  17. for sp, st in status['spiders'].items():
  18. if sp.name == self._spider_name:
  19. return st
  20.  
  21. def getChild(self, name, txrequest):
  22. return EngineStatusResource(name, self.crawler)

Example of web service client

scrapy-ws.py script

  1. #!/usr/bin/env python
  2. """
  3. Example script to control a Scrapy server using its JSON-RPC web service.
  4.  
  5. It only provides a reduced functionality as its main purpose is to illustrate
  6. how to write a web service client. Feel free to improve or write you own.
  7.  
  8. Also, keep in mind that the JSON-RPC API is not stable. The recommended way for
  9. controlling a Scrapy server is through the execution queue (see the "queue"
  10. command).
  11.  
  12. """
  13.  
  14. from __future__ import print_function
  15. import sys, optparse, urllib, json
  16. from urlparse import urljoin
  17.  
  18. from scrapy.utils.jsonrpc import jsonrpc_client_call, JsonRpcError
  19.  
  20. def get_commands():
  21. return {
  22. 'help': cmd_help,
  23. 'stop': cmd_stop,
  24. 'list-available': cmd_list_available,
  25. 'list-running': cmd_list_running,
  26. 'list-resources': cmd_list_resources,
  27. 'get-global-stats': cmd_get_global_stats,
  28. 'get-spider-stats': cmd_get_spider_stats,
  29. }
  30.  
  31. def cmd_help(args, opts):
  32. """help - list available commands"""
  33. print("Available commands:")
  34. for _, func in sorted(get_commands().items()):
  35. print(" ", func.__doc__)
  36.  
  37. def cmd_stop(args, opts):
  38. """stop <spider> - stop a running spider"""
  39. jsonrpc_call(opts, 'crawler/engine', 'close_spider', args[0])
  40.  
  41. def cmd_list_running(args, opts):
  42. """list-running - list running spiders"""
  43. for x in json_get(opts, 'crawler/engine/open_spiders'):
  44. print(x)
  45.  
  46. def cmd_list_available(args, opts):
  47. """list-available - list name of available spiders"""
  48. for x in jsonrpc_call(opts, 'crawler/spiders', 'list'):
  49. print(x)
  50.  
  51. def cmd_list_resources(args, opts):
  52. """list-resources - list available web service resources"""
  53. for x in json_get(opts, '')['resources']:
  54. print(x)
  55.  
  56. def cmd_get_spider_stats(args, opts):
  57. """get-spider-stats <spider> - get stats of a running spider"""
  58. stats = jsonrpc_call(opts, 'stats', 'get_stats', args[0])
  59. for name, value in stats.items():
  60. print("%-40s %s" % (name, value))
  61.  
  62. def cmd_get_global_stats(args, opts):
  63. """get-global-stats - get global stats"""
  64. stats = jsonrpc_call(opts, 'stats', 'get_stats')
  65. for name, value in stats.items():
  66. print("%-40s %s" % (name, value))
  67.  
  68. def get_wsurl(opts, path):
  69. return urljoin("http://%s:%s/"% (opts.host, opts.port), path)
  70.  
  71. def jsonrpc_call(opts, path, method, *args, **kwargs):
  72. url = get_wsurl(opts, path)
  73. return jsonrpc_client_call(url, method, *args, **kwargs)
  74.  
  75. def json_get(opts, path):
  76. url = get_wsurl(opts, path)
  77. return json.loads(urllib.urlopen(url).read())
  78.  
  79. def parse_opts():
  80. usage = "%prog [options] <command> [arg] ..."
  81. description = "Scrapy web service control script. Use '%prog help' " \
  82. "to see the list of available commands."
  83. op = optparse.OptionParser(usage=usage, description=description)
  84. op.add_option("-H", dest="host", default="localhost", \
  85. help="Scrapy host to connect to")
  86. op.add_option("-P", dest="port", type="int", default=6080, \
  87. help="Scrapy port to connect to")
  88. opts, args = op.parse_args()
  89. if not args:
  90. op.print_help()
  91. sys.exit(2)
  92. cmdname, cmdargs, opts = args[0], args[1:], opts
  93. commands = get_commands()
  94. if cmdname not in commands:
  95. sys.stderr.write("Unknown command: %s\n\n" % cmdname)
  96. cmd_help(None, None)
  97. sys.exit(1)
  98. return commands[cmdname], cmdargs, opts
  99.  
  100. def main():
  101. cmd, args, opts = parse_opts()
  102. try:
  103. cmd(args, opts)
  104. except IndexError:
  105. print(cmd.__doc__)
  106. except JsonRpcError as e:
  107. print(str(e))
  108. if e.data:
  109. print("Server Traceback below:")
  110. print(e.data)
  111.  
  112.  
  113. if __name__ == '__main__':
  114. main()