(30 hackdays day 30) StrongLoop – Javascript全栈框架

SL_Logo_Stacked_RGB.png

StrongLoop是一个基于Nodejs的强大框架,几乎包含了移动开发全栈所需要的所有功能。2013年成立,很少的员工,一个技术驱动,执行力强大的团队。也是在13年我开始接触StrongLoop,当时是为了做nodejs方面的技术选型,看了许多框架,LoopBack是我觉得最酷的一个。我还记得当时是觉得LoopBack的文档太差(主要是跟在线的版本不一样),不知道能活多久所以才放弃了它。时隔一年回来看到这个绿油油的框架,这一年可真是突飞猛进呢。

LoopBackarch7162014-3.png.pagespeed.ce.tUGpVTVgYP.png

全栈框架StrongLoop

StrongLoop基本提供了制作一个移动产品所有的框架和工具,从标准的Backend server,Devops,应用监控,。要想介绍完全StrongLoop的所有产品得写一个长篇连载了,这里只简单的浏览一遍。

  • LoopBack
    一个功能很强大的WebServer框架,隐约看到Spring的影子…

    • SDK
      这是当时我觉得StrongLoop最好用的一点:根据后端Model自动生成对应的前端SDK。
    • RESTful API
      MEAN stack推动了所有新框架默认支持RESTful API,比如Nodejs里的Node-restifySailjs之类的天生就是为了serve API的。StrongLoop还提供了一个用来设计API的工具(beta状态),当然跟RAML比还是差不少的。
    • Data model
      很容易创建数据模型,自动产生对应的RESTful API。
    • Connectors
      当然可以对接各种数据库。
  • StrongLoop Controller
    Debug, Package Management, Build, Deploy, Cluster, Log等等一堆Devops工具。
  • Application Monitor
    一堆监控服务器的模块。
  • mBaaS
    使用StrongLoop,你也可以创建自己的LeanCloud啦,包括Push,地理位置计算(Geopoint),Social Login,User management,各种Replication,Offline sync(离线同步是个好东西),对接各种Stroage云(AWS,Rackspace之类内置了的)。当然,其实这些都是LoopBack里提供的功能,意思是用StrongLoop得永生。

开始

安装StrongLoop sudo npm install -g strongloop
创建一个应用 slc loopback(找个空白的目录哦) 它只会问你项目叫啥。
创建一个Model slc loopback:model (是不是想起了Yo generator~),然后它就会问一堆乱七八糟的问题了。

图片描述

plural指的是RESTful API的route名,一个Model对应的route默认情况下会被plural(复数化),比如Post的路径是Posts。

猜一下,像LeanCloud里类似,这时候应该去访问一个类似127.0.0.1:3000/xxx的地方看到他的后台吧~嗯 xxx=explorer http://127.0.0.1:3000/explorer 就能看到一个用Swagger做的API dashboard。

图片描述

看一下项目的结构,像Meteor一样,LoopBack将JS代码分成服务端(server),前端(client),以及共用部分(common,个人认为这种纯schema的方法比Meteor分割的更清晰)。

略过后面的API Explorer,如何连Database,直接看如何在前端使用刚刚创建出来的API。LoopBack的做法是帮助你创建你当前API的各端SDK,目前只支持Android/iOS/Angularjs。如果你用别的Web框架可能就只能自力更生了。

创建自己的SDK lb-ng server/server.js client/lb-services.js 看看client/lb-services.js,不错吧~

嗯,你的前端工程师需要个文档?执行 lb-ng-doc client/lb-services.js 有时候需要在前面添加sudo(不知道为啥),然后访问 http://localhost:3030/ 就能看到文档啦~这个功能是基于Docular做的。另外,如果你点LoopBack Services进去没东西,别担心,刷新一下。

看看是不是很像早期Angular的文档~

图片描述

API Designer

相信很多程序员喜欢Parsejs或者LeanCloud的原因都是那个图形化界面的Model设计(或者叫API设计)后台。如果前面用的那个”Yo generator”的API creator看起来还不够贴心,你可以试试StrongLoop新推出的StrongLoop Studio beta。

  • 在工程根目录下sudo npm install -g http://get-studio.strongloop.com/strong-studio.tgz 安装Studio。
  • 启动Studio strong-studio,自动打开浏览器
  • 去StrongLoop官网右上角注册个账号(文档里居然写了On the bottom right is a link to go to the registration page on strongloop.com,你们不知道页面很容易改版么…)
  • 用注册的用户名登录进去看到API Composer和Profiler。
  • Bingo!各种玩耍吧!

图片描述

