0.问题现象和原因

如下图所示,由于 Scrapyd 的 Web Interface 的 log 链接直接指向 log 文件,Response Headers 的 Content-Type 又没有声明字符集 charset=UTF-8,因此通过浏览器查看 log 会出现非 ASCII 乱码。

1.解决思路

(1) 如下图所示,在 Jobs 页面添加带有项目信息的 UTF-8 超链接,如 http://127.0.0.1:6800/logs/UTF-8.html?project=proxy&spider=test&job=cd2cc82a87f111e8ac72b827ebc33e0b

(2) 在 Scrapyd 的 logs 目录新建 UTF-8.html,通过 <meta charset="UTF-8"> 声明编码

(3) 新页面打开超链接后,通过 JS 获取 url 查询对,然后更新  UTF-8.html 页面的 iframe 的 src 属性,如 <iframe src="/logs/proxy/test/9a7ee91287f111e8ac72b827ebc33e0b.log" width="100%" height="100%"></iframe>

(4) 浏览器自动加载 iframe 获取 log 文件

2.修改 Scrapyd 代码

/site-packages/scrapyd/website.py

改动位置:

(1) table 添加最后一列,见红色代码

  1. def render(self, txrequest):
  2. cols = 9 ######## 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,见红色代码

  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) 完整代码:

  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 = 9 ######## 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. if self.local_items:
  138. s += "<td><a href='/items/%s/%s/%s.jl'>Items</a></td>" % (p.project, p.spider, p.job)
  139. s += "</tr>"
  140. s += "<tr><th colspan='%s' style='background-color: #ddd'>Finished</th></tr>" % cols
  141. for p in self.root.launcher.finished:
  142. s += "<tr>"
  143. for a in ['project', 'spider', 'job']:
  144. s += "<td>%s</td>" % getattr(p, a)
  145. s += "<td></td>"
  146. s += "<td>%s</td>" % p.start_time.replace(microsecond=0)
  147. s += "<td>%s</td>" % (p.end_time.replace(microsecond=0) - p.start_time.replace(microsecond=0))
  148. s += "<td>%s</td>" % p.end_time.replace(microsecond=0)
  149. s += "<td><a href='/logs/%s/%s/%s.log'>Log</a></td>" % (p.project, p.spider, p.job)
  150. 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) ########
  151. if self.local_items:
  152. s += "<td><a href='/items/%s/%s/%s.jl'>Items</a></td>" % (p.project, p.spider, p.job)
  153. s += "</tr>"
  154. s += "</table>"
  155. s += "</body>"
  156. s += "</html>"
  157.  
  158. txrequest.setHeader('Content-Type', 'text/html; charset=utf-8')
  159. txrequest.setHeader('Content-Length', len(s))
  160.  
  161. return s.encode('utf-8')

3.新建 UTF-8.html 页面

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

  1. <html>
  2. <head><meta charset="UTF-8"></head>
  3. <iframe src="" width="100%" height="100%"></iframe>
  4.  
  5. <script>
  6. function parseQueryString(url) {
  7. var urlParams = {};
  8. url.replace(
  9. new RegExp("([^?=&]+)(=([^&]*))?", "g"),
  10. function($0, $1, $2, $3) {
  11. urlParams[$1] = $3;
  12. }
  13. );
  14. return urlParams;
  15. }
  16.  
  17. var kwargs = parseQueryString(location.search);
  18. document.querySelector('iframe').src = "/logs/" + kwargs.project + '/' + kwargs.spider + '/' + kwargs.job + '.log'
  19. </script>
  20.  
  21. <html>

4.实现效果

