利用微信公众平台实现手机命令行运维

前几天玩了一下微信公众平台, 取名: 我的公司名, 哈哈, 单凭这个名字竟然每天吸引了数十人的关注.

公众平台的规定是, 关注人数达到 500 人就可以申请认证. 我觉得我可能不需要发一句话, 就可以达到这个认证标准了, 哈哈.

公众平台有两种模式, 一种是编辑模式, 只能实现有限的几种对话模式, 定义好一些关键字, 如果用户发了这些关键字, 就回复事先指定好的回复.

一种是开发模式, 你发一句话给公众帐号, 微信服务器就帮你把相关的内容发送到你定义好的服务器, 然后你的服务器处理完, 就可以返回相应的信息给你了.

选择开发模式.

好了, 我的玩法是, 我用我自己的普通帐号发一个命令行的命令给公众帐号, 然后微信把这个命令发送给我之前定义好的服务器, 然后我的服务器就执行完这个命令, 把结果按照微信规定的格式返回, 然后公众帐号就返回那个结果给我了.

首先, 所谓的网址接入, 文档在这里: http://mp.weixin.qq.com/wiki/index.php?title=消息接口指南 . 照着做吧.

然后, 请准备好服务器程序, 我花了几十分钟用 tornado 写了个简单的接收 POST 请求的接口, 关键处理逻辑部分如下:

def post(self):
  dom = parseString(self.request.body)
  root = dom.documentElement
 
  toUserName = root.getElementsByTagName("ToUserName")[0].firstChild.nodeValue
  fromUserName = root.getElementsByTagName("FromUserName")[0].firstChild.nodeValue
  createTime = root.getElementsByTagName("CreateTime")[0].firstChild.nodeValue
  msgType = root.getElementsByTagName("MsgType")[0].firstChild.nodeValue
  content = root.getElementsByTagName("Content")[0].firstChild.nodeValue
  msgId = root.getElementsByTagName("MsgId")[0].firstChild.nodeValue
 
  print toUserName, fromUserName, createTime, msgType, content, msgId
  cmd = os.popen(content).readlines()[0]
 
response = """
 <xml>
 <ToUserName><![CDATA[%s]]></ToUserName>
 <FromUserName><![CDATA[%s]]></FromUserName>
 <CreateTime>%s</CreateTime>
 <MsgType><![CDATA[text]]></MsgType>
 <Content><![CDATA[%s]]></Content>
 <FuncFlag>0</FuncFlag>
 </xml>
  """ % (fromUserName, toUserName, createTime, cmd)
  self.write(response)

再然后, 就向公众帐号发命令试试吧. 如图:

2013-03-05 17.42.52

 

发挥想象还可以玩出很多不同的花样呢. 例如, ssh 到别的机器来完成某种命令再返回.

我玩什么都是三分钟热度, 这个也就玩玩, 不打算深入研究. 就这样了.

TPB AFK

TPB AKF (The Pirate Bay Away from Keyboard)

我不能完全认同他们三人的行为,但觉得他们很酷,甚至非常羡慕他们的能力和所在的环境(其中一个环境是:有个政党保护着他们的服务器)。

看到他们的机房,服务器摆设,闪动的开机画面。– 兴奋吗?

General speaking, for storage u use byte and when u measure speed u use bit. — 我也是这样强迫周围的人。

When did u meet IRL (In Real Life) ? We don’t use the expression IRL. We say AFK (Away from Keyboard). We think that Internet is for real. — 曾几何时,我也是这样认为的。这个也是影片的名字来源。

影片中为他们做证人的教授说了一句话:I support copyright, only if it encourages creativity or economic incitement or is an incentive to create. Not the way copyright as a huge control mechanism for people sit on large swathes of rights.

你会看到一个天才在默写 Pi ,一直写到了小数点后的好几十位。这个天才用的是 ubuntu 。

It’s a pleasure to announce that the Pirate Bay is back online!! In your face, Hollywood !!

