0.提出问题

Scrapyd 提供的开始和结束项目的API如下,参考 Scrapyd 改进第一步: Web Interface 添加 charset=UTF-8, 避免查看 log 出现中文乱码,准备继续在页面上进一步添加 START 和 STOP 超链接。

http://scrapyd.readthedocs.io/en/stable/api.html#schedule-json

Example request:

  1. $ curl http://localhost:6800/schedule.json -d project=myproject -d spider=somespider

Example response:

  1. {"status": "ok", "jobid": "6487ec79947edab326d6db28a2d86511e8247444"}

http://scrapyd.readthedocs.io/en/stable/api.html#cancel-json

Example request:

  1. $ curl http://localhost:6800/cancel.json -d project=myproject -d job=6487ec79947edab326d6db28a2d86511e8247444

Example response:

  1. {"status": "ok", "prevstate": "running"}

1.解决思路

尝试直接通过浏览器地址栏 GET 请求页面 http://localhost:6800/schedule.json?project=myproject&spider=somespider

返回提示需要使用 POST 请求

{"node_name": "pi-desktop", "status": "error", "message": "Expected one of [b'HEAD', b'object', b'POST']"}

那就继续通过 URL 查询对传参,通过 JS 发起 POST 异步请求吧

2.修改 Scrapyd 代码

/site-packages/scrapyd/website.py

改动位置:

(1) table 添加最后两列,分别用于 UTF-8 和 STOP/START 超链接,见红色代码

  1. def render(self, txrequest):
  2. cols = 10 ######## 8
  3. s = "<html><head><meta charset='UTF-8'><title>Scrapyd</title></head>"
  4. s += "<body>"
  5. s += "<h1>Jobs</h1>"
  6. s += "<p><a href='..'>Go back</a></p>"
  7. s += "<table border='1'>"
  8. s += "<tr><th>Project</th><th>Spider</th><th>Job</th><th>PID</th><th>Start</th><th>Runtime</th><th>Finish</th><th>Log</th>"
  9. if self.local_items:
  10. s += "<th>Items</th>"
  11. #cols = 9 ########
  12. cols += 1 ########

(2) 有两处需要添加 UTF-8 超链接,分别对应 Running 和 Finished,见红色代码

前面 Running 部分添加 UTF-8 超链接后继续添加 STOP 超链接,见蓝色代码

  1. s += "<td><a href='/logs/%s/%s/%s.log'>Log</a></td>" % (p.project, p.spider, p.job)
  2. s += "<td><a href='/logs/UTF-8.html?project=%s&spider=%s&job=%s' target='_blank'>UTF-8</a></td>" % (p.project, p.spider, p.job) ########
  3. s += "<td><a href='/logs/scrapyd.html?opt=cancel&project=%s&job_or_spider=%s' target='_blank'>STOP</a></td>" % (p.project, p.job) ########

后面 Finished 部分添加 UTF-8 超链接后继续添加 START 超链接,见蓝色代码

  1. s += "<td><a href='/logs/%s/%s/%s.log'>Log</a></td>" % (p.project, p.spider, p.job)
  2. s += "<td><a href='/logs/UTF-8.html?project=%s&spider=%s&job=%s' target='_blank'>UTF-8</a></td>" % (p.project, p.spider, p.job) ########
  3. s += "<td><a href='/logs/scrapyd.html?opt=schedule&project=%s&job_or_spider=%s' target='_blank'>START</a></td>" % (p.project, p.spider) ########

