支出数据爬取完成

把上次发布的一年期支出统计数据爬取了一下(数据长度是一年的,从 2011 年 8 月 1 日到 2012 年 7 月 30 日,但是有效数据是从 2012 年 2 月起才有的,之前是空白数据),就在上次发布的那个页面上,Chrome 开发工具里跑了一下,把数据复制到编辑器里了,晚上可以拿来做可视化。

表格是很简单的,去掉表头(第一行)和最后一行,剩余的是数据,没一行从左到右爬取,按顺序读取单元格内容。单元格内容都是包裹在一层 p 标签里的,取出来之后再取第一个子元素的内容就行了。

var billData = [];
for(var i = 1; i < num-1; i ++){
  var data = {};
  var trs = document.querySelectorAll('tr')[i];
  data.date = trs.children[0].children[0].innerHTML;
  data.Food=trs.children[1].children[0].innerHTML;
  data.Transportation = trs.children[2].children[0].innerHTML;
  data.Entertainment = trs.children[3].children[0].innerHTML;
  data.Clothing = trs.children[4].children[0].innerHTML;
  data.Accrssories = trs.children[5].children[0].innerHTML;
  data.Billings = trs.children[6].children[0].innerHTML;
  data.ElectronicEquip = trs.children[7].children[0].innerHTML;
  data.Family = trs.children[8].children[0].innerHTML;
  data.HealthCare = trs.children[9].children[0].innerHTML;
  data.Housing = trs.children[10].children[0].innerHTML;
  data.Education = trs.children[11].children[0].innerHTML;
  data.Sum = trs.children[12].children[0].innerHTML;
  billData.push(data);
}

我先这么跑了一遍,然后发现一个问题,源数据是最新的在最前面,而我希望最旧的数据在前面。排序本来是很简单的,但是被我那个记账的软件搞复杂了,它的格式是 dd.mm.yyyy 。后来想了一下,不需要排序的,只要把数据反转一下就行了。但是为了格式统一,我还是想把时间字符串搞标准一点。这里用了正则。

var billData = [];
for(var i = 1; i < num-1; i ++){
  var data = {};
  var trs = document.querySelectorAll('tr')[i];
  var dateStr = trs.children[0].children[0].innerHTML;
  var date = dateStr.match(/\d{4}$/)[0].toString()+'-'+dateStr.match(/\.\d{2}/)[0].replace(/\./, '').toString()+'-'+dateStr.match(/^\d{2}/)[0].toString();
  data.date = date;
  data.Food=trs.children[1].children[0].innerHTML;
  data.Transportation = trs.children[2].children[0].innerHTML;
  data.Entertainment = trs.children[3].children[0].innerHTML;
  data.Clothing = trs.children[4].children[0].innerHTML;
  data.Accrssories = trs.children[5].children[0].innerHTML;
  data.Billings = trs.children[6].children[0].innerHTML;
  data.ElectronicEquip = trs.children[7].children[0].innerHTML;
  data.Family = trs.children[8].children[0].innerHTML;
  data.HealthCare = trs.children[9].children[0].innerHTML;
  data.Housing = trs.children[10].children[0].innerHTML;
  data.Education = trs.children[11].children[0].innerHTML;
  data.Sum = trs.children[12].children[0].innerHTML;
  billData.push(data);
}

然后就很简单了,不需要排序,只需要将数组倒转就行了。

Ubuntu 下将任意格式音频转换为mp3

昨天要上传一首歌到点电网,结果搞不定转换,因为点电网只支持mp3 格式的上传,而我平时听的都是m4a 的。

来解释下我为什么不需要在Ubuntu 下将无损格式转换为其他。我听歌都是这么听,首先,我听到一首歌很好听,搜索到演唱者、专辑名称,搜索这张专辑的无损下载。如果有无损,下载无损;没有无损,找320kbps 的mp3 ;没有的话找192kbps 的mp3; 要是连这种音质都无法满足,那这歌太偏了,或者是刚刚出来还没有人抓轨,要么去买CD 要么等一段时间。如果下载的不是无损,直接放手机里;如果是无损,虚拟机win 系统用Foobar2000 打开,用NeroAAC 编码器转换到双通道512kbps aac 文件(m4a 扩展名),放到手机里。