三人中的 Fredrik 喝了两杯后就醉,说起酒话来好可爱,你会发现他们就像一帮无害的大小孩。

影片最后,

Please share this film online

 

 

 

puppet 在 mongodb 的机器上卡死

好久没写技术的内容了, 要长草了… 随便写一篇今天的吧 ( 同事发现的…我只是搬运工 )

现象: 在部分跑着 mongodb 的机器上跑 puppet , 巨卡, 竟然需要跑好几十分钟, 并且 top 看起来还是 system 占用的 cpu 资源而不是 user .

解决:

因为众所周知的原因, 跑 mongodb 时必须关闭 NUMA 来启动. 看了以下这个页面, 才发现还需要关闭 zone_reclaim_mode. 因为此值默认总是关闭的, 所以一直没有为意.

http://docs.mongodb.org/manual/administration/production-notes/#production-numa

因为某些原因, 我们上周开启了 /proc/sys/vm/zone_reclaim_mode, 导致的问题.

http://www.kernel.org/doc/Documentation/sysctl/vm.txt , 已经提醒过大家, 最好关闭了.

http://www.poempelfox.de/blog/2010/03/, 解释最全了. 阐述了多种症状, 我们都命中了…例如一个普通的系统调用需时甚长.

唉, 关闭了 zone_reclaim_mode 就好了.

BLOG 迁移到新域名

终于买到 hellosa.org 的域名了… helosa.org 这个域名就慢慢退出历史舞台吧 …

买了域名才发现, 原来 helosa.org 是在 3 年前的同一天购买的, 真巧.

好了, blog 迁移到新域名 !!

 

AD time: 本人可以提供个人及小型办公室爬篱笆服务, 有兴趣联系我啊

西游观影

2.10 上映,其实我当天就可以去看了,不过既然是我很喜欢的星爷,我决定选个特别的日子才去看,我选了情人节。(据说当天的票房大爆灯。。。)

我看的是粤语版,估计和国语版的区别甚大,后者很影响效果。但观影过程中发现,好像都是后期配音的啊,演员在拍摄时都是用国语的吗?又是一大影响。。。

我的总的感觉是:没有期待中好看,也没有意料中的烂。

演员们都在重复着星爷当年的无厘头套路,但是,我发现,可能无厘头一词只属于一个人,其他的演绎,都觉得生硬。尤其是空虚公子那段,空虚肾虚的来回解释,其实有点烦了。

虽然说仿佛看到了当年星爷的无厘头,但是对白老套啊,对于我这种爱好者,你说出第一句,我都可以帮你接下一句了,连语调都可以模仿得一模一样。

有些地方,明知道是想观众笑的,但我也笑不出来。不知道是人长大了,还是演员变了。

踏入 2000 年后,星爷貌似爱上了电影特技,少林足球、功夫等,无一不是在炫特技。我还记得那个为风云1(当时特技水平十分领先)做特技的先涛数码特技有限公司呢,它也为少林足球做的特技。

结局部分尤其硬伤,全部在 show 特技了,没有情节了!!仙履奇缘的点睛之笔,令其成为经典,这次就太差了。

———————-

看完后,我又再温习了一次《大话西游》,感慨。

让我们回到那个再也回不去的时代。

当年我看《大话西游》的时候,哪懂什么爱情不爱情的,只会笑。

翡翠台每逢周六晚上 9:30 就会播一部旧电影,《大话西游》第一集叫:西游记第一百零一回之月光宝盒,什么乱七八糟的啊!第二集叫:西游记完结篇仙履奇缘。而且还在第二个星期的周六才播!那时候的一个星期是很漫长的。

因为有 80 年代央视版《西游记》的思维定势,看完两集我都觉得很乱很乱,为什么和我认识中的西游记区别这么大呢?

逐渐长大,后来又看了好多次《大话西游》,看到了更多的东西。配乐超赞,天地孤影任我行(又是取材于王家卫。。。)和一生所爱,都令到电影本身升华不少。

现在的电影,再也凑不齐那么多的明星了,(蓝洁瑛啊!!!)现在那些明星,气质上输几条街!!