p.s.如果这时候你遇到了类似SyntaxError: Cannot parse package.json: Unexpected token e这样的问题,检查一下你工程根下的package.json里是不是被jslint搞乱了(I dont’t know why…)
好吧好吧,我告诉你这个beta还真是特别beta…尽量不要在之前写过代码的项目里用…

懒人们

如果你缺个可以玩耍的Mongodb:Mongolab
如果你想要个可以协作开发的IDE:Koding
如果你想要个便宜好用的VPS:Digital Ocean
如果你觉得他们家不够便宜:Serverbear
如果你连VPS都不想要,只想部署个Node项目:Nodejitsu
如果你想设计个API尽快给客户端同学,又不想搭Node,找地方部署:Apiary
如果你想做(抄)个APP,又不想花钱雇一堆人:Appdupe

30天结束

这是最后一篇30hackdays啦,终于熬出来了(吁…)。Anyway,这三十天我学到了很多,如何寻找自己想要的服务,如何快速实现原型,如何比较开发者产品的竞品…

回头看,文章的内容肯定没有Shekhar的那篇Learning 30 Technologies in 30 Days: A Developer Challenge写的详尽(确实也没人家投入那么多时间精力),但我选择的技术更广泛(或者叫更乱七八糟),对我也是一种扩展视野的过程。

希望我的这个系列能给大家一些启发。技术并不都是那么深奥难懂,拥抱它也许不能立刻涨工资,但至少在寒冷的日子里,没有wifi,孤独寂寞冷的时候,还可以打开浏览器的console,输入个while,来暖暖手。

下面

嗯,不是你想的那个,也不是用来吃的。之后我会继续时不时写一些发现的好玩的开发者服务;另外,我会写一个StrongLoop的系列文章来介绍这个框架更多的特性,希望大家能够尝试这个迅猛发展的产品。

再做个小广告:最近在做一个小班(免费),专门教高中生技术,直到达成可以自行参加Hackathon的程度。如果身边有高中生对技术感兴趣,欢迎联系我~ fxp007@gmail.com

ok,那些输了的情自行来约~

ec0a6159252dd42af84c70ce003b5bb5c9eab82a.jpg

(30 hackdays day 29) Google TTL – 让Google讲小说

最近一直在喜马拉雅听有声小说《轮转长生》。每天会更新两集。但由于我记忆力差,所以如果等着每天的小说更新,就会由于记不住之前的情节而感觉很迷茫。所以我去搜了下,发现了这个小说的文本。于是就想如果能用Text To Speech把文字变成语音的话,不就能一直听下去,而不用等播音了嘛。

于是我就去搜各种TTS API Chinese。发现了iSpeech, tts-apivoicerss之类的服务。确实,TTS技术发展了这么多年,肯定有巨多的服务提供商。但翻了一圈发现中文的效果都很差。郁闷的时候想起Google Translate的TTS,就是那个整天被人当B-Box玩的东西。

图片描述

Google-TTS

这是一个非官方的Google TTS库,但是文档里说到Playbackwill only work when running the script locally as Google's server only returns audio if you can prevent the browser from sending the Referrer HTTP Header to their server,看来是Google对referrer做了检查。不过这点后面看起来似乎没那么严格。

我们先用个简单的页面测试一下播音(注意科学上网),播放一小段中文听起来还不错。

    var tts = new GoogleTTS('zh-CN');
    tts.play('你好呀呀呀呀', 'zh-CN', function (err) {
        console.log('complete');
    });

拿小说

这就用到了之前介绍的技术Import.io。安装桌面应用,Create API from URL 2.0,找一个详情页,比如第一百五十三章 照片上的三口之家,选择文章内容的div改column名为text。就可以create啦。
看一下文章的url是连续的,好,那就用node写个循环,然后用AVOS把文章内容保存下来。

// ...省略初始化
var Chapter = AV.Object.extend('Chapter');

_.each(_.map(_.range(27888, 27996), function (num) {
    return 'http://book.guidaye.com/daomu/85/' + num + '.html';
}), function (url) {
    var q = new AV.Query(Chapter);
    q.equalTo('url', url);
    q.first()
        .then(function (chapter) {
            if (chapter) {
                console.log('exists,%s',url);
            } else {
                unirest.get('https://api.import.io/store/data/c2f29086-18a8-4c7b-9d5d-6ea1d54a9bc5/_query')
                    .query({
                        'input/webpage/url': url,
                        '_user': '[UER]',
                        '_apikey': '[API KEY]'
                    })
                    .end(function (response) {
                        new Chapter({
                            url: response.body.pageUrl,
                            text: response.body.results[0].text
                        }).save().then(function () {
                                console.log('done,%s', response.body.pageUrl);
                            }, function (err) {
                                console.log(err);
                            })
                    })
            }
        })

})