我为什么要开虚拟机,原因也在这里,在Ubuntu 下我不知道怎么使用NeroAAC Encoder ==。 抱歉我找不到任何使用的提示。而使用faac 码率最高只有320kbps 。虽然据说faac 的算法能更好地还原低音,但我也没听出来区别,倒是码率低的硬伤无法弥补。

我尝试了一个小时,搜索到的方法没有一个能解决我的问题。要么就是mencoder ,要么就是ffmpeg 。我遇到了下面的情况。

ffmpeg -i 魔法の人.m4a -acodec mp3 魔法の人.mp3
# 输出结果
# ffmpeg version 0.8.3-4:0.8.3-0ubuntu0.12.04.1, Copyright (c) 2000-2012 the Libav developers
  built on Jun 12 2012 16:52:09 with gcc 4.6.3
# *** THIS PROGRAM IS DEPRECATED ***
# This program is only provided for compatibility and will be removed in a future release. Please use avconv instead.
# Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '魔法の人.m4a':
#   Metadata:
#     major_brand     : mp42
#     minor_version   : 0
#     compatible_brands: M4A mp42isom
#     creation_time   : 2012-08-30 05:27:37
#     track           : 2/12
#     genre           : JPop
#     album           : やさしい花の咲く場所
#     artist          : 奥華子
#     comment         : YEAR: 2006
#     title           : 魔法の人
#     encoder         : Nero AAC codec / 1.5.4.0
#     date            : 2006
#   Duration: 00:04:39.12, start: 0.000000, bitrate: 477 kb/s
#     Chapter #0.0: start 0.059501, end 279.127075
#     Metadata:
#       title           : 魔法の人
#     Stream #0.0(und): Audio: aac, 44100 Hz, stereo, s16, 476 kb/s
#     Metadata:
#       creation_time   : 2012-08-30 05:27:37
# Unknown encoder 'mp3'

不管怎么更换acodec 参数,结果都是一样的,ffmpeg 根本找不到对应的mp3 编码器。使用mencoder 就会出现下面这种情况。

mencoder -o 魔法の人.mp3 -ovc frameno -oac mp3lame -lameopts cbr:br=320 -of rawaudio 魔法の人.m4a
# MEncoder svn r34540 (Ubuntu), built with gcc-4.6 (C) 2000-2012 MPlayer Team
# success: format: 0  data: 0x0 - 0xfe563c
# libavformat version 53.21.0 (external)
# Mismatching header version 53.19.0
# libavformat file format detected.
# [lavf] stream 0: audio (aac), -aid 0, -alang und
# Video stream is mandatory!
# 
# Exiting...

而实际上只要使用 mplayer 将源文件 dump 成 wav 格式,再使用 lame 编码成 mp3 就可以了,简单有效。

