iMacros脚本运行三天,爬取了40万行现代化支付行号

离题万里的开场

码农界有个著名的“三次法则 (rule of three)”:如果一段代码重复出现了三次,就要考虑抽出来写一个子程序,以便复用。这是条宝贵的法则,可以衍生出更多的强迫症版本,随随便便就能举出很多喜闻乐见的例子:

比如

  • 一个词在同一句话里出现三次就不能忍,必须换近义词;
  • 一件事手动做三次就不能忍,必须写程序自动化;
  • 一顿饭重复吃三口就不能忍,必须开发一个喂饭机;
  • 同一处空气重复呼吸三口就不能忍,必须装一台呼吸机

……

发人深省,对不对?这些正是当今最严肃而真实的信仰,有着最为坚定的践行者。在古代2015年全球最大的雄性交友平台GitHub上出了个网红毛子码农、脚本狂魔Narkoz,他的人生信条是:如果一件事要耗费自己90秒以上,那就写个脚本。这些奇葩的脚本包括:

脚本

  • 如加班到21点以后就自动给老婆发马屁短信;
  • 收到蠢货DBA的任何求助邮件后自动恢复数据库的最近备份
  • 让咖啡机等待17秒然后煮杯咖啡并等待24秒再灌入杯子(正好是作者起身走到咖啡机前的耗时)
  • ……

从时间效益的经济学评价来讲,这个准则烂透了。这好比为了节约每天通勤的公交车钱,去买了一辆跑车。但跑车本身还是很拉风的。若能竖立起极客死宅的品牌形象,还是可能会产生某些潜在的溢价——比如说,会有更多的人请你修电脑。

判断一个人是否天生适合当码农,很重要的一点就是看他/她有没有这种懒癌强迫症。而这种强迫症的形上学本质是:对自由王国的无尽向往。——重复劳动太蠢了,它侮辱人类的尊严,阻碍我们证法悟道,必须消灭。这就是自动化的诞生。

所以——尽管技术上跌跌撞撞,也并不妨碍我隔三差五搞个三脚猫爬虫出来。这回的主题又跳跃了:爬取现代化支付行号。

这就带来了第一个问题

蛤?这是啥?

【动态图版】北宋、明清进士空间可视化分析

注意

如果浏览器提示本文加载了不安全的脚本,请点允许。

今次主题比较简单。上个话题留了点冷饭,看起来还没馊,咱敲个鸡蛋炒个蛋炒饭。

炒个什么蛋炒饭呢?——动态图(dynamic charts)。这也是应留言要求额外发的番外。基本和分析关系不大,纯粹是可视化范畴。

动态图

通常我们看到的都是静态图,最常见的是.jpg、.png这类位图,逼格高一点的会用到.svg矢量图。但它们都是死图,所有图形元素都不会动。某些情况下,我们不仅要把统计结果映射到特定的视觉通道,还希望表现其历时性,或者允许用户自己进行挖掘。这就需要让图形部件动起来。

  • 一种方法是做成动画(animation)。

R有个名包,叫animation,可以用它压制.gif,用在社交媒体效果足够醒目。它的基本思路就是拿出一维来映射时间,基于时间点对数据切片、统计、制图,最后把静态图们合成一个动画。

  • 还有一种方法是交互图(interactive charts)。

把统计数据绑定到JavaScript控件上,定义好交互方法,用户即可在网页上通过控件操作来调整视觉呈现(切片、缩放、改变类型等)。RStudio发过一个工具框架包htmlwidgets,可以很方便地把已有的JavaScript可视化库移植到R。我们今天就要用到其中的两个:ECharts2和leaflet。

如果再进一步,就是数据交互面板了。R有shiny及其系列衍生品,比如flexboard。想象一下作战室交互图仪表盘面板,几行命令就做出来了。简直酷炫。但是这需要部署在shiny服务器上。

北宋、明清进士空间分析

哈佛人文地理可视化平台

周末找在线GIS素材,误打误撞进到了worldmap.harvard.edu——一座名副其实的学术宝库。它是哈佛大学搞的一个开源地理可视化平台,社会、经济、历史学科的学者可以自己创建、上传数据集,方便地做多图层地理可视化。这个设计充满学术理想,也为有志于成为世界一流名校的大学树立了一座看得见摸得着的价值标杆:卓越、开放、自治、共享