(3) 完整代码

  1. from datetime import datetime
  2.  
  3. import socket
  4.  
  5. from twisted.web import resource, static
  6. from twisted.application.service import IServiceCollection
  7.  
  8. from scrapy.utils.misc import load_object
  9.  
  10. from .interfaces import IPoller, IEggStorage, ISpiderScheduler
  11.  
  12. from six.moves.urllib.parse import urlparse
  13.  
  14. class Root(resource.Resource):
  15.  
  16. def __init__(self, config, app):
  17. resource.Resource.__init__(self)
  18. self.debug = config.getboolean('debug', False)
  19. self.runner = config.get('runner')
  20. logsdir = config.get('logs_dir')
  21. itemsdir = config.get('items_dir')
  22. local_items = itemsdir and (urlparse(itemsdir).scheme.lower() in ['', 'file'])
  23. self.app = app
  24. self.nodename = config.get('node_name', socket.gethostname())
  25. self.putChild(b'', Home(self, local_items))
  26. if logsdir:
  27. self.putChild(b'logs', static.File(logsdir.encode('ascii', 'ignore'), 'text/plain'))
  28. if local_items:
  29. self.putChild(b'items', static.File(itemsdir, 'text/plain'))
  30. self.putChild(b'jobs', Jobs(self, local_items))
  31. services = config.items('services', ())
  32. for servName, servClsName in services:
  33. servCls = load_object(servClsName)
  34. self.putChild(servName.encode('utf-8'), servCls(self))
  35. self.update_projects()
  36.  
  37. def update_projects(self):
  38. self.poller.update_projects()
  39. self.scheduler.update_projects()
  40.  
  41. @property
  42. def launcher(self):
  43. app = IServiceCollection(self.app, self.app)
  44. return app.getServiceNamed('launcher')
  45.  
  46. @property
  47. def scheduler(self):
  48. return self.app.getComponent(ISpiderScheduler)
  49.  
  50. @property
  51. def eggstorage(self):
  52. return self.app.getComponent(IEggStorage)
  53.  
  54. @property
  55. def poller(self):
  56. return self.app.getComponent(IPoller)
  57.  
  58. class Home(resource.Resource):
  59.  
  60. def __init__(self, root, local_items):
  61. resource.Resource.__init__(self)
  62. self.root = root
  63. self.local_items = local_items
  64.  
  65. def render_GET(self, txrequest):
  66. vars = {
  67. 'projects': ', '.join(self.root.scheduler.list_projects())
  68. }
  69. s = """
  70. <html>
  71. <head><meta charset='UTF-8'><title>Scrapyd</title></head>
  72. <body>
  73. <h1>Scrapyd</h1>
  74. <p>Available projects: <b>%(projects)s</b></p>
  75. <ul>
  76. <li><a href="/jobs">Jobs</a></li>
  77. """ % vars
  78. if self.local_items:
  79. s += '<li><a href="/items/">Items</a></li>'
  80. s += """
  81. <li><a href="/logs/">Logs</a></li>
  82. <li><a href="http://scrapyd.readthedocs.org/en/latest/">Documentation</a></li>
  83. </ul>
  84.  
  85. <h2>How to schedule a spider?</h2>
  86.  
  87. <p>To schedule a spider you need to use the API (this web UI is only for
  88. monitoring)</p>
  89.  
  90. <p>Example using <a href="http://curl.haxx.se/">curl</a>:</p>
  91. <p><code>curl http://localhost:6800/schedule.json -d project=default -d spider=somespider</code></p>
  92.  
  93. <p>For more information about the API, see the <a href="http://scrapyd.readthedocs.org/en/latest/">Scrapyd documentation</a></p>
  94. </body>
  95. </html>
  96. """ % vars
  97. return s.encode('utf-8')
  98.  
  99. class Jobs(resource.Resource):
  100.  
  101. def __init__(self, root, local_items):
  102. resource.Resource.__init__(self)
  103. self.root = root
  104. self.local_items = local_items
  105.  
  106. def render(self, txrequest):
  107. cols = 10 ######## 8
  108. s = "<html><head><meta charset='UTF-8'><title>Scrapyd</title></head>"
  109. s += "<body>"
  110. s += "<h1>Jobs</h1>"
  111. s += "<p><a href='..'>Go back</a></p>"
  112. s += "<table border='1'>"
  113. s += "<tr><th>Project</th><th>Spider</th><th>Job</th><th>PID</th><th>Start</th><th>Runtime</th><th>Finish</th><th>Log</th>"
  114. if self.local_items:
  115. s += "<th>Items</th>"
  116. #cols = 9 ########
  117. cols += 1 ########
  118. s += "</tr>"
  119. s += "<tr><th colspan='%s' style='background-color: #ddd'>Pending</th></tr>" % cols
  120. for project, queue in self.root.poller.queues.items():
  121. for m in queue.list():
  122. s += "<tr>"
  123. s += "<td>%s</td>" % project
  124. s += "<td>%s</td>" % str(m['name'])
  125. s += "<td>%s</td>" % str(m['_job'])
  126. s += "</tr>"
  127. s += "<tr><th colspan='%s' style='background-color: #ddd'>Running</th></tr>" % cols
  128. for p in self.root.launcher.processes.values():
  129. s += "<tr>"
  130. for a in ['project', 'spider', 'job', 'pid']:
  131. s += "<td>%s</td>" % getattr(p, a)
  132. s += "<td>%s</td>" % p.start_time.replace(microsecond=0)
  133. s += "<td>%s</td>" % (datetime.now().replace(microsecond=0) - p.start_time.replace(microsecond=0))
  134. s += "<td></td>"
  135. s += "<td><a href='/logs/%s/%s/%s.log'>Log</a></td>" % (p.project, p.spider, p.job)
  136. s += "<td><a href='/logs/UTF-8.html?project=%s&spider=%s&job=%s' target='_blank'>UTF-8</a></td>" % (p.project, p.spider, p.job) ########
  137. s += "<td><a href='/logs/scrapyd.html?opt=cancel&project=%s&job_or_spider=%s' target='_blank'>STOP</a></td>" % (p.project, p.job) ########
  138. if self.local_items:
  139. s += "<td><a href='/items/%s/%s/%s.jl'>Items</a></td>" % (p.project, p.spider, p.job)
  140. s += "</tr>"
  141. s += "<tr><th colspan='%s' style='background-color: #ddd'>Finished</th></tr>" % cols
  142. for p in self.root.launcher.finished:
  143. s += "<tr>"
  144. for a in ['project', 'spider', 'job']:
  145. s += "<td>%s</td>" % getattr(p, a)
  146. s += "<td></td>"
  147. s += "<td>%s</td>" % p.start_time.replace(microsecond=0)
  148. s += "<td>%s</td>" % (p.end_time.replace(microsecond=0) - p.start_time.replace(microsecond=0))
  149. s += "<td>%s</td>" % p.end_time.replace(microsecond=0)
  150. s += "<td><a href='/logs/%s/%s/%s.log'>Log</a></td>" % (p.project, p.spider, p.job)
  151. s += "<td><a href='/logs/UTF-8.html?project=%s&spider=%s&job=%s' target='_blank'>UTF-8</a></td>" % (p.project, p.spider, p.job) ########
  152. s += "<td><a href='/logs/scrapyd.html?opt=schedule&project=%s&job_or_spider=%s' target='_blank'>START</a></td>" % (p.project, p.spider) ########
  153. if self.local_items:
  154. s += "<td><a href='/items/%s/%s/%s.jl'>Items</a></td>" % (p.project, p.spider, p.job)
  155. s += "</tr>"
  156. s += "</table>"
  157. s += "</body>"
  158. s += "</html>"
  159.  
  160. txrequest.setHeader('Content-Type', 'text/html; charset=utf-8')
  161. txrequest.setHeader('Content-Length', len(s))
  162.  
  163. return s.encode('utf-8')