跑一遍基本就ok啦,如果有发生错误的就再执行一遍验证下。

读小说

好,我们要一个前端页面来读小说。

<div id='playing'></div>


    AV.initialize("[APPID]", "[APPKEY]");

    function speakChapter(chapterNum) {
        var url = 'http://book.guidaye.com/daomu/85/' + chapterNum + '.html';
        var Chapter = AV.Object.extend('Chapter');
        var query = new AV.Query(Chapter);
        query.equalTo('url', url);
        query.first()
                .then(function (chapter) {
                    document.getElementById('playing').text = "playing:" + url;
                    console.log('playing,%s', chapter.get('url'));
                    var tts = new GoogleTTS('zh-CN');
                    tts.play(chapter.get('text'), "zh-CN", function (err) {
                        console.log('complete,%s', chapter.url);
                    });
                }, function (err) {
                    console.log(err);
                })
    }
    //speakChapter(27889);

我们在console里实验一下speakChapter(27889);,终于开始读啦!听起来感觉还好,但还是TTS的最大普遍问题:语气不对。每个人说的,旁白说的都是一个味道…另一个问题是,读文章的时候一段以后会需要几秒钟的加载时间去加载下一段。而这个gap很有可能是一句话的中间。这就让人听起来很诡异了…“雷英…(上个厕所)…雄笑了笑”…

最后说一下浏览器问题。我发现safari打开这个页面就能正常播放,所以,也还算可忍~

好啦,按这个思路我们可以把自己喜欢的小说播放出来~如果你想把声音保存成mp3的话,可以用node-google-text-to-speech来把数据pipe到文件里,再用pushbullet类的产品把数据同步到移动端去。

29天啦,坐等吃串!

(30 hackdays day 27) CloudRail – 云存储的POSIX

这是一个Mashup的时代,所有你知道的API都将成为你设计开发新产品的武器。但跟App一样,太多的选择反而给开发者带来了困扰:到底哪家的服务更好,SDK用起来更舒服,以后维护的成本更低?存储是所有应用都会用到的,Cloudrail的出现正是帮助开发者摆脱SDK选择困难的工具。

图片描述

Mashape类似,Cloudrail认为存储API应该拥有共通的结构和描述方式。但是,跟Cloud Element的Document hub类应用不一样,为了让用户安心,Cloudrail并不用将数据都都经过它的服务器,而是让用户直接和存储服务提供者通信。通过下面的图能很清楚看出Cloudrail是怎么做到的。

图片描述

这就是Cloudrail最核心的技术:Self Adapting API。它的作用是保证客户端随时获得最新的API提供者的API定义,换句话说,就是保证开发者不用换SDK就可以持续访问最新的API(所以叫’No API Updates’)。Javascript的实现方法大家都能想到,就是每次都下载新的connector库。Android呢想一想估计是用ClassLoader或者DexClassLoader之类的吧,iOS就不知道了…不过也许通过WebView的js层做一个中转也是一个低效率的方案。看了下Download页面,发现果然Android版本还没有上线,剩下的是Others。只有JS版本的可以用。不过目前只是Beta嘛,可以理解~

API

CloudRail为数据存储服务定义了一系列标准接口,通过一个CloudRailInterface把各家的存储服务封装起来。比如Sample代码中,CloudRail把Dropbox和GoogleDrive的API封装为一个CloudRailInterface,通过一个CloudRailClient来统一访问。其中用户在不同平台的的标识client_id可以按照帮助文档获得到。

var CRC = CloudRailClient,
    CRI = CRC.CloudRailInterface,
    DBServiceTag = 'Dropbox',
    GDServiceTag = 'GoogleDrive',
    userIdentities = [],
    userIdentitieServiceTags = [],
    serviceDB, serviceClientDB,
    serviceGD, serviceClientGD;

/* Initialize services */

serviceDB = CRI.initService(DBServiceTag);
serviceClientDB = new CRC.ClientIdentity(DBServiceTag,
        {'client_id': 'abcd'}); // insert here your own client id

serviceGD = CRI.initService(GDServiceTag);
serviceClientGD = new CRC.ClientIdentity(GDServiceTag,
        {'client_id': '1234'}); // insert here your own client id

/* Read User Identities */

var dbUser = new CRC.NodeIdentifier(CRI.ObjectType.USER_IDENTITY, DBServiceTag),
    gdUser = new CRC.NodeIdentifier(CRI.ObjectType.USER_IDENTITY, GDServiceTag);