中国地图 <worldmap.harvard.edu/chinamap> 是其中一个子站,已经上线多个数据层,包括社会/人口、经济、交通、能源、环境/气候、公共卫生,甚至历史地图。比如下面的视图,就是细化到县级的人口密度热力图,默认叠加在OpenStreetMap底图上,效果非常棒。

图 | 哈佛中国地图

让我特别感兴趣的是历史地图部分。上面赫然列着北宋、明、清的进士散点图和热力图。当把它们和当今人口密度热力图叠加显示,我们会惊讶地发现过去一千年来盛产进士的地方,几乎严丝合缝地对应着今天中国人口最稠密的地区。进士分布只能指示那个时代的财富分布。但把千年以来的进士之乡叠加起来(如同叠加起无数个财富变迁的历史断面),就能看到这些财富分布的残影。而古今之间的这种辉映,说明今天的人们依然在从宋以降的这种“地气”大格局中受惠。

右键这三个进士数据层,选择“Share Layer”,就能看到一个分享页面。除了视图本身,还有参考数据集的链接,可以导出多种格式。我们导一个Excel出来。Excel的名称是CBDB_exams_NSong_WGS84_kto.xls。

图 | CBDB科举数据集

这就十分厉害了:

  1. CBDB,是大名鼎鼎的哈佛中国历代人物传记数据库(China Biographical Database)。这个图层的基础,来自CBDB,可靠性就有保障了。这个数据库内容非常丰富,后面还会用到;
  2. WGS84,说明坐标系用的是WGS-84坐标系,所以只要利用基于WGS-84的GIS工具,就不需要针对GCJ-02或BD-09做逆偏置了。

一次不太成功的搬砖(下):rvest爬取法定传染病疫情月报数据

甲乙丙类每月发病、死亡数

接上篇

1
library(data.table)

看一下甲乙丙类每个月的发病和死亡例数。

1
2
3
sta <- dcast(dat, 日期 ~ 分类, sum, value.var="发病数")
sta <- melt(sta[,names(sta) != "NA"], id="日期", variable.name="分类")
makeTsPlot(sta, "法定传染病每月发病数", xlab="年月", ylab="例数")

图 | 法定传染病每月发病数

1
2
3
sta <- dcast(dat, 日期 ~ 分类, sum, value.var="死亡数")
sta <- melt(sta[,names(sta) != "NA"], id="日期", variable.name="分类")
makeTsPlot(sta, "法定传染病每月死亡数", xlab="年月", ylab="例数")

图 | 法定传染病每月死亡数

甲类数字很少,看不太出。而不论乙类还是丙类,发病高峰都在春夏季。死亡高峰却在冬季。

按月算一下均数,看得更清楚。

1
2
3
4
5
sta <- dcast(dat, format(日期, "%m") ~ 分类, mean, value.var="发病数")
names(sta)[1] <- "月份"
sta <- melt(sta[,1:4], id="月份", variable.name="分类")
sta$月份 <- as.integer(sta$月份)
makeTsPlot(sta, "法定传染病平均月发病数", unit=1, ylab="平均例数", xvar="月份")

图 | 法定传染病月平均发病数

1
2
3
4
5
sta <- dcast(dat, format(日期, "%m") ~ 分类, mean, value.var="死亡数")
names(sta)[1] <- "月份"
sta <- melt(sta[,1:4], id="月份", variable.name="分类")
sta$月份 <- as.integer(sta$月份)
makeTsPlot(sta, "法定传染病平均月死亡数", unit=1, ylab="平均例数", xvar="月份")

图 | 法定传染病平均月死亡数

一次不太成功的搬砖(中):rvest爬取法定传染病疫情月报数据

数据提取

接上篇

现在,可以着手把存储在附件里的信息结构化提取出来了。但在这之前,还有一个硬骨头要啃。

要把图片附件识别成文本。

首先考虑OCR。但是Abbyy Finereader似乎没有Ubuntu版本。其他一些主流工具要钱。网上找到几个免费OCR工具,试用了下,转出来一堆乱码亲妈都唔识得。一怒之下,放了个大招:

手工录入。

这项工作很不好做,让我不禁怀疑起人生。但只有经过这样的磨练,才能对疾控系统的信息化水平有一个实操层面的认识。倘若遇到这方面的项目机会,记得要把工程预算乘以3

图片方面的坑包括:

  1. 有些图片附件分辨率低到了厚马赛克水准,别说OCR,钛金狗眼也认不出
  2. 有些表格作为OLE对象内嵌到了Word文件里,当我满怀希望点进去才发现,这个内嵌对象竟仍是个图片
  3. 有个别文件特别贴心地把表格割成两张图,插到了正文里