/site-packages/scrapyd/website.py

3.新建 scrapyd.html

根据 http://scrapyd.readthedocs.io/en/stable/config.html 确定 Scrapyd 所使用的 logs_dir,在该目录下添加如下文件

  1. <html>
  2. <head>
  3. <meta charset="UTF-8">
  4. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  5. <title>scrapyd</title>
  6. </head>
  7.  
  8. <body>
  9. <p>仅用于内网环境下执行 scrapyd API</p>
  10. <div id="result"></div>
  11.  
  12. <script>
  13. function parseQueryString(url) {
  14. var urlParams = {};
  15. url.replace(
  16. new RegExp("([^?=&]+)(=([^&]*))?", "g"),
  17. function($0, $1, $2, $3) {
  18. urlParams[$1] = $3;
  19. }
  20. );
  21. return urlParams;
  22. }
  23.  
  24. function curl(opt, project, job_or_spider) {
  25. console.log(opt);
  26. console.log(project);
  27. console.log(job_or_spider);
  28. var formdata = new FormData();
  29. formdata.append('project', project);
  30. if(opt == 'cancel') {
  31. formdata.append('job', job_or_spider);
  32. } else {
  33. formdata.append('spider', job_or_spider);
  34. }
  35.  
  36. var req = new XMLHttpRequest();
  37. req.onreadystatechange = function() {
  38. if (this.readyState == 4) {
  39. if (this.status == 200) {
  40. document.querySelector('#result').innerHTML = this.responseText;
  41. } else {
  42. alert('status code: ' + this.status);
  43. }
  44. } else {
  45. document.querySelector('#result').innerHTML = this.readyState;
  46. }
  47. };
  48. req.open('post', window.location.protocol+'//'+window.location.host+'/'+opt+'.json', Async = true); req.send(formdata);
  49. }
  50.  
  51. var kwargs = parseQueryString(location.search);
  52. if (kwargs.opt == 'cancel' || kwargs.opt == 'schedule') {
  53. curl(kwargs.opt, kwargs.project, kwargs.job_or_spider);
  54. }
  55. </script>
  56. </body>
  57. </html>

scrapyd.html

4.实现效果

(1) 点击 STOP 超链接

(2) 返回 Jobs 页面

(3) 点击 START 超链接

(4) 返回 Jobs 页面