CRI.read(dbUser, serviceClientDB, (function(servicetag) {
    return function(res) {
        saveUserIdentity(res, servicetag);
    };
})(DBServiceTag));
CRI.read(gdUser, serviceClientGD, (function(servicetag) {
    return function(res) {
        saveUserIdentity(res, servicetag);
    };
})(GDServiceTag));

目前CloudRail提供了基本的CRUD API,看起来还是挺通用的。突然觉得,CloudRail是不是想实现一个云存储版的POSIX接口呢。

文档最后提到了一个额外代码CloudRailClient.Crypto.Hash.SHA256。看来是为了对文件作Hash用的。

Price

官网的说法是’Integrations to Cloud Storage Providers will be free forever.’,So…他们怎么活下来…

StrongLoop

最后,提一下之前用过的Nodejs框架。其中有一个很好用的功能,Auto generate SDK。在后端创建一个数据Model以后,Grunt一下就会自动把这个Model的访问代码。比如Angular的版本生成一个访问该Model的Service。

图片描述

提到这个原因是,Nodejs+browser这样的脚本语言组合太适合WebApp的开发了,这样组合所带来的灵活性也带来了很多新的可能。像CloudRail,StrongLoop这样动态接口比原来那些“死板”的接口通信拥有更强大的整合能力。也许以后就会出现一批服务,专门负责接口的维护,而文中这类技术就是这种服务的基础。不过CloudRail还会遇到很多问题,诸如版本管理,平台支持能力等。

另外,CloudRail也是个国际团队,总部在德国的曼海姆,在旧金山也有办公室,公司内居然有幼儿园…

(30 hackdays day 26) Mailgun – 发Mail是个技术活儿

发“垃圾”Email对于很多Web产品来说都是一种稳定的营销手段。让我们看一个众多Startup都在用的Mail服务,Mailgun。这是一个众多牛逼Startup都在使用的服务,里面不乏37signal,Github,Parse,Stripe这样的大佬。

图片描述

为啥要用第三方Mail服务?各种原因,一个是自己host mail server不安全,二是在程序端调用Mail发送邮件还是比较麻烦,万一换个邮箱所有代码都要变,三是自己的smtp server发的mail很可能被扔到垃圾邮箱里,还有就是Mailgun还可以帮助你追踪邮件的发送状态,比如多少被列入spam了(咋做到的呢?),以及各种行为,比如打开邮件,点击链接,以及有多少订阅用户再也无法投递到了。(还有6789)

图片描述

针对为啥用HTTP而不用SMTP,Mailgun的说法是

The HTTP API has some advantages:

  • It’s faster.
  • Better for large scale sending.(这个很关键)
  • You don’t have to deal with MIME because we will assemble it on our side.
  • Just use a request library available for your language of choice.

使用

按照signup之后的guide很容易的就能发送一个email,就是这么简单。

curl -s --user 'api:key-xxx' \
    https://api.mailgun.net/v2/sandboxa26b3f9c5f31491a906e26bb87864007.mailgun.org/messages \
    -F from='Mailgun Sandbox ' \
    -F to='Xiaoping Feng '\
    -F subject='Hello Xiaoping Feng' \
    -F text='Congratulations Xiaoping Feng, you just sent an email with Mailgun!  You are truly awesome! 

想把发送者的域名改为自己的,需要添加自己的域名,配置DNS的TEXT,CNAME和MX,之后等一等就好啦。

Nodejs

怒,官方居然对Nodejs部分只写了个参见NPM module…发现指向的是一个不知道什么人写的小module…时间会让他们后悔的!

Track

前面说到,Mailgun的Track是很重要的功能。用户打开事件的监测应该是按照图片的view行为触发的The email recipient opened the email and enabled image viewing。click链接事件是根据对mail内link地址的replace来实现的。其他的都好说啦~

可以通过API来下载以上事件的数据(包括query),也是超简单实用的~

curl -s --user 'api:key-xxx' -G \
      https://api.mailgun.net/v2/samples.mailgun.org/events \
      --data-urlencode begin='Fri, 3 May 2013 09:00:00 -0000' \
      --data-urlencode ascending=yes \
      --data-urlencode limit=25 \
      --data-urlencode pretty=yes \
      --data-urlencode recipient=joe@example.com

Sender vs From

Docment里的一个FAQ让我涨了姿势,邮件里的Sender和From还可以是不一样的。

The sender domain is what the receiving email server sees when initiating the session.
The from address is what your recipients will see.
For better deliverability it is recommended to use the same from domain as the sender.

哈,为了早起,今天再偷懒一下~晚安各位,祝打枪愉快~

(30 hackdays day 25) Tuling123 – 光棍节找个机器人聊天吧

Tuling123