mplayer 魔法の人.m4a -ao pcm
# MPlayer svn r34540 (Ubuntu), built with gcc-4.6 (C) 2000-2012 MPlayer Team
# mplayer: could not connect to socket
# mplayer: No such file or directory
# Failed to open LIRC support. You will not be able to use your remote control.
# 
# Playing 魔法の人.m4a.
# libavformat version 53.21.0 (external)
# Mismatching header version 53.19.0
# libavformat file format detected.
# [lavf] stream 0: audio (aac), -aid 0, -alang und
# Clip info:
#  major_brand: mp42
#  minor_version: 0
#  compatible_brands: M4A mp42isom
#  creation_time: 2012-08-30 05:27:37
#  track: 2/12
#  genre: JPop
#  album: やさしい花の咲く場所
#  artist: 奥華子
#  comment: YEAR: 2006
#  title: 魔法の人
#  encoder: Nero AAC codec / 1.5.4.0
#  date: 2006
# Load subtitles in ./
# ==========================================================================
# Opening audio decoder: [ffmpeg] FFmpeg/libavcodec audio decoders
# libavcodec version 53.35.0 (external)
# Mismatching header version 53.32.2
# AUDIO: 44100 Hz, 2 ch, s16le, 476.2 kbit/33.74% (ratio: 59526->176400)
# Selected audio codec: [ffaac] afm: ffmpeg (FFmpeg AAC (MPEG-2/MPEG-4 Audio))
# ==========================================================================
# [AO PCM] File: audiodump.wav (WAVE)
# PCM: Samplerate: 44100Hz Channels: Stereo Format s16le
# [AO PCM] Info: Faster dumping is achieved with -benchmark -vc null -vo null -ao pcm:fast
# [AO PCM] Info: To write WAVE files use -ao pcm:waveheader (default).
# AO: [pcm] 44100Hz 2ch s16le (2 bytes per sample)
# Video: no video
# Starting playback...
# A: 279.1 (04:39.1) of 279.1 (04:39.1)  0.2% 
# 
# 
# Exiting... (End of file)
# 
lame audiodump.wav -o 魔法の人.mp3 -b 320
# LAME 3.99.3 64bits (http://lame.sf.net)
# Using polyphase lowpass filter, transition band: 20094 Hz - 20627 Hz
# Encoding audiodump.wav to 魔法の人.mp3
# Encoding as 44.1 kHz j-stereo MPEG-1 Layer III (4.4x) 320 kbps qval=3
#     Frame          |  CPU time/estim | REAL time/estim | play/CPU |    ETA 
#  10687/10687 (100%)|    0:10/    0:10|    0:10/    0:10|   27.695x|    0:00 
# --------------------------------------------------------------------------------------------------------------# ----------------------------------------
#    kbps        LR    MS  %     long switch short %
#   320.0       47.3  52.7        97.2   1.6   1.3
# Writing LAME Tag...done
# ReplayGain: -7.7dB

笔电各种奇葩的问题

这两天各种悲催。。哈尔滨最近火了,因为塌了座桥还反咬一口说是因为超载,加上各种塌陷事故,动不动出来个几十米的大洞 ==。我们去实习都小心翼翼地,一不小心掉下去可就完蛋。

昨天晚上把笔电拆开清灰,蛋疼的室友把螺丝装错了 ==。 ,腕托那里被顶起一个小包。。。。。。然后今天还发现他忘了把我风扇的线给插上,我说今天怎么一开机 CPU 烧到 80+ 度散热口居然还是凉的==。

今天更悲催。。从昨天晚上开始,电脑变得很卡,鼠标键盘动不动无响应,打字都成问题。今天折腾了一下午,还把compiz 设置重置了 ==。结果你猜是怎么回事?昨天突然热起来了,我就插了个USB 风扇,结果功率太大了,USB 口自动禁用,然后系统会不停尝试启用,然后系统就和硬件在那打架。。。。。。在这里提示一下,如果遇到有在字符终端不断出现下面这种信息(或是运行dmesg 出现这种信息),同时鼠标经常卡顿,键盘市场无响应的情况,应该检查USB 口或是其他端口是否有接入大功率外设。

# USB port 1 disabled by hub (EMI?), re-enabling...

再有就是关于 Apache2 服务器安装 Python 扩展的,搜索教程的话多半会发现给出的方法大多是说,去 mod_python 官网下载最新模块源码,然后编译,同时不要忘了指定 apxs 的路径。我不知道这种方法是适用于哪个版本的 Ubuntu ,至少在我的 Ubuntu 12.04 64 位上,连 Apache 配置文件的路径都对不上 ==。

所以安装方法类似于给 Apache2 安装 PHP 模块那样,直接终端执行下面的命令就行了。

# 安装mod_python 模块
sudo apt-get install libapache2-mod-python
# 生成模块的软链接
cd /etc/apache2/mods-enabled
sudo ln -s ../mods-available/mod_python.load mod_python.load