Scrapyd 改进第二步: Web Interface 添加 STOP 和 START 超链接, 一键调用 Scrapyd API的更多相关文章

  1. Scrapyd 改进第一步: Web Interface 添加 charset=UTF-8, 避免查看 log 出现中文乱码

    0.问题现象和原因 如下图所示,由于 Scrapyd 的 Web Interface 的 log 链接直接指向 log 文件,Response Headers 的 Content-Type 又没有声明 ...

  2. Hive- Hive Web Interface

    当我们安装好hive时候,我们启动hive的UI界面的时候,命令: hive –-service hwi ,报错,没有war包 我们查看hive/conf/hive-default.xml.templ ...

  3. django web 中添加超链接

    django web 中添加不传参的超链接的方法如下: html: 在web中的超链接中加入一下url <a href="{% url 'app_name.views.url_func ...

  4. kubernetes Helm-chart web UI添加

    charts web ui 添加chart仓库 helm repo add cherryleo https://fileserver-1253732882.cos.ap-chongqing.myqcl ...

  5. Hive Web Interface的安装

    Hive Web Interface,简称hwi,是Hive的Web接口. 首先,安装ant,下载ant,解压,并在/etc/profile中设置: export ANT_HOME=/opt/apac ...

  6. maven 如何给web项目添加jar包依赖

      maven 如何给web项目添加jar包依赖 CreateTime--2018年4月19日19:06:21 Author:Marydon 开发工具:eclipse 1.打开pom.xml文件--& ...

  7. 通过J2EE Web工程添加Flex项目,进行BlazeDS开发

    http://www.cnblogs.com/noam/archive/2010/07/22/1782955.html 环境:Eclipse 7.5 + Flex Builder 4 plugin f ...

  8. 为Azure Web Site 添加ADFS验证支持之二 在代码里使用ADFS

    下面我们来创建一个MVC 5.0的ASP.Net程序,并且将它部署到Azure Web Site上 通过Visual Studio 2015创建Web Project 在选择ASP.net模板的地方, ...

  9. 为Azure Web Site 添加ADFS验证支持之一 设置ADFS的信任关系

    很多时候企业开发的应用都会通过AD(Active Directory)进行验证用户名密码的,在企业里面统一一个AD来进行账号密码管理也是一个很好的实践.当企业打算将一个应用迁移到Azure的时候,使用 ...

随机推荐

  1. H3C WAP712C 路由器设置

    0.做完任何设置之后都要执行保存操作,否则断电后设置会丢失! 1.默认登录参数:IP:192.168.0.50ID:adminPD:h3capadmin 2.修改默认IP地址:设备 --> 接口 ...

  2. logstash配置多入多出并互相隔离

    需求:需要利用同一logstash进程采集不同日志,输出到es的不同index,各输入输出隔离: 主要需要解决如下两个问题: 1.如何加载多个配置文件? 普通启动方式:nohup bin/logsta ...

  3. MySQL8.0.19主从环境搭建(CentOS7)

    默认情况下,复制是异步的,从站不需要永久连接以接收来自主站的更新.根据配置,您可以复制数据库中的所有数据库,所选数据库甚至选定的表. MySQL中复制的优点包括: 横向扩展解决方案 - 在多个从站之间 ...

  4. python全栈开发中级班全程笔记(第二模块、第四章(三、re 正则表达式))

    python全栈开发笔记第二模块   第四章 :常用模块(第三部分) 一.正则表达式的作用与方法 正则表达式是什么呢?一个问题带来正则表达式的重要性和作用      有一个需求 : 从文件中读取所有联 ...

  5. Httpclient发送json请求

    一.Httpclient发送json请求 public String RequestJsonPost(String url){    String strresponse = null;    try ...

  6. Linux下nc命令的使用

    nc命令的作用 实现任意TCP/UDP端口的侦听,nc可以作为server以TCP或UDP方式侦听指定端口 端口的扫描,nc可以作为client发起TCP或UDP连接 机器之间传输文件 机器之间网络测 ...

  7. 解决ssh连接linux服务器速度慢

    服务器端sshd配置文件 /etc/ssh/sshd_config 看是否有如下的两条配置条目 GSSAPIAuthentication no UseDNS no 如果前面带#,请把#删掉,或者新添加 ...

  8. 装饰器模式-Decorator(Java实现)

    装饰器模式-Decorator(Java实现) 装饰器模式允许向一个现有的对象添加新的功能, 同时又不改变其结构. 其中 "现有对象"在本文中是StringDisplay类. 添加 ...

  9. 桥接模式-Bridge(Java实现)

    桥接模式-Bridge 桥梁模式的用意是"将抽象化(Abstraction)与实现化(Implementation)脱耦, 将"类的功能层次结构" 与 "类的实 ...

  10. openssl实现自签名证书

    前言 证书的作用 加密通信数据,验证对象身份,保证数据完整性 什么是自签名证书 公认的证书往往都需要收费,如果客户端与服务端都是由我们自己来操控,那便可以使用自签名证书(说白了就是只是自己认可的证书) ...