今天介绍又一个AI机器人Tuling123,当然是开放API的。而且,Tuling123号称可以创建个性化的机器人哦~(别跟我说这logo像个啥…)

图片描述

注册获取API Key,看平台接入文档。发现一个悲惨的事实:发送请求的API居然是GET的…而且URL还设计成这样 http://www.tuling123.com/openapi/api…让我猜一下,以后你们会把新的API设计成/openapi/api2吧。这惨状仅次于我见到过的一个把发送评论API设计成GET的。

而且,这可怜的GET还要承载userid,location,lon(lon….哎),lat以及询问信息info所有内容…

想要测试这个API很容易,curl一下就好啦。

curl "http://www.tuling123.com/openapi/api?key=xxxxxx&amp;info=我喜欢你"
{"code":100000,"text":"宝贝,我也很爱你,来,亲一下"}

这个回答看起来不错。但当我问“哪里当片儿”时,丫居然说

{"code":100000,"text":"最好别有这样的想法。为了你好才说的。 希望对你能有好处。 yin是生活事业学业福气寿命婚姻命运的克星。一个人消耗完了自己相应的福报,等待他的是苦果自受。 看、想、听、说、做yin秽的事,都会大大折损人的福报。"}

只能说…真没幽默感…

另外,这API居然把unauthorized的statusCode写在200的reponse里,而且还跟正常业务的statusCode写在一个地方,受不鸟…还40001,40002…C时代穿越过来的么…请好好看看How To Design A Good API And Why It Matters Google

再吐槽一下:Luling123把referrer link居然翻译成“升级连接”,还真是…目的导向…是不是应该考虑把消息发送按钮改名叫“约”呢…哎,再吐呆毛就爆了…

好啦,官方只提供了两端调用代码,PHP和Java的=.=。让我们来改造成nodejs的吧~

unirest

受够了mikeal/request的non-promise,随便一搜就找到了新欢:unirest。选择的一个重要原因是,Mashape做的~

var API_ASK = 'http://www.tuling123.com/openapi/api',
function ask(info, cb, err) {
    var query = {
        'key': 'xxx',
        'info': info
    }
    unirest.get(API_ASK)
        .query(query)
        .headers({'Accept': 'application/json'})
        .end(function (response) {
            if (response.ok) {
                if (cb) cb(response.body);
            } else {
                if (err) cerr(response.error);
            }
        });
}

虽然不提供美妙的promise返回,但其他的一些超便利的功能,以及好看的字体,足以让人从mikeal的傲慢中舒缓不少~

grunt-init

好,让我们把它封装成一个npm吧。我猜你肯定不想从npm init开始。所以我们找找那些肯定存在的Project Scaffolding吧,很显然Grunt就有Project Scaffolding。我们选nodejs的grunt-init-node来用。

安装好以后grunt一下确定没问题。把之前的代码改吧改吧扔进去。

var unirest = require('unirest'),
    Q = require('q');

var API_ASK = 'http://www.tuling123.com/openapi/api',
    API_KEY,
    RESPONSE_TYPE = {
        100000: 'text',
        200000: 'url',
        301000: 'novel',
        302000: 'news',
        304000: 'app',
        305000: 'train',
        306000: 'plane',
        307000: 'groupon',
        308000: 'coupon',
        309000: 'hotel',
        310000: 'lottery',
        311000: 'price',
        312000: 'restaurant'
    }

exports.init = function (key) {
    API_KEY = key;
};

exports.ask = function (info) {
    if (!API_KEY) {
        throw new Error('Please init with api key first');
    }
    var deferred = Q.defer();
    var query = {
        'key': API_KEY,
        'info': info
    }
    unirest.get(API_ASK)
        .query(query)
        .headers({'Accept': 'application/json'})
        .end(function (response) {
            if (response.ok) {
                var result = JSON.parse(response.body);
                result.type = RESPONSE_TYPE[result.code];
                if (result.type) {
                    deferred.resolve(response.body);
                } else {
                    deferred.reject('unknown code,' + response.body.code);
                }
            } else {
                deferred.reject(response.error);
            }
        });

    return deferred.promise;
};

哦sorry忘了提了,我用了可爱的Q来Promisify一下这个接口。想看这完整代码的请看tuling123 nodejs-sdk。如果有人有兴趣一起维护欢迎加入哈。哎,看惯了国外API产品的文档看国内的真是不习惯。

最后抱怨一下,Tuling123的登陆timeout设置的太短了…而且,似乎跟cookie里写的不一样…

然后呢…欢迎注册Tuling123 via Link~

(30 hackdays day 24) Atatus – what happened before crash