—————————

后来,又看了一些影评,发现大家褒贬不一,尤其是贬的部分,举出了 n 个证据来证明电影的烂。

我决定从一个脑残粉的角度,去辩驳一下那些所谓的观点!!!

说电影血腥重口味的,不想想功夫里也有一幕挺血腥的?火云邪神把斧头帮帮主的头打得转了几个圈的那幕!(如果开头那场,鱼怪把小女孩给咬断成两截。。。)

说男女主角爱情来得唐突的,不想想大话西游里的爱情也是不知道为什么就爱你一万年了吗??电影的时间有限,要表达的东西那么多。

徒弟三人不知道为什么就归顺了,那就当是降伏了,就归顺了吧!!!

——————————-

脑残过度了,哈哈,有些地方确实很难说得过去的。也就是因为周星驰这个名字,让大家对其期望值,远超于电影本身了。所以各种吹毛求疵的评论。

整个电影确实想象力很丰富的了。从《西游记》的故事框架,再颠覆出一个神魔鬼怪的世界。天残脚的出招,真的想不到是这样的。空虚公子对孙悟空,你们没看过粤语残片的,看到他的出招能知道取材于哪里吗??星爷把它现实化了啊!!星爷是有多爱粤语残片啊!!

我本来对舒淇无感的,但看完这个电影,大爱啊!!两个画面,一个是她的《一生所爱》,一个是玄装刚出洞口,幻想中的舒淇,从远处一缕白衣轻跑过来,那个笑容,killing me 啊女神!!

最后黄渤那个造型。。。明显取材于《七龙珠》的孙悟空了,这里不太喜欢。

PS: 正巧是这个假期我才发现,《破坏之王》里面的那招无敌风火轮,竟然是取材于《行运超人》的努力超人的必杀。。。

2012

2012,对于我来说,实在是十分难忘的一年,这一年,很多很多都改变了。

人开始回忆,证明已经开始老了。可是,我的心态又很幼稚,甚至拒绝成熟,所以总在矛盾中挣扎。

2012 年发生在我身上的两件大事:从知乎离职去了陌陌,和女朋友分手。

就从知乎说起吧。http://www.zhihu.com/question/20029279/answer/13767154,这个是我在知乎回答的问题中,赞同票数最多的一个。现在回头来看,这不是年少轻狂,反倒觉得自己很幼稚,想法不考虑后果和现状。但是我不会 update 这个答案了,因为如果让我再年轻一次,我可能还是会这样选择,选择了就不后悔了。这个回答的第一个评论,真美好。

以下,可能对我的旧东家略有微词,但仅代表我本人在当时的想法,我个人对所有的旧东家都是很有感情的。在知乎后期的工作,令我觉得很疲累,疲累到觉得自己做的事情总是没有意义,觉得每天晚上忙到深夜没有意义,甚至已经开始影响到了我的生活了,那就是变得没有生活。而且个人感觉上,我当时的技能,并不是知乎所必须的,未来也不是,甚至,其实根本不需要我。那时我想到了离开。

为什么是陌陌呢?这个是我当时被问得最多的一个问题。。。话说我当年,离职网易后,第一个想法是加入陌陌。当时可能连陌陌这个名字还没有,我没有看中它的产品(当时乃至现在产品的 sense 还是很贫乏),其实也还不知道是什么产品,就觉得当时的室友,同学,同事都在陌陌,只看人,就想加入了。可惜,当时貌似还真的不需要我的技能,就没去成了。

工作中,我的心态也终于有了大变化。我以前更多的是为了技术而技术,瞬间,眼界都开阔了不少。

这个选择,从现在的情况来看,也算是正确的。我的生活比那时候轻松了,工作也有了更广的接触,更深入的发展。事业,或者说职业上,算是得意。通常来说,这个时候会轮到情场失意了。