当然更详细的可以参考这里:ubuntu mod_python 安装 。在这里也有另一种方法:Ubuntu Apache2 Python的配置

中国天气网API

上次做飞信天气预报的时候简单搜索了一下中国天气网的 API ,当然是没有官方文档的,最初是谁挖掘出来的也不可考了。刚才在增加新功能的时候又顺手搜了一下相关资料,发现这个 API 还真不简单。中国天气网有三个 API 适用于不同场合的使用。

http://m.weather.com.cn/data/101050101.html 这个接口返回的格式如下。

阅读剩余部分 -

Python 批量下载煎蛋妹子图

上次不知道在哪里找到一个煎蛋妹子图地址,好像有5000 个图片地址来着?貌似是这里:http://www.funet8.com/5k-beauty-jiandan.html(时间太久我也不太清楚了,不过我同时还找到了这个:http://acora.cc/jandan-gril.live

然后补考完了么,感觉还不错,就把上次某人发的批量下载脚本顺手改了改(其实就是加上了了两个错误处理,中间有图片下载失败时不会直接退出而已)。代码存一下。

#! /usr/bin/python
# -*- coding: utf-8 -*-
import urllib
import os
import sys
try:
  if __name__ == '__main__':
    cur_dir = os.path.dirname(os.path.abspath(__file__))
    urlfile = os.path.join(cur_dir, sys.argv[1])
    f = open(urlfile, 'r+')
    print 'Input checked.\n'
    for line in f:
      print 'Downloading file ', line
      names = line.split('/')
      name = names[-1].strip()
      try:
        urllib.urlretrieve(line, os.path.join(cur_dir, name))
        print 'Done!\n'
      except IOError, e:
        print 'Error occured!', e, '\n'
except IndexError, e:
  print 'No input specified. Exiting...\n'

另外我还找到了这个:http://acora.cc/quantities-to-obtain-a-custom-omelette-sister-figure.live,作者把爬虫发布了,可以用这个在本地服务器上抓妹子图地址,存在文本文件里,一行一个地址,然后用上面那个脚本加上执行权限运行就行了。

# 加上执行权限
chmod +x getooxx.py
# 启动批量下载(假设存有地址的文本文件为 jandan.txt )
./getooxx.py jandan.txt

上半年账单统计

上半年账单发布,从2月到7月底的数据,除去学费,包括生活费和自己买书(非教科书)以及租主机的费用。到什么时候来把这个记账的专题做起来,再用JavaScript 把表格跑一遍出张图什么的。地址在:地址已失效 。ods 电子表格源文件会在专题做好之后放出。

等到下周会优化一下两个demo 的代码(主要是看到教主在微博上说谁污染原型就切谁小鸡鸡。。。),然后写两篇文章说说这两个东西。

Tab 切换

在写一个Chrome 的快速拨号扩展。起因是Chrome 默认的新标签页很让人头疼,没有快速拨号,那 8 个最常访问网站会随着一段时间的浏览习惯而动来动去。好的地方就是有网站缩略图,还有那个“最近关闭的标签页”挺好的。

缩略图这一块,现在是没有办法了。在Chromium 的Issues 里看到有人反映过,但是目前还是禁止扩展访问chrome://thumb/ 的,chrome://favicon/ 倒是可以访问,前提是在扩展的manifest 中请求chrome://favicon 权限。

“最近关闭的标签页”这个还真是没有想到具体实现方式,默认新标签页源码太多了,实在是没有耐心去完整地看一遍。(不过性能真的是优化得很好)现在在看Sexy Undo Close Tab 的代码。

扩展中使用到了我自己写的query 选择器,不过我昨天找到了一个非常优秀的替代品——司徒正美的Icarus,有很大的可能会使用它。还使用到了Mousetrap 来绑定快捷键(这货真的很方便的说),所以原来的Tab 切换函数不能用了。(因为用快捷键的话要能记住当前焦点在哪个Tab 或Item 上)记一下现在用的。(没有优化过,现在很容易报错。)