满脸辛酸地处理完了这些杂碎,把doc和xls存作docx和xlsx,接下来总算能把它们当成正常的xml来处理了。

一次不太成功的搬砖(上):爬取法定传染病疫情月报数据

带怀旧色彩的源起

清明节跑去一个休闲浴场鬼混,在电影厅懒散地看掉了《生化危机6》。场地很豪华(但我就是不透露门牌地址),然而剧情不怎么样——女主光环实在太亮了。倒是病毒-丧尸-疫苗的急性传染病建模设定引起了我的一些职业回忆。

毕业后,我曾在基层疾控中心干过一年多,主要做疫苗接种规划和传染病控制。除了定期不定期地出外勤下现场,就是统计数字、写报告、汇编材料。这些数字沿着行政金字塔的梯级层层上卷,最终汇入国家卫生部疾控局官方报表的大海中。

说是大海,视觉上其实就是类似这样的一张表格:

图 | 法定传染病统计表

一晃很多年过去了。籍着这个由头,我又登上了卫生部(现在叫卫计委了,早晚改回卫生部)的官网,那感觉就像——拜会一个久寓故居,新近敲了墙、刷了房门的老派的朋友。那些月报还原封不动,化石一样静静地躺在信息动态里。

图 | 卫计委传染病控制动态

图 | 法定传染病月报

这种格式报告,行文和结构都很固定,特别适合用机器人来自动生成。比如最新这期,正文就包括了发病、死亡合计总数,以及甲乙丙类各自的发病、死亡数。明细数据放在附表里。掐指一数,从2004年到现在,卫计委也积攒了140多份月报,不少了。何不爬下来看看?

所以,尽管当时身还在浴场,但心在砖场了,已经!

礼法两难的辱母杀人案

炸锅

平静无奇的周末,出了一则摇撼人心的新闻

新闻不大。简单说来就一句话:

案情

一对母子被催债黑帮拘禁、侮辱,儿子愤而刀刺讨债者致一死多伤。法院一审判以无期徒刑。

就这?“杀人者死,伤人者刑”的原则,两千年来大家都认。摘要看起来没毛病啊,摇撼啥呢?

把折叠的关键词徐徐展开,细节立体而狰狞。女企业家向当地某富豪借了135万高利贷,偿还184万现金+70万房产后还是不够(月息10%)。于是债主找了11个黑社会去讨债。怎么讨的呢?先到厂里骚扰,堵门、埋灶,按送葬的标准来闹。次日,黑社会将母子俩拘禁在办公室:辱骂、掌掴、摁马桶、鞋子熏,带头人还当着其中儿子的面脱下裤子侮辱其母。施暴不断升级的同时,债务人做了什么?当然是良民的自救SOP:第一日拨打110和市长热线,第二日电话报警。但热线没解决问题,警察只来刷了个脸,告诫黑社会“要账可以,不许动手打人”,便启动警车要走(事后称去进一步了解情况)。女企业家的儿子当即失控,拿起水果刀刺死、刺伤该团伙多人

该事件被冠以“辱母杀人案”,在社交媒体上快速发酵。这个热词目前在百度、微博、微信都还搜不到(或者已经被屏蔽),但以之前并不出名的事发地“冠县”为题搜索,热词趋势如下:

朝左还是朝右?来看一份17万行的小样本数据

回味2016

2017年已经热火朝天地过掉两个月。现在回过头去评价2016年,显得反射弧特别长。但我仍禁不住要多回头看两眼,因为直到现在,我还不能完全理解它。

表面看,鹄年一声炮响,送来了英国脱欧和Trump,以及无数脍炙人口的自媒体段子。华尔街全球化秩序的崩裂,给国际政治投下两个巨大的阴影。这源自30年前苏东剧变和中国改开,全球化捡到两个巨大的劳动力要素价值洼地,于是轰鸣运转、财源滚滚。而今红利渐渐吃尽,而劳动生产率其实并没有什么革命性增长,资本回报率一天不如一天,旧模式不免穷途末路。而它带来的副产物(贫富分化、难民危机、恐怖主义、债务危机)却开始反噬全球化主导经济体自身。所以在可以预计的未来,崩裂还将加速,阴影也将进一步扩大。