Scrapyd 改进第一步: Web Interface 添加 charset=UTF-8, 避免查看 log 出现中文乱码的更多相关文章

  1. Scrapyd 改进第二步: Web Interface 添加 STOP 和 START 超链接, 一键调用 Scrapyd API

    0.提出问题 Scrapyd 提供的开始和结束项目的API如下,参考 Scrapyd 改进第一步: Web Interface 添加 charset=UTF-8, 避免查看 log 出现中文乱码,准备 ...

  2. zabbix客户端的安装、zabbix主被动模式、添加主机模板等、处理页面的中文乱码

    1.zabbix客户端的安装: 如下步骤: wget repo.zabbix.com/zabbix/3.2/rhel/7/x86_64/zabbix-release-3.2-1.el7.noarch. ...

  3. 关于web.xml中配置Spring字符编码过滤器以解决中文乱码的问题

    当出现中文乱码问题,Spring中可以利用CharacterEncodingFilter过滤器解决,如下代码所示: <!-- Spring字符编码过滤器:解决中文乱码问题 --> < ...

  4. form 表单添加 enctype ="multipart/form-data" 属性后后台接收中文乱码

    解决办法: new String( request.getParameter("title").getBytes("ISO-8859-1"),"utf ...

  5. 第一讲 从头开始做一个web qq 机器人,第一步获取smart qq二维码

    新手教程: 前言:最近在看了一下很久很久以前做的qq机器人失效了,最近也在换工作目前还在职,时间很挺宽裕的.就决定从新搞一个web qq机器人 PC的协议解析出来有点费时间以后再做. 准备工作: 编译 ...

  6. 零代码第一步,做个添加数据的服务先。node.js + mysql

    node.js + mysql 实现数据添加的功能.万事基于服务! 增删改查之添加数据. 优点:只需要设置一个json文件,就可以实现基本的添加功能,可以视为是零代码. 添加数据的服务实现的功能: 1 ...

  7. Blazor client-side + webapi (.net core 3.1) 添加jwt验证流程(非host)第一步

    第一步,设置并检查CROS跨域请求 因为我们并不打算将Blazor 由webapi来进行host,所以Blazor和api将是两个域名,这样操作即方便以后单独使用Blazor来写前端,但后端采用已有或 ...

  8. linux系统性能调优第一步——性能分析(vmstat)

    linux系统性能调优第一步--性能分析(vmstat) 分类: LINUX 性能调优的第一步是性能分析,下面从性能分析着手进行一些介绍,尤其对linux性能分析工具vmstat的用法和实践进行详细介 ...

  9. [EntLib]微软企业库5.0 学习之路——第一步、基本入门

    话说在大学的时候帮老师做项目的时候就已经接触过企业库了但是当初一直没明白为什么要用这个,只觉得好麻烦啊,竟然有那么多的乱七八糟的配置(原来我不知道有配置工具可以进行配置,请原谅我的小白). 直到去年在 ...

随机推荐

  1. flink部署操作-flink standalone集群安装部署

    flink集群安装部署 standalone集群模式 必须依赖 必须的软件 JAVA_HOME配置 flink安装 配置flink 启动flink 添加Jobmanager/taskmanager 实 ...

  2. (N叉树 DFS 递归 BFS) leetcode 559. Maximum Depth of N-ary Tree

    Given a n-ary tree, find its maximum depth. The maximum depth is the number of nodes along the longe ...

  3. sass补充(2019-3-9)

    @each 输出 格式: @each $var in value,value1,value2{ } eg: @each $var1 in 100px,200px,300px{ .box{ width: ...

  4. js根据毫米/厘米算像素px

    <html><meta http-equiv="content-type" content="text/html;charset=utf-8" ...

  5. java 中使用RSA非对称性加密解密

    需要引入的jar包:bcprov-jdk15on-161.jar 下载地址:https://www.bouncycastle.org/latest_releases.html //公钥加密 publi ...

  6. go语言基础知识笔记(二)之数组和切片

    数组和切片知识用的也是比较多,的给我们工作带来很大的便利 (一) 数组 定义:在golang中数组的长度是不可变,数组存放要求是同一种数据类型 //golang中数组定义的四种方法1.先声明,后赋值 ...

  7. Redis集群管理

    1.简介 Redis在生产环境中一般是通过集群的方式进行运行,Redis集群包括主从复制集群和数据分片集群两种类型. *主从复制集群提供高可用性,而数据分片集群提供负载均衡. *数据分片集群中能实现主 ...

  8. 关于JVM加载class文件和类的初始化

    关于JVM加载class文件和类的初始化 1.JVM加载Class文件的原理机制 1.1.装载 查找并加载类的二进制数据 1.2.链接 验证:确保被加载类的正确性.(安全性考虑) 准备:为类的静态变量 ...

  9. ArcGis dbf读写——挂接Excel到属性表 C#

    ArcMap提供了挂接Excel表格信息到属性表的功能,但是当数据量较大到以万计甚至十万计的时候这个功能就歇菜了,当然,你可以考虑分段挂接.这个挂接功能只是做了一个表关联,属性记录每个字段的信息需要通 ...

  10. Python——爬取人口迁徙数据(以腾讯迁徙为例)

    说明: 1.迁徙量是腾讯修改后的数值,无法确认真实性. 2.代码运行期间,腾讯迁徙未设置IP屏蔽和浏览器检测,因此下段代码仅能保证发布近期有效. 3.代码功能:爬取指定一天的四十(此四十是根据自己的城 ...