// Tab and item switch.
var Tab = {};
Tab.TabMax = 3;
Tab.ItemMax = 0;
Tab.nowTab = 1;
Tab.nowItem = 0;
Tab.switchToTab = function(targetTabId){
  var self = this;
  self.nowItem = 0;
  if(targetTabId != self.nowTab){
    $('#tab-controler').$('.now')[0].removeAttribute('class');
    $('#tab-controler').children[targetTabId-1].setAttribute('class', 'now');
    $('#fall-tabs').$('.now')[0].setAttribute('class', 'falltab');
    $('#fall-tabs').children[targetTabId-1].setAttribute('class', 'now');
    self.nowTab = targetTabId;
    self.ItemMax = $('#fall-tabs').children[self.nowTab-1].$('a').length;
  }
  return self.nowTab;
};
Tab.switchToItem = function(targetItemId){
  var self = this;
  if(targetItemId != self.nowItem){
    $('#fall-tabs').children[self.nowTab-1].$('a')[targetItemId-1].focus();
    self.nowItem = targetItemId;
  }
  return self.nowItem;
};

比较简单,其他的就不记了哈。

百度贴吧のHTML 实体符号

在学校的贴吧里看到这样一个帖子:谁的电脑能打出这两个字,你的电脑起码两万以上!!不信试试...

说实话这种帖子标题是非常可笑的,属于猫扑脑残帖的一种。楼主想说的其实是,百度屏蔽了“涅槃”这两个字,所以能发表这两个字回复的必然都是高级主。8楼随后贴出了一段JavaScript 代码迎合了这种需要。

(function (){
  var str=rich_postor._editor.getHtml();
  var out="";
  for(var i=0;i<str.length;i++){
    if(str.charCodeAt(i)<128){
      out+=str.charAt(i);
    }else{
      out+="&#"+str.charCodeAt(i)+";";
    }
  }
  rich_postor._editor.getHtml=function (){
    return out;
  };
  rich_postor._submit();
})();

这里貌似构造了一个闭包?鉴于我还完全没有参透闭包的奥妙,也完全不会使用闭包,我们先把它放一放。其实这段代码只做了一件事就是把字符转义为HTML 符号实体。

取自W3SCHOOL 的资料(虽然W3SCHOOL 有很多错误,但是这个没有错误):一些字符在 HTML 中拥有特殊的含义,比如小于号 (<) 用于定义 HTML 标签的开始。如果我们希望浏览器正确地显示这些字符,我们必须在 HTML 源码中插入字符实体。字符实体有三部分:一个和号 (&),一个实体名称,或者 # 和一个实体编号,以及一个分号 (;)。比如,要在 HTML 文档中显示小于号,我们需要这样写:&lt;或者 &#60;

HTML 字符实体本来是用来向HTML 文档中插入一些特殊字符用的,比如尖括号(&lt;&gt;),但是在这个例子中它把所有字符都转义成了符号实体,比如“涅槃”这两个汉字被转义成了&#28037;&#27075;

这样的话百度就不屏蔽了。

但是在HTML 文档到达浏览器的时候,浏览器会认出来这是一串符号实体,所以该是汉字的会还原成汉字。如果你查看一下源文件就会发现这两个字确实是符号实体了。

发现还真有很多人乐于发表这种帖子啊:【转】惊现百度超级漏洞,5亿万人只有60多个人能打出此字,...。。。果然非技术宅们容易被伪科学玩弄呢。

这个方法可以用来做一些好玩的事,比如发繁体的帖子之类的。

for 循环求值

虽说提倡“退吧保智商”,不过我时常还会去瞄两眼的,毕竟贴吧在去年我人生最低谷的时候帮助我走了上来。刚刚在贴吧里看到一个并不很起眼的帖子for循环求值

本来看到这种帖子根本都不会去点开的,天知道我今天下午是不是被热坏了脑子打开看了一下那张图片。大意就是说这样一个循环执行之后j 的值为什么等于4:

for(i = 0; i < 5; i ++) j = i ++;