《春娇与志明》里面一句话:“我一直好努力在逃避张志明,谁知原来自己已经变成另外一个张志明”。她在看完后的落泪,直到分手后我才终于体会到。或许我就像张志明般就是长不大。电影始终是电影,志明可以通过一段这样的视频追回春娇,而我即使在楼下等到三点也无法再次换来一面。每当寂静一个人时,就会单曲循环王馨平的《别问我是谁》。

我在电子产品上的追新和我骨子里的念旧,又是另外一个矛盾。搬家时,我选择了她的旧住址双井。每天中午吃饭,也会经过和她第一次见面的世贸天阶。每当回家打车,也会选择以前一起等车的那个十字路口,尽管那里并不好打车。除了共同的朋友们,还有更多的是生活上,不知不觉间也染上了她的习惯。在收拾旧家时,在整理旧相册时,往事一幕一幕,放佛在时光机中观赏着记忆。情人分手后一般会丢弃一些东西,删除各种网上的关系,但对于记忆,却找不到方法去 delete。想起她陪我走过了我的职业生涯比较苦逼的一段时间,而我什么都没有带给她,甚至连一段视频都没有。

《女朋友 男朋友》中,看得最痛心的就是林美宝在机场做爱心姿势的一幕,因为那个姿势我看她做过两次。

还是那句老话,失去了才知道珍惜。我真是一个活在回忆里的人,总想回到过去,但回到过去的时间点太多了。我只好相信,一切向前的都是好的,尘封回忆,重新振作向前。

感情的问题导致了我低落了很长一段时间,直到 12 月,才重新恢复过来。用朋友的话,我终于变回正常人了。

陌陌总算在这个假期上没有犯傻,从 28 号开始放假,7 天!可惜,幸福来得真的太突然了,完全没有准备,虽然我很有上演真人版泰囧的冲动,但是考虑到荷包和春节临近的问题,决定缓一下,2013 年,总会完成一次一个人的旅行的。

两部电影

blog 尘封了好长时间。。。因为感情问题,低落了好长一段时间,最近才从低谷中走了出来。

为了证明我也撑过了末日,随记一篇

上周看了两部电影:《一路向西》《女朋友 男朋友》
《一路向西》我等了三个月,一部三级片竟然要三个月才出 DVD ,有你的!!中间因为期待,还专门看完了小说版。小说给我的感觉就是,假装文艺,有那么一点港人的无聊。电影版是改篇了不少,但这回是充斥着港人的无聊了,为了三级而三级,为了裸露而裸露。个人觉得很差,比起今年的《低俗喜剧》,差太远了。

 

《女朋友 男朋友》,因为桂纶镁的影后才关注到这部片。我一向不太看文艺片,因为就一俗人,经常看不懂。当初还以为单纯是台湾小清新青春片,谁知道是就一文艺片。。。看着纠结难受。
小清新的开场,林美宝、陈忠良、王心仁三人的青春无敌。夏天、暑假、叛逆,台湾小清新都这调调。三人成长的时代,伴随着台湾追逐民主自由,一起成长。学生时代总是很单纯的,没有夹杂着爱情的三人的友谊是最干净美好的。
王心仁对民主自由的追逐,在当时来说,无疑是最有魅力的。难怪两位主角都喜欢他。后来的他,在岳丈部长面前,像狗一样的低头,一前一后的对比,理想和现实的对比。现实中,大多数人何尝不是选择了低头。

 

一直不太懂林美宝给陈忠良那封空白的信的含义,他看后先笑后哭,据说空白信有“告白”的含义。
不断重放的三人骑着摩托车在林荫道上奔驰的片段,是三人最快乐的时光,可惜再也回不去了。
最为痛心的镜头,机场,林美宝和王心仁正准备离开台湾。美宝看出了心仁的真心,临别前,强颜欢笑地跳出了她当年答应他的追求时的舞步,并做出爱心的姿势,尽管再也换不来心仁的爱心回应。她带着微笑转身。伴随着黃韵玲的插曲,直逼人掉泪。

 