Atatus是一个用来记录“犯罪现场”的工具。正如其他类似的统计异常的SDK一样,Atatus除了收集你的web app里发生的异常,在后台看到各种Bug的统计,最重要的功能的可以记录发生异常的时候用户的行为。

图片描述

如何重现

工程师在解决bug的时候遇到的最讨厌的事情莫过于不知道如何重现问题。现代Web App的复杂度越来越高,一个错误可能是长时间的javascript执行的积累造成的,很可能用户遇到一个问题,但只能报告一个有#的url。而之前的操作才是引发这个bug的原因。
Atatus就是来解决这个问题的。用户只要配置好SDK,一旦遇到Exception,Atatus就会把它,以及之前所做的行为报告给服务器。

比如我做了个登陆页面,我在一个input里输入了用户名,在另一个密码登陆框里输入了密码,然后点击了登陆按钮,bang,出bug了。整个栈被如实打出。同时,一旦你觉得某个bug被解决了,可以点resolve隐藏掉这个bug。对于小型项目来说还是很方便的。

图片描述

Angular

Angular是个FrontEnd又爱又恨的的框架,其中一恨就是有些错误超难track。Atatus专门为Angular写了一小段错误处理代码(当然这个不是Atatus专用的)。

.factory('$exceptionHandler', ["$window", function ($window) {
  return function (exception, cause) {
    if (exception.stack) {
      exception.stack = exception.stack.replace('new ', '');
    }
    if ($window.atatus) {
      $window.atatus.send(exception);
    }
  };
}]);

增加这么一个处理exception的factory,你的Angular程序就能在发生错误的时候统计下整个的用户行为和错误了。

离线存储

一个H5应用经常会面临用户不在线的情况,只要打开offline模式,等到用户连上网以后就会自动发送。它用的是localstorage,所以不用怕关掉tab。

atatus.enableOffline(true)

Pricing

唯一有点遗憾的就是它不提供永久免费的方案。当然你可以试用30天之后再注册一个账号…
不过,12刀15万个exception听起来还是不错的(除非你的App写了个抛异常的死循环)~

(30 hackdays day 23) Favico.js – 网页杀死处女

是不是觉得网页的favicon(就是一个tab上的小图片)太乏味?用微信头像谋杀处女座还不够爽?怕用户看不到“您有新的消息”?好吧,用Favico.js来屠戮用户吧!

图片描述

看到了吧…凄苦吧…想象一下以后你的浏览器变成这样,还在跳来跳去…是不是想死…

图片描述

这个库使用起来超简单:创建一个Favico,.badge设置数字,done

var badge = 5;
var favicon = new Favico({
    animation : 'slide'
});
favicon.badge(badge);

原理也很简单,js里创建一个canvas的dom,把图片变成DataURL,然后直接改head里icon的href。所以,它还有高级用法:把html5视频放到favicon里,甚至把摄像头内容变成favicon…

var favicon=new Favico();
favicon.webcam();
//stop
favicon.webcam('stop');

好吧我承认我幻想过在tab里看porn,who care有码无码…

哎,写到现在都不知道写啥了…没心情写码态。先这样吧,以后再增加一些源码分析。

(30 hackdays day 22) Import.io – 最简单好用的网页爬虫服务

import.io,一个2012年成立的公司。至今已经有3m刀的…种子轮…为毛…

注意,这是一篇由脑残和图片组成的文章。

图片描述