下面回复的有说“for的写法没有括号默认执行第一句”,虽然没有听过这个说法,不过我表示赞同。平时谁会这样用for 循环啊,自增量放到循环体里面用的场景我还真没见过==|||。

解决这个问题很简单,F12 开发工具跑一下。

for(i = 0; i < 5; i ++){
  console.log("before: i="+i+"\n");
  j = i ++;
  console.log("after: j="+j+"\n");
  console.log("after: i="+i+"\n");
}
// 结果:
// before: i=0
// after: j=0
// after: i=1
// before: i=2
// after: j=2
// after: i=3
// before: i=4
// after: j=4
// after: i=5

这样看明白了没有?在作 a = b ++ 这种运算时,是这样的:a = b; b ++ 。在 for 循环里也是这样的,判断完 i < 5 之后,先执行循环体,然后 i 的值增加 1 ,再继续下一次循环。如果没有 j = i ++ 这一句,而是 j = i 的话,j 的值就是从 0 到 4 ;但是这里第一个循环,到 j = i ++ 这一句时, i 的值为 0 , j = i ++ 实际等价于 j = i; i = i + 1 ,这样 j 第一个值为 0 ,之后 i 的值增加 1 ,再之后循环体结束, i 的值加 1 (这是 for 那个小括号里的 i ++ 语句),此时 i 的值为 2 。

顺着这种逻辑推理下去,够明白了吧?

搞清楚之后,可以尝试一下下面这种变体。

for(i = 0; i < 5; ++i){
  console.log("before: i="+i+"\n");
  j = i ++;
  console.log("after: j="+j+"\n");
  console.log("after: i="+i+"\n");
}
// 结果:
// before: i=0
// after: j=0
// after: i=1
// before: i=2
// after: j=2
// after: i=3
// before: i=4
// after: j=4
// after: i=5

做过这个就会知道 for 循环的用法。在 for 循环里,自增运算符的位置其实无关紧要,自增量总是在整个循环体结束之后加 1 。可以参照 C 语言相关的书,或是这里:for 循环中的i++ 和 ++i有什么区别?

HoverHey Chrome扩展

昨天写成了第一个 Chrome 扩展之后,手痒痒,想一鼓作气再写几个。正好室友在看考研信息,那个网站上做了表格的 hover 效果,鼠标指针移上去会变换背景颜色,室友觉得这个效果不错。所以我就写了个这样的扩展。今天花了一天的时间改了改,现在可以调整颜色和过渡时间了。下载:http://www.qblog.kilu.net/source/HoverHey.crx。(没有钱,本来想放到 Webstore 上去的,发现要先交5美元的注册费)

记录一下一些代码。

// 改变鼠标所在元素背景色。
var highlight = function(e){
  e = e || window.event;
  var _now = e.target || e.srcElement;
  oriBg = _now.style.backgroundColor;
  _now.style.backgroundColor = rgba;
  _now.style.webkitTransition = "background-color "+duration+"s ease-in-out";
};
window.addEventListener('mouseover', highlight);
// 向背景页面请求设置中保存的数据,其实就是交换json 。(因为脚本是inject 到页面中使用的,不能读取自身扩展的localStorage )
chrome.extension.sendRequest({askfor: "rgba"}, function(response){
  rgba = response.RGBA;
  duration = response.duration;
});
// 背景页面响应请求,发送设置数据。
chrome.extension.onRequest.addListener(
  function(request, sender, sendResponse){
    if(request.askfor == "rgba"){
     sendResponse({"RGBA": rgba, "duration": duration});
   }else{
     sendResponse({"RGBA": rgba, "duration": duration});
   }
  }
);
// 设置页面,写入localStorage 。
_saveOptions = function(){
  var rgba = "rgba(" + red + ", " + green + ", " + blue + ", " + alpha + ")";
  localStorage.setItem("duration", duration);
};
// localStorage 的用法与window 的一个属性一样,只要存取直接量就行了,不需要记住相关的函数。

使用了 jQuery 的部分比较简单(在成功了之后才觉得挺简单的),没有记录在内。