我喜欢的对白:
虽然我不是主打歌,但我是 B 面第一首。
我想你这样一直抱我,一直抱我,很久很久,不要放开好不好。(这句话在电影中重复了多次,但到最后,王心仁已经忘记了拥抱的感觉了)
如果我们是浪,眼泪都从海里来;我们是花,长在一棵树上,只在不同季节盛开。
我们说过的“改天”,应该不是一般人的“改天”吧。
这么多年,谢谢你的照顾。
他一直跟我说,全世界只有我肯为他吃苦,但其实,我们都在自讨苦吃。(我一般表达为,恋爱中的人,都是犯贱的)

 

“我们都有两个情人,一个爱我的,一个我爱的”。这是一部好片。

入手 rMBP 的第一天

Retina MacBook Pro ,只能说太帅了,哈哈

我机器拿到手,到完全可用,大概花了 4 个小时吧,从旧的 MBP 基本整个完全 copy 过来,这个时间还好啦。过程中有点小折腾,有些地方不知道具体原因,就不特意去重现了,就当 mark 下我的过程吧。

1)预计好我的到货时间,这几天我每天都在 Time Machine 。但有时候很奇怪,我两小时的使用中,也就上上网,vim 一下,竟然一次 Time Machine 要备份 2G 的东西。。。

2)机器到手!开机!经过简单的设置后,选择 restore from time machine ,企图从我昨晚的备份恢复进去新的 rMBP 中。

3)TM 的恢复时间,139G 左右的数据,恢复耗时 2.5 小时,属于可接受的正常范围。

4)正常开机,发现我的账号密码一直提示不正确。。。准备重启进入单用户模式修改密码

5)重启进入 single user mode ( 开机过程按 command + s ) ,并输入以下命令才可以正常修改密码

mount -uw /

launchctl load /System/Library/LaunchDaemons/com.apple.opendirectoryd.plist ( 如不执行这句,会爆  The daemon encountered an error processing request. 的错误)

然后就可以正常地 passwd [username] 来修改密码了

6)终于进入系统,但发现,系统竟然还是 10.7.4 ,我的旧系统已经是 10.8 了。这里我怀疑是,TM 并不会把系统也恢复进去。。。

7)重新安装 MountainLion ,及其相关的一些软件。如 command line tools for xcode , java run time , iwork , imovie 等等等。

8)最后,终于可以使用了!TM 唯一没有成功恢复进来的软件是 ClipMenu.app (失败的日志它会放在 Desktop 上),重装一下就好。基本和我原来的系统差不多了!

 

从第 4 点开始出乎我意料地不正常,导致我又花了点时间去折腾好。

 

作为敝司第一台 15” 的 MacBook ,兼第一台 rMBP ,我决定发一张工作桌面照

工作桌面

广告时间,我正在出手我的旧 MBP :http://v2ex.com/t/43849

2.6.29 – 2.6.39 之间的 kernel 不要在 LVS Director 的网卡开启 GRO

我之前开发了一个传图系统,直接使用了 @agentzh 的 lua_resty_upload (其实这里不应该用 nginx_lua 做的),但发现其在 LVS 下,POST 请求总是 socket timeout ,而直接 POST 到 real server 没有问题。无奈我当时只好用 DNS 轮训的方式,终于在最近找到原因了。

Linux 在 2.6.29 的时候,引入了一个 GRO (Generic receive offload) 。

MTU 一般都是 1500 字节,如果一个包超过了 MTU ,就会被分片。1500 这个数字,估计是基于当时的网络环境制定的,而现在,10Gbps 的网卡都普遍使用了,可能就不太适用了。如果 10Gbps 的网卡满载地来跑,一个完整的数据包会被分片 800w 片。我们可以通过调整 client 和 server 端的 MTU 令到分片尽可能减少,提高吞吐量。但是,如果 client 端(例如用户)不受我们控制呢,那我们就无法提高性能了。于是有人想到通过网卡的行为来间接实现相当于提高 MTU 的作用,这就是 GRO [1] 。