anyway,import.io是我用过最简单的爬虫,没有之一。简单到…只要输入一个网址(当然其实它可以更简单到不用输入http://),就可以获得一个该页面对应的API。更牛逼的是,这是一个我想寻找付费服务却寻觅不得的产品!

两步得到网站API

  1. 打开https://magic.import.io/ 输入要爬取的网址,比如http://producthunt.com
  2. 调整你需要的数据列,比如把url_link那列改为title
  3. 点击下面的GET API…

然后import.io就会给出一个GET API,一个POST API,甚至还有直接从Google sheets取数据的地址!

图片描述

这个API足够足够简单吧!也不用[o]auth就直接拿数据!唯一可惜的一点是,似乎它有一些延迟,没法很实时获得数据(也正是这时候我开始满页面找pricing…)。

除了GET以外,你还可以往里拽(POST)其他URL,很适合那种同构的多页爬取。

当然,虽然你觉得Http GET已经足够简单了,但import.io不这么认为,它觉得你还是需要SDK的…好吧,其实我是为了展示他的API doc页面…里红色的那部分!想起了一个以前看到过别人分享的注释

//Attempt Handshake: Hello? This is London calling. Are we reaching you?
//Handshake Failed: I don't understand...he just hung up.

图片描述

import.io不光是一个爬网页的平台,它还提供存储,搜索(是的…)等服务。赞一下里面的文档样式。

图片描述

App

一个爬虫用的桌面App?想到了啥?像浏览器一样,圈圈点点?
这里下载import.io的桌面应用,安装完了去桌面打开(谁知道为啥它不扔launcher里…),splash都cute到死…

图片描述

打开以后呢,发现呢,就是个firefox内核的浏览器…区别就在与在这个app里可以使用chrome里无法使用的API from URL 2.0,API from Authenticated URL这些功能。估计就是为了把各种登陆cookie都很容易拿到,所以就做了个浏览器的壳吧。

用起来才发现这简直是…简直了…找到要爬的页面,鼠标选择要爬取的内容,搞定以后publish就可以产生一个某类页面的API了,以后只需要把新的文章URL扔给他就ok了。

图片描述

可以看出import.io其实没有diffbot那么智能,但对于那些懒得写xpath的程序员,import.io真的很方便!说到xpath,似乎我真的好久没写过了…可以看出import.io也是用xpath来实现的。

图片描述

好啦,这篇真的是一行代码都没有(这完全是为了配合import.io的好用好吧)…好吧,我有姿势我自豪~

(30 hackdays day 21) Wakatime – 现在几点了,你在做什么呢

前面提到了一个用来QS自己的工具Clarify。这次介绍一个专门给程序员用的工具。
大家都知道一万小时定律,但我究竟写java写了多久了呢?WakaTime知道。估计很多人都知道RescueTime,我也是用RescueTime来记录我的工作时间。但它无法记录更具体的行为(也许实际也做了),比如我写了多久javascript,多久objective-C。Waka就可以啦,因为它提供了几个主要的IDE的插件,只需要安装对应的插件,Waka就能自动记录你在IDE里的行为。

WakaTime

Waka的基本设计和rescuetime类似。每个人注册完将获取一个key,装一个客户端,把key输进去(登陆是同一个道理),然后它就把本地的所有行为带个key扔给服务器来统计,一段时间之后给你个报表。不过Waka做的真的很精准,精确到每一个文件用了多少秒,每一种语言用了多少时间。

图片描述
图片描述

据说Waka还能知道你在每个branch用了多久,这还真是一个量化程序员的好办法。如果有项目管理会用这种工具,相信也能更精准的把握开发进度,控制每一次开发尝试的代价。

作为一个付费用户狂人,登陆以后做的第一件事当然是搜Pricing~对比一下,付费用户的功能就是无限期数据查看和原始数据导出。想了想…算了这次…还有团队版本哦~

工作原理

Waka让我觉得好玩的是他的plugin都是开源的,而且在他的开源项目列表里看到了一个叫wakatime的核心项目,python写的,说是大家基本不需要直接使用的。基本可以确定是每个plugin都会去调用的核心库。

从queue.py里看出所有的log都会被放到~/.wakatime.db下,那我们就去看看它存了啥。打开以后发现是空的…想必是Waka把log都传走了以后就删掉了本地的。于是,断网,假装写代码,再看看。

图片描述

可以看到它记录了我在哪个文件,什么时间,什么项目下,写了什么语言,甚至在哪一行下都记录下来了。如果这个数据结合版本管理,动态的展现出来,将会是多酷啊~

Anyway,由于我很想用koding这样的WebIDE来工作,这样就又没发用Waka了,所以我需要知道发送记录的具体API是什么。

这也叫API文档

Wakatime是觉得代码是最好的文档么,居然在API文档里没有写出这些log是如何被打进数据库的…文档里只有认证,获得统计信息,和当前用户信息的接口…

好吧,那我们直接去刚才那个python的repo里看吧。首先,这里面肯定有个代码文件是包括http和wakatime.com的。那就搜吧,找到了init.py里一个函数叫send_action。很明显这是用来发log的。

那先让我们用curl模拟一下这个请求吧(看起来也不用太多域)。

$ curl -d '{"time":"1414688349","file":"/root","lines":"123","language":"javascript","is_write":"0","project":"manhattan"}' -H "User-Agent: space" -H "Content-Type: application/json" -H "Authorization: Basic ODMwOTliYjMtZGUwZS00NWFkLThhODItZmY4OWUwYzxxxxxxx" https://wakatime.com/api/v1/actions
{
  "data": {
    "id": "83682870-8306-4d12-b4a9-e0ba5f6cc295"
  }
}

把Key base64一下,用date +%s拿一下当前的timestamp,就可以往Waka里扔数据啦。扔完了以后看看Dashboard~好啦,我们的曼哈顿项目出现啦~

图片描述

如果你愿意可以把它改成各种SDK(当然browser里会出现跨域的问题,所以你可以找个中间服务器做这事儿)。

在未来做点儿事儿

刚才看到了有个timestamp来表示时间,如果改成以后呢。估计waka不会蛋疼到做这个检查吧…往2015年1月1日扔几个action,等一小会儿,看manhattan项目的URL,改成https://wakatime.com/project/manhattan?start=2014-12-26&end=2015-1-3。就能看到我在未来写javascript了~

扩展的waka

所以其实wakatime是一个特别基础的action记录和统计平台。如果它愿意开源(其实这真的还不如一个logger分析平台…),完全可以搭建成一个QS小服务。比如晚上9点连续发出20分钟加速度变化数据,然后就停止了10分钟…

(30 hackdays day 20) Clarify – 处于pending状态的QS服务

如果你还不知道量化自身,那么建议你看看Quantified Self。量化自身对于我来说,就是希望在我比较短的生命预期内,能尽量留下我的数据,从而在身体死掉前,用算法创建出一个IO近似于我的“个体”。

“我TM说啥了?”

这是两三年前就在做的一件事情,时断时续的我用一些12小时的录音笔记录了一天我周围的声音(当然也包括我自己的)。最简单的目的就是希望能知道我之前说了啥(我记性比较差…)。但当时找了一些voice recognition的SDK来分析记录下来的语音信息。结果都特别不如意,基本没有能用的。
直到最近买了一个Narrative Clip,我又开始重燃QS的热情了。Rekognition – 借我借我一双慧眼吧文章中,我第一次把narrative里的部分数据拿来分析,得到的结果意外的好。这坚定了我之前的想法:

如果有一个一开始设计好的idea的api flow的blueprint,让机器能帮你在发展的过程中不断紧盯这些API能不能满足需求,直到某一天,这个pipeline通了。丫就告诉你,爷,你丫的idea跑通了。

Clarify

好啦,BB这么多的目的是介绍一个不那么牛逼的服务,Clarify。Clarify号称自己做三件事儿:Capture,Analyze,Discover。简单的说就是把语音视频数据扔给它,它帮你做信号处理(估计就是降噪),语音识别和自然语言处理。当然这也是收费的,音频每小时3刀,每5000个搜索一刀。

听着很简单,但当我看到这个服务的时候,我知道这就是我想要的~不用装SDK,不用运行在我的server上。我只需要把媒体数据扔到一个像七牛的地方,拿到URL,扔给Clarify。稍等就可以问它“我前天这时候说下周去哪聚会来着?”。好吧,至少能做到,搜索,上周这时候,有“聚会”的这段音频。

API

丫居然用PHP来做tutorial…好吧,不发表言论…创建一个App,拿到AppKey。找到nodejs SDK,翻翻tutorial,猜一猜就写个测试程序出来。

var clarifyio = require('clarifyio');
var client = new clarifyio.Client("api.clarify.io", "[key]")

var bundleId;
var data = {
    name: "qwerty bundle " + Math.random(),
    media_url: "https://s3-us-west-2.amazonaws.com/op3nvoice/harvard-sentences-1.wav"
};
client.createBundle(data, function (err, res) {
    if (err) return done(err);
    bundleId = res.id;
    client.search({query: "fire", query_fields: "*"}, function (err, res) {
        if (err) return done(err);
        console.log('search');
        console.dir(res);
        client.removeBundle(bundleId, function (err) {
            if (err) return done(err);
            client.removeBundle(bundleId);
        });
    });
});

就是这句 client.search({query: “fire”, query_fields: “*”},…)。执行结果也很简单。

{ total: 1,
  limit: 10,
  search_terms: [ { term: 'fire' } ],
  item_results: [ { score: 1, term_results: [Object] } ],
  _class: 'SearchCollection',
  _links:
   { self: { href: '/v1/search?query=fire&amp;query_fields=*' },
     items: [ [Object] ],
     first: { href: '/v1/search?query=fire&amp;query_fields=*&amp;iterator=S10a47fbc8ef' } } }

它就是告诉你某个bundle里(bundle就是一个media的集合)有没有这个字出现。所以具体在哪里出现,似乎也还是得很多人工介入的。

无论如何,Clarify让我能用十几行代码就能分析一个我之前的录音(这个就不放出来了~)。虽然结果不理想,但我相信不久的将来我一定能等到那个”test全部通过”的API的~

另外,介绍一个全球第一个虚拟Hackathon。是之前提到的Koding举办的。https://koding.com/Hackathon
在一个WebIDE里,和一群全世界的hacker们玩一次hackathon,一定很酷!

哎,最近精力有限,水点儿请见谅…

在WordPress.com的博客.

向上 ↑