但崩裂成什么模样,并不好说。布雷顿森林体系崩裂,其实并没有闹出什么大浪来。但凡尔赛体系的崩裂,就直接导致了第二次世界大战。西晋帝国体系的崩溃,带来了长达五百年的大分裂。如果把回望尺度放大一点,2016年可能只是历史进程的左右摇摆周期中的一小部分。19世纪后期西方自由主义大发展,对内损害了平等,对外制造了殖民,结果殖民地红利吃尽后,在20世纪催生了两次世界大战,以及追求平等的共运和反殖民运动。然而平等主义在吃完战后红利就逐渐陷入了极权和经济乏力,于是新自由主义回潮。这波回潮绵延至今(构成了我们这代人迄今人生经历的主体),如今同样碰到了瓶颈,于是向平等转向的声音又高涨起来。每一次左右摇摆,几乎都伴随着战争、动乱和政经格局的洗牌。

每一次摇摆,大输家都是那些共识建设较差的社会。如果社会共识不能应付新的形势,或在形势变化面前发生断裂,都会增加内生动乱的风险。而赢家,则是把自身共识引领到世界的那个社会。

问题来了,

瑞典挪威逐光七日记(6):Stockholm

2月6日

上午坐飞机回到股票岛。​旅程进入收官阶段。

和去Tromsø一样,离开Tromsø同样花掉了几乎一天的时间。原因是这样,到Arlanda机场已经三点多了,第二天下午五点多的飞机回国,基本上用在股票岛的时间不超过24小时。这不是启用24小时票的绝佳场合吗?于是,为了省钱​,我们买了张24小时票,浩浩荡荡向市中心挺进。

但噩耗是,24小时票可以用于大斯德哥尔摩地区的所有公共交通——公交车、地铁——除了机场大巴和机场特快之外。基本上,就是一张“除了刺身其他都可以畅吃的自助餐优惠券”。

无可奈何,搭上一辆公交车,向西坐到迈什塔(Märsta​),再转通勤轨交(pendeltåg,读音像“偏头陀”),哐当哐当坐了一个多小时才来到西北郊的索尔纳(Solna,大名鼎鼎的Karolinska医学院就在Solna和斯德哥尔摩交界)。又转公交,开到酒店附近。

​已经六点,天又全黑了。看到路边有家超市,卖熟食和沙拉,就点了些土豆泥、肉和奶油汤。令人惊喜的是,还卖炒米,店员看起来像印度人,果然是产米区出来的,不忘本!赶紧买了些。结果吃进嘴里差点吐了,这米兼具夹生和过火两种属性,又干又硬。作为印度人未免太给你们祖国丢脸了吧。

​吃完饭,check in,放好行李。电视里都是瑞典语。做点什么呢?

​干脆把24小时票用到极致——去坐地铁玩。

瑞典挪威逐光七日记(5):Tromsø

不是逐光游吗,说好的光呢?

​不要急,这就来。

极光

前次赏光效果不佳,这次决定挂靠个组织。​晚上花1000克朗/人报了个极光团。这是个个体户极光团,所有资产就一辆面包车。观测点都在免费的野地,没有营地也没有帐篷。导游是个加拿大出生的澳大利亚人,跑到Tromso做电影和艺术会展,兼职做做导游。

当天天气很好,各项预测指数都爆表,看到极光的几率很大。8点钟,司机把车开到主岛南端的小码头,一群人​顺着结冰的路面走到埠头上,架起三脚架,开始朝西看。此处虽然离市中心只有几公里,但位置很讨巧,灯光已经非常稀疏,利于观测。

我以为得等上个两三小时,结果很快夜空就起了变化。肉眼看到的是薄雾一般的暗光,但微微泛绿,快速地变化形状。光带的上端是闪烁的向上冲的芒刺。​这无疑就是极光了。

这未免太容易了吧?真不敢相信。跑这么点路就能看这么清楚,真是知识创造财富。​

由于没有三脚架,只能用手端着手机延时曝光,画面糊成印象派。​

​司机很有经验,调好相机参数,先丢一发暗闪光,然后再按快门,就能同时捕捉到极光和人像,明暗对比非常合适。

​看了半个多小时,再次发车,开过Sandnessund桥,绕到城北Kvaløya岛的一个山坳里。山体挡住了月亮,夜空显得格外黑。在这里,极光分外妖娆。时而横贯夜空,时而星芒绽放,时而交织掩映,时而轻纱环笼。极光的变换非常迅速,突然间盈满夜空,甚至会旋转和舞动,但倏忽间又消隐不见。

图:极光