GRO 就是在网卡中将满足一定的条件(比较严格)的包,将分片的包组装合并了,才一次性交给上面的协议栈。现在的网卡一般都支持了,除了网卡支持,还要驱动也支持才可以。如果网卡和驱动都支持,那么在 2.6.29 以后的 kernel ,都会默认开启。

ethtool -k eth0 ,来查看是否有 generic-receive-offload: on ,如果是 off ,也不一定是不支持,可以通过 ethtool -K eth0 gro on 来尝试开启。

但是 GRO 和 LVS 协作得并不好,具体表现就是,POST 数据到 LVS 很慢。

抓包看我的 POST 请求,握手阶段用了较长时间,出现了数次 incorrect 后才真正开始传输。POST 小于 MTU 的数据,并不会触发这个问题,而 POST 大于 MTU 的数据,就会。证明了这里肯定是 GRO 惹的祸。

没有 google 到最根本的原因,但也有一些说法 [2] ,就是 GRO 和 LVS 之间的兼容没有做好,知道 2.6.39 已经修复了这个问题。

经验主义一点,就是 LVS 的 director 一律关闭网卡的 GRO 。

ethtool -K eth0 gro off

这个情况我遇到的,一般发生在内网 POST 的时候,而用户 POST 给我的情况太慢的不太多,也有可能是他们网络根本就慢,手机的网络环境变数太多,而我暂时也没有办法把他们过滤出来一一查看。我猜想,是因为内网的网络环境正好符合了 GRO 的 merge 的条件,GRO 起作用了,所以我的 POST 很慢。而用户的请求不符合条件,所以还是比较正常的。

 

 

Reference:

[1] http://lwn.net/Articles/358910/

[2] http://archive.linuxvirtualserver.org/html/lvs-users/2011-05/msg00004.html

几个关于 python package (objc , PIL )的 bug

最近在处理一个图片需求的时候,竟然遇到数个几年了都没人修正的 bug。。。这个过程值得记录一下。。。

原始需求是这样的:

首先,是用一个 ImageAlpha 的 app ,这个 app 其实做得不错的,但是因为基于 GUI ,如果我要处理 1000 张图片,那会累死人的

但是。。。这个 app 竟然还不能直接使用,需要安装 pyobjc ,至少在我的 MacOSX 上需要安装。

1)第一个 bug ,pip 安装 pyobjc

pip install pyobjc

报错:

 
Downloading/unpacking PyObjC
 
Running setup.py egg_info for package PyObjC
 
Downloading/unpacking pyobjc-core==2.3 (from PyObjC)
 
Running setup.py egg_info for package pyobjc-core
 
/usr/local/Cellar/python/2.7.2/lib/python2.7/distutils/dist.py:267: UserWarning: Unknown distribution option: 'use_2to3'
 
warnings.warn(msg)
 
Traceback (most recent call last):
 
File "", line 14, in
 
File "/usr/local/Cellar/python/2.7.2/lib/python2.7/site-packages/trashcli/build/pyobjc-core/setup.py", line 452, in
 
**extra_args
 
File "/usr/local/Cellar/python/2.7.2/lib/python2.7/distutils/core.py", line 152, in setup
 
dist.run_commands()
 
File "/usr/local/Cellar/python/2.7.2/lib/python2.7/distutils/dist.py", line 953, in run_commands
 
self.run_command(cmd)
 
File "/usr/local/Cellar/python/2.7.2/lib/python2.7/distutils/dist.py", line 972, in run_command
 
cmd_obj.run()
 
File "/usr/local/Cellar/python/2.7.2/lib/python2.7/site-packages/trashcli/build/pyobjc-core/setup.py", line 110, in run
 
orig_egg_info.egg_info.run(self)
 
File "", line 7, in replacement_run
 
AttributeError: class egg_info has no attribute 'iter_entry_points'
 
Complete output from command python setup.py egg_info:
 
/usr/local/Cellar/python/2.7.2/lib/python2.7/distutils/dist.py:267: UserWarning: Unknown distribution option: 'use_2to3'
 
warnings.warn(msg)
 
running egg_info
 
Traceback (most recent call last):
 
File "", line 14, in
 
File "/usr/local/Cellar/python/2.7.2/lib/python2.7/site-packages/trashcli/build/pyobjc-core/setup.py", line 452, in
 
**extra_args
 
File "/usr/local/Cellar/python/2.7.2/lib/python2.7/distutils/core.py", line 152, in setup
 
dist.run_commands()
 
File "/usr/local/Cellar/python/2.7.2/lib/python2.7/distutils/dist.py", line 953, in run_commands
 
self.run_command(cmd)
 
File "/usr/local/Cellar/python/2.7.2/lib/python2.7/distutils/dist.py", line 972, in run_command
 
cmd_obj.run()
 
File "/usr/local/Cellar/python/2.7.2/lib/python2.7/site-packages/trashcli/build/pyobjc-core/setup.py", line 110, in run
 
orig_egg_info.egg_info.run(self)
 
File "", line 7, in replacement_run
 
AttributeError: class egg_info has no attribute 'iter_entry_points'

参考 [1] , [2] 才能完整解决

1、pip 安装失败后,会在当前目录有 build 目录,修改 setup.py 文件,build/pyobjc-core/setup.py

use_2to3 = True   =>    use_2to3 = False

2、chmod 执行权限

chmod a+x build/pyobjc-core/libxml2-src/configure
chmod a+x build/pyobjc-core/libxml2-src/install-sh

3、export MACOSX_DEPLOYMENT_TARGET=10.5 (我也不知道为什么要声明这个系统变量)

4、python build/pyobjc-core/setup.py install ,就可以把 pyobjc-core 安装上了,然后再 pip install pyobjc ,会安装其它的一些包

 

但我不喜欢用 GUI ,我相信用几行 python 就能解决,于是我用 PIL ,并参考 [3] ,但文章中的函数并不能正常运行,原文如下:

from PIL import Image
 
im = Image.open('mouse.png')
# Get the alpha band
alpha = im.split()[3]
# Convert the image into P mode but only use 255 colors in the palette out of 256
im = im.convert('RGB').convert('P', palette=Image.ADAPTIVE, colors=255)
# Set all pixel values below 128 to 255,
# and the rest to 0
mask = Image.eval(alpha, lambda a: 255 if a &lt;=128 else 0)
# Paste the color of index 255 and use alpha as a mask
im.paste(255, mask)
# The transparency index is 255
im.save('mouse.gif', transparency=255)

会报错:

python pil ‘NoneType’ object has no attribute ‘bands’

2)以上就是第二个 bug … 两个解决方法

1、参考 [4] ,在调用 im.split() 前,先 im.getdata() 一下,如下

from PIL import Image
 
im = Image.open('mouse.png')
# Get the alpha band
 
im.getdata()
 
alpha = im.split()[3]
# Convert the image into P mode but only use 255 colors in the palette out of 256
im = im.convert('RGB').convert('P', palette=Image.ADAPTIVE, colors=255)
# Set all pixel values below 128 to 255,
# and the rest to 0
mask = Image.eval(alpha, lambda a: 255 if a &lt;=128 else 0)
# Paste the color of index 255 and use alpha as a mask
im.paste(255, mask)
# The transparency index is 255
im.save('mouse.gif', transparency=255)

2、fix 了这个 bug 的 patch ,在这里 [5] http://hg.effbot.org/pil-2009-raclette/changeset/fb7ce579f5f9

 

为了处理 1k 张图片。。。真折腾。。。还以为我需要把 ImageAlpha 的函数抽出来了,它是用 python 调用 objective-c 的接口来做的。

 

Reference:

[1] https://github.com/pypa/pip/issues/11#issuecomment-6485553

[2] http://www.slevenbits.com/2012/05/installing-pyobjc-in-virtualenv-on-mac-os-x-10-7

[3] http://nadiana.com/pil-tips-converting-png-gif

[4] http://bugs.python.org/issue9383

[5] http://hg.effbot.org/pil-2009-raclette/changeset/fb7ce579f5f9