如何做一个更好的Python开发者(1)?(Python Performance Tips, Part 1译)
从最开始的写python代码到现在,其实还没有对如何更好的开发python代码有一个全新的认识,这片文章是我翻译一个大牛中的大牛给Python写的tips。正文如下:
如果你想要阅读Zen of Python,只需要在你的Python解释器里键入import this。对于刚刚接触python的人,如果心细,就会注意到“解释器”这个词,随即也会意识到Python是另一种脚本语言。“它肯定很慢!”这种观点毫无疑问是正确的:Python程序不像编译语言那样高效。甚至Python的倡导者也会告诉你Pyhton语言在效率方面处于劣势。然而,YouTube已经证明,Python在每个小时内能提供4000万视频的服务。你必须做的所有事情就是写出高效的代码,在速度要求方面寻找C/C++的外部实现。这里有一些tips可以帮助你成为一个更好的Python开发者:
1. 选择内建函数:
你可以使用Python写高效的代码,但是你却很难避免用到内建函数(用C语言完成的)。点击这里察看。这写内建函数非常快。
2. 使用join()函数将大量的字符串连起来:
你能使用符号“+”将几个字符串结合起来。因为字符串是不可改变的,每个涉及到“+”的操作都会创建一个新的字符串,同时拷贝旧字符串的内容。一种应用频度非常高的惯用语法是利用Python的数组模式修改单个字符;接着就是使用join()函数再创建你最终的字符串。
#This is good to glue a large number of strings for chunk in input(): my_string.join(chunk)
3. 在交换变量值中使用多重赋值:
这种方式在Python中是非常快捷的:
x, y = y, x
而下面这种方式要慢很多:
temp = x x = y y = temp
4. 尽可能使用临时变量:
Python检索临时变量的速度要比检索全局变量快。所以,尽量避免使用“global”这个关键词。
5. 尽可能的使用“in”:
一般在检查成员关系时,会用到关键词“in”。这种方式很简洁,也很快捷。
for key in sequence: print "found"
6. 通过懒惰方式的importing提高速度:
将“import”语句移到函数中去,这样你就只会在用到的时候import某些内容。也就是说,如果有些模块你不是马上用到,你就可以晚些import它们。例如,在启动的时候,你可以先不import一长列的模块来提高你代码的速度。这个技术没有加强全局的效率。但是它帮助你将导入模块的时间更加均匀的分布在代码中。
7. 在无限循环中使用“while 1”:
有时候你会在你的代码中使用到无限循环。(例如,一个监听socket)虽然“while True”实现了同样的功能,但是,“while 1”是一个单独的跳操作。你可以将这个trick用在你高效的Python代码中。
while 1: #do stuff, faster with while 1 while True: #do stuff, slower with while True
8. 使用list内涵:
在Python2.0之后,你就可以使用list内涵代替很多“for”和“while”块。list内涵更快的原因在于,在循环的过程中,Python解释器能最优的发现一个可预测的模式。一方面,list内涵更加易读(函数编程中),另一方面,它为你节约了一个额外的计数变量。例如,我们可以在一行内得到1到10之间的偶数数值:
#the good way to iterate a range evens = [i fo i in range(10) if i % 2 == 0] [0, 2, 4, 6, 8] #the following is not so Pythonic i = 0 evens = [] while i < 10: if i % 2 == 0: evens.append(i) i += 1 [0, 2, 4, 6, 8]
9. 在每个长序列中使用xrange():
这样做可以为你节约大量的系统内存空间,因为xrange()每次只在一个序列中产生一个整数元素。和range()相反,它给出你整个列表,而这在整体的循环中是不必要的。
10. 根据需求,使用Python生成器得到相应的数值:
这种做法同样可以节约内存空间,提高代码效率。如果你在传输视频的数据流,你能send一个chunk的字节,而不是整个数据流。例如,
chunk = (1000 * i for i in xrange(1000)) chunk <generator object <genexpr> at 0x7f65d90dcaa0> chunk.next() 0 chunk.next() 1000 chunk.next() 2000
11. 学习itertools模块:
这个模块在迭代和聚合方面非常高效。通过三行代码我们就可以得到list[1, 2, 3]的所有排列:
import itertools iter = itertools.permutations([1, 2, 3]) list(iter) [(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2 ,1)]
12. 学习bisect模块将list保持有序:
这是一个开源的二叉查找实现,同时也是一个很快的有序序列插入工具。也就是说,你能这样使用:
improt bisect bisect.insort(list, element)
通过上面的操作,你就在list中插入了一个元素,接下来你也不需要再次调用sort()函数来保持有序,我们也必须注意到后者在长序列中的代价是非常高的。
13. 需要理解,一个Pyhon列表其实就是一个数组:
Python中的列表实现和人们在Computer science中谈到的一般独立链接列表是不一样的。Python的列表其实是一个数组。这样一来,你就可以以O(1)的时间,利用下标索引,很快搜索到列表中的一个元素,而不需要从列表的开始处搜索。这点意味着什么呢?一个Python的开发者在使用insert()函数插入一个list元素时需要三思。例如:list.insert(0, element)
在list的首部插入一个元素并不是一个高效的选择,因为这个list中所有的其他下标都会改变。然而你可以将一个元素高效的添加到list尾部,利用函数list.append()。如果你确实想要更快的插入或者删除两端的元素,最好选择双端队列deque。因为deque在Python中是作为双向链表来实现的,所以它会更快一些。这里不再做过多的赘述。
14. 使用dict和set检测成员关系:
Python的dict和set在检查一个元素是否存在时非常快捷。因为这两者是利用hash表来实现的。它们的查找时间可以达到O(1)。因此,如果你要经常检查成员关系,最好使用dict或者set。
mylist = ['a', 'b', 'c'] #Slower, check membership with list: 'c' in mylist True myset = set(['a', 'b', 'c']) #Faster, check membership with set: 'c' in myset: True
15. 在sort()函数中兼并使用Schwartzian Transform:
list.sort()函数在处理数据方面是非常快的。它会将一个list处理得到一个自然顺序的列表。然而,你有时候想要得到非自然序列的list。例如,你想要将IP地址根据你服务器的位置来排序。Python支持传统的比较,也就是说你可以使用list.sort(cmp())函数来实现,但是这样做会比list.sort()慢很多,因为你引入该函数调用了上层的内容。如果速度需要考虑进来,你可以使用在Schwartzian Transform算法的基础之上产生的Guttman-Rosler Transform算法。可以读到其具体如何实现的算法当然是非常有趣的,这里我们对其如何工作的只做一个简单的总结,你可以通过调用Python的内建函数list.sort()快速转换list,而不需要使用相对时间较慢的list.sort(cmp())函数。
16. 使用Python的decorator缓存结果:
符号“@”是Python的点缀(decorator)语法。不仅仅可以使用它来跟踪,上锁or记录日志。你甚至可以点缀Python的函数,使其记住接下来会使用到的结果。这个技术被称之为memoization。接下来有一个例子:
from functools import wraps def memo(f): cache = {} @wraps(f) def wrap(*arg): if arg not in cache: cache['arg'] = f(*arg) return cache['arg'] return wrap
我们也可以将点缀用在Fibonacci函数中
@memo def fib(i): if i < 2: return 1 return fib(i - 1) + fib(i - 2)
这里的主要目的很简单:增强(or decorate)你的函数,使其记住已经计算得到的每个Fibonacci元素。如果这些值都在缓存中,也就没有必要再次计算。
17. 理解Python的GIL(全局解释器锁global interpreter lock):
GIL是非常必要的,因为CPython的内存处理并不具有线程安全性。你不能简单的创建多个线程,然后希望Python会在多核机器上跑的非常快。这是因为GIL将会防止多个本地线程一次执行Python的多个字节码。也就是说,GIL会将你的所有线程系列化。然而,你可以通过使用多线程协调几个已创建的且独立运行于Python代码之外的进程,加速代码运行效率。
18. 将Python源代码作为你的文档:
Python有很多考虑到速度而使用C语言实现的模块。当效率在编码中非常重要时,官方的文档就会显得匮乏,此时你可以自由的去体验源代码。而在这个过程中你会发现很多潜在的数据结构和算法。Python仓库是一个非常美妙的地方,也很值得在其中徜徉:http://svn.python.org/view/python/trunk/Modules
结语:
世界上没有大脑的替代物。程序开发者的责任是深刻的看清楚问题,达到不会轻易的将一个坏的构思集合在一起的境界。这篇文章里关于Python的tips能帮助你获得更好的效率。如果速度仍然不是足够的优,此时Python需要寻求额外的帮助:编写以及运行外部扩展代码。我们将会在这片文章的第二部份涉及到这一块。
To be continued...
(原文链接:http://blog.monitis.com/index.php/2012/02/13/python-performance-tips-part-1/)
从python文件提取以及绘图谈其设计思想
我最近对python特别着迷,学会爬虫抓取数据之后,接着尝试将抓取到的文件数据提取,然后绘制出走势图,后面的这个任务的完成,我用了整整一个下午的时间(不睡午觉),但是最后在代码以及变量函数命名方面做的诸多不足,下面给出我前后代码,谈谈python的设计思想
主模块:
#!/usr/bin/python # coding = utf-8 import sys import string import matplotlib.pyplot as plt import numpy as np import getNum #获得歌曲排名,因为设计中文编码问题,单独作为一个自定义模块 def getKey(fileName, s, dist): f = open(fileName, "r") info = f.read() f.close() start = info.find(s) + dist info = info[start:] end = info.find(",") return info[:end] def songDraw(): x = np.arange(1, 8, 1) c = ('y', 'b', 'g', 'r', 'c', 'y', 'b', 'g', 'r', 'c') fileDay = ("day1.txt", "day2.txt", "day3.txt", "day4.txt", "day5.txt", "day6.txt", "day7.txt") for i in range(0, 10, 1): num = [] tmp = str(i * 20 + 1) strlen = len(tmp) if strlen == 1:#歌曲的位数要考虑,这样每次截取得到的才是正确的 dist = 2 elif strlen == 2: dist = 3 else: dist = 4 key = getKey("day1.txt", tmp, dist) num.append(i * 20 + 1) for j in range(1, 7, 1): num.append(int(getNum.getY(fileDay[j], key))) print num if i < 5: plt.plot(x, num, color=c[i], label="Monday's rank NO."+tmp) else: plt.plot(x, num, linestyle='--', color=c[i], label="Monday's rank NO."+tmp) # plt.plot(x, num, color=c[i], label=u(key)) plt.legend(loc='upper right') plt.title("The song's seven-day rank") plt.xlabel("days") plt.ylabel("rank") plt.grid(True) plt.show() if __name__ == '__main__': songDraw()
主模块使用到的自定义模块:
#!/usr/bin/python # -*- coding:gbk -*- import string def getY(fileName, key): f = open(fileName, "r") info = f.read() f.close() #print s end = info.find(key) - 1 if end == -2: return 0 else: info = info[:end] info = info[::-1]#将字符串反转,从最后面取值 end = info.find(",") info = info[:end] #print tmp return info[::-1]
接下来的是修改之后的:
#!/usr/bin/python # coding = utf-8 import sys import string import matplotlib.pyplot as plt import numpy as np def getSongName(file_name, rank): info = open(file_name, "r").read() start = info.find(rank) return info[start:].split(',')[1] def getSongRank(file_name, songName): info = open(file_name, 'r').read() end = info.find(songName) if end == -1: return 201 else: return int(info[:end].split(',')[-2])#使用split之后,函数反转不需要了,中文字符的查找问题也不存在了 def songDraw(): x = np.arange(1, 8) c = ('y', 'b', 'g', 'r', 'c', 'y', 'b', 'g', 'r', 'c') fileDay = ("day1.txt", "day2.txt", "day3.txt", "day4.txt", "day5.txt", "day6.txt", "day7.txt") for i in range(10): songName = getSongName("day1.txt", str(i * 20 + 1)) num = [getSongRank(fileDay[j], songName) for j in range(7)]#简化的一个亮点 print num if i < 5: plt.plot(x, num, color=c[i]) else: plt.plot(x, num, linestyle='--', color=c[i]) plt.title("The song's seven-day rank") plt.xlabel("days") plt.ylabel("rank") plt.grid(True) plt.show() if __name__ == '__main__': songDraw()
可以看到,两者之间有非常明显的区别。前后代码量以及松散程度得到了很好的对比。作为一个菜鸟,感觉对python最重要的三大数据结构list,string以及字典使用的还是不到位,不过通过这个训练过程,我至少学会了如何去调试python。同时,在命名规范上,我要做到更加简洁明了。
其实之前写的C代码多了,所以总感觉在for循环里计算的东西能放在外面就放在外面,经高人提点,python最初的设计理念就不是这样的。python作为一种解释执行的编程语言,其最大的优点之一是它使你能够专注于解决问题而不是去搞明白语言本身。作为每一个python代码的书写者,都要牢记简洁是python的一大美,简洁才能做到易读易看,简洁才能让自己的代码更加优美,不会太慵懒!在网上搜了下——python之禅:
- 优美胜于丑陋(Python 以编写优美的代码为目标)
- 明了胜于晦涩(优美的代码应当是明了的,命名规范,风格相似)
- 简洁胜于复杂(优美的代码应当是简洁的,不要有复杂的内部实现)
- 复杂胜于凌乱(如果复杂不可避免,那代码间也不能有难懂的关系,要保持接口简洁)
- 扁平胜于嵌套(优美的代码应当是扁平的,不能有太多的嵌套)
- 间隔胜于紧凑(优美的代码有适当的间隔,不要奢望一行代码解决问题)
- 可读性很重要(优美的代码是可读的)
通过这次的经历,感触还是蛮多的,在代码的技巧以及书写方面也深深觉察到自己的不足,前面的一段路还是很长啊!不过我会加油的,每天进步一点就够了!
初试絮叨之beamer
在正式开始之前,我想吐吐我的肺腑之言。
最开始接触latex是在去年7月份,当时,觉得这个东西非常神奇,但是很长一段时间,我都只是止步于看资料,做笔记的阶段,以至于后来将这写都抛诸脑后了。前天又开始拿起来是因为想写一个ppt,但是迫于ubuntu12.04的压力,我只得弃office而择“良木”。吸取上次的教训,我这次不仅仅是看资料,而是边看边自己动手尝试,仅仅用半天时间,我就基本可以写出一个不错的演示本,基本语法啥的都掌握了!
事后,这让我感触很深。“其实任何东西都是我自己想难了,其实当你真正去-动手-学习时,一切都会是那么easy”,这是我在扣扣上写的签名,这里作这么多的赘述,只是想在接下来的学习中,当我遇到困难,当我停滞向前时,我会回过头来翻翻这篇文章,i believe i can!
latex的基本语法就那些,而且在某种形式上说,各个模版如artical,beamer,report等,很多语法都可以通用,所以当你会了一种之后,其他就显得手到擒来。
作为一个greenhand,这里我有几个问题还是没有搞的太清楚,虽然写出了一个演示稿。
- \useoutertheme{infolines}这个主题的使用,通过测试,个人感觉好像在使用中具体和所使用的theme有关,例如rochester就配合的非常好,但是在warsaw下,就显得有点小小的问题!
- only和uncover这两个功能的区别具体还不是很清楚;因为我在一个地方对同一个对象使用的时候,得到的结果都是一样的;
- 最后一点也是我最纠结的,我使用的是kile+okular,当我设置了改变颜色,编译运行之后,viewpdf的时候,居然颜色还是之前的,没有什么改变,不知道是okular的原因,还是我的原因!(切换到该文件夹下,用其他pdf工具察看,颜色确实改变了)
-
承接第3点,我在能够运行得出pdf的情况下,加了下面的一个frame。我点击编译运行成功之后,再viewpdf的时候,没有生成相应的pdf文件,在output里,我可以看到“!pdfTeX error: /usr/bin/pdflatex (file ./faq.jpg): reading JPEG image failed (no JPEG header found)”,不知道是什么原因。(我后来在命令行里执行,latex *.tex,dvipdf *.dvi,这种情况下,可以得到输出的pdf,用pdf阅读器看,是正常的!!)
\frame{ \frametitle{Questions and answers} \begin{figure} \includegraphics[width=6cm]{faq.jpg} \end{figure} }
这是我这次总结得出的几个问题,希望我能在接下来的学习中一一解决,如果有牛人可以指点一下,也是非常好的!
PS:我学习beamer中使用到的资料
http://wenku.baidu.com/view/848fe465783e0912a2162aa2.html
http://www.math.umbc.edu/~rouben/beamer/quickstart.html
(还下载了一些手册,没来得及看!对与latex,这个网站也不错:http://math.ecnu.edu.cn/~latex/)
ubuntu12.04 & putty & xshell
不知道有没有谁在ubuntu下使用过putty?
因为实验的要求,我必须要远程登陆至少5台linux服务器主机,测试,运行程序,而本身系统是ubuntu,在bash下用ssh操作,总是让人觉得很复杂,所以我在网上搜到了支持linux环境的便捷工具putty,这个工具在很多方面做的很好,有ssh,telnet等等很多可选项,开始的时候,我觉得还可以,但是用着用着就出现了很多问题。
这里我不得不吐槽一下,首先字体方面的设置,就弄的很不合理,非得在最开始绑定设置,如果你保存了某个配置好的远程登陆,后期想设置字体啥的也是不可以的;其次,每个登陆窗口都是一个独立的页面,所以在切换的时候很纠结;最后一点是让我最不爽的一点,putty可能本身存在的问题,所以在同时登陆多台主机时,总是有一两个会报错,如下图示。(由于我是在ubuntu下使用,所以在windows下具体是什么样子,我还没研究)
当我机器出现这个问题的时候,我以为是服务器主机的问题,但是我通过使用我同学xshell登陆该台主机,是正常的,然后我就非常囧了。在这样平行的实验下,最后可以归纳出putty在linux下特别是登陆多台主机的问题上,做的确实不够好。
迫于无奈,我选择用wine+xshell4的组合方式,希望接下来不会出现类似于上面putty的问题。
PS:现在对wine的了解很少啊,只知道是可以在其下可以运行windows程序,也就是模拟了一个windows的环境!不知道有没有牛人能具体介绍下?
ping && url/uri/urn
这个标题很多人初次一瞥,可能会觉得没有什么可以说的。的确,这个问题是一个简单的问题,但是正是因为太简单了,所以,在编写程序时,往往不会注意到,我也其中一个,所以在此做点总结,用来警醒自己,也希望这篇文章能对初学者有些许帮助。
通过维基百科,可以看到这样的定义“ping是:一个电脑网络工具,用来测试待定主机是否通过IP到达”,具体的工作原理是利用网络上机器IP地址的唯一性,给目标地址发送一个数据包,再要求对方发送同样大小的数据包来确定两台机器是否连通,时延是多少。
其基本格式是ping [某个主机IP],根据结果的不同情况来判断网络情况以及故障原因。这里外加提一点,ping to death,其实是一些木马等病毒程序会不断给某服务器发送ping,抢占服务器资源,导致不能正常应答其他请求,最后整个系统崩溃不能工作。
所以,在使用ping时,要注意,你ping的对象是否是一个IP地址,还是简单的url网络资源,例如:
ping http://music.baidu.com ping http://music.baidu.com/top/dayhot
第一个ping使用是可以ping通的,而后者是ping不通的,原因很简单,因为前者的地址对应的是一个服务器的IP地址,而后者对应的是一个url网络资源,不符合ping的基本“范式”。(我这几天因为这个东西犯错了,总结还是对url和ping的概念以及知识的掌握不够深入)
除了上面的简单总结,我还想回顾一下uri,url和urn,加深印象。
- URI:uniform resource identifier,统一资源标识符;
- URL:uniform resource locator,统一资源定位符;
- URN:uniform resource name,统一资源名称;
其中,uri和urn是uri的子集,网络上每个资源,如html文档,图像,视频,表格,文字等,都是通过uri来进行定位的。这三者基本都是由三部分构成:协议/主机IP(端口)/主机资源目录(一般以目录形式存在)。可以这样来比喻,一个网站就是一个大屋子,uri就是这个屋子的大门,而url就是对屋子里的每个物品进行了标识,urn就是这些物品能被计算机理解的名字。
PS:这里我可能说的不是很充分,但是,我觉得只要记住这些就已经差不多了,主体就已经出来了。
以太网&&10M家庭宽带
“以太网是什么?”
这是很久之前,有人问我的一个问题,当时,我就呆了,我好像从来没有考虑过这个问题,我只知道以太网是一个局域网的协议,至于现在每个人都知道的以太网和这里的协议是不是一回事???
到现在为止,我也不是太清楚。又认真翻看了一下课本,我推测可能是这样的。由于以太网是目前为止最流行的有线局域网技术,而且几乎完全占领着现有的市场,以太网硬件(适配器以及交换机)成了一个很普通的商品,所以现在人们口中的以太网应该是使用了以太网技术的网络。
不知道这里的分析是否正确??
很多时候家人总是抱怨在某个时段,家里的10M宽带很慢,而我的解释总是说“这10M是共享的,而不是专有的,运营商是根据一个概率来计算,保证没人使用的时候是10M!”因为之前的网络课,提到过,但是现在记忆中只有简单的答案,而我今天又翻了翻网络书,得到了算是一个比较满意的答案。
其实网络是可以分为电路交换网络和分组交换网络,前者因为对网络资源的浪费现在已经很少用到了,只是一些大型的,需要网络带宽保障的公司现在还在使用,但是这类网络一般都很贵。我们现在家庭装的网络基本都是后者。
电路交换链路中通过频分复用和时分复用(前者一个将频率分成一片一片,每个人享有固定的频率区间;后者是将时间分成一片一片,每个人也有固定的时间使用整个网络),但是,人们不可能每时每刻都在网络上传输数据,这样以来,就会造成很多浪费。所以后来出现了分组交换网络(核心:存储转发,分组交换),其端到端的时延是不可预计的,但是它较分组交换网络有两点优点:提供了更好的带宽共享;更简单,有效,且实现成本更低。
假设有1Mbps的链路,电路交换有10个用户,可以每个用户有完整的100kbps恒定速率。而对分组交换,一个特定用户活动的概率是0.1,如果有35个用户,有11或更多同时上网活动的用户概率大约是0.0004,也就是a(a<=10)个用户在网上活动的概率为0.9996,这样一来,每个人享有的带宽是1Mbps/a >= 100kbps。
这个例子说明了在同一带宽的情况下,分组交换网络能够维持更多用户,所以,在某个特定的时刻,上网人数猛增的话,会导致人均占有的网络带宽明显下降。(而且有排队时延)
PS:在一个小区内,运营商基本只会设置一条固定带宽的链路。
英文歌收录
收录一下自己喜欢的英文歌,这是我一直都想做的事情,今天才抽出点时间来整理整理。在此之前,我想说明的是,每个人喜欢的风格不同,所以,我这里也只是列出了我比较喜欢的一些曲子:
- Yesterday Once More--------Carpenters
- My Heart will Go on--------Celine Dion
- Call me Maybe--------Carly Rae Jepsen
- --------Britney Spears
- Everytime--------Britney Spears
- Baby One More Time--------Britney Spears
- --------Sarah Connor
- Innocence--------Avril Lavigne
- How you Remind me--------Avril Lavigne
- Hero
- Heal the World--------Michael Jackson
- We Are the World--------Michael Jackson
- Who is It--------Michael Jackson
- Seasons in the Sun
- Soledad
- Right here Waiting for You--------Richard Marx
- Say you Say me--------Lionel Richie
- Can you Feel the Love Tonight--------Elton John
结语:可能不够完善,但是有了这样一个支点,以后就可以继续增加丰富!也欢迎大家list your favors!
urllib2.URLError: <urlopen error [Errno 111] Connection refused>
在说道这个问题之前,我想先说下python中的str.find()函数,因为我几次用到这个函数时,都有点出了乱子。
str.find()查找的是一个长字符串中的子串,看如下一个例子的输出:
1 #!/usr/bin/python 2 #coding = utf-8 3 4 import string 5 import sys 6 7 def main(): 8 s = "Today is Friday, I am so happy!" 9 print s.find("I") 10 print s.find("I am") 11 12 if __name__ == '__main__': 13 main()
结果都是17,所以要知道,这里find匹配的是最开始的一个独立成的字符!!!这样以来,我们在分析网页的时候就要注意这个问题了!
下面开始说下我遇到的问题,urllib2.URLError: <urlopen error [Errno 111] Connection refused>。我测试了好久,甚至运行了之前可以运行成功的python代码,但是最后都以失败告终。
这里要注意,我们通过这个库可以得到网络上的资源(一个url对应的就是一个资源,要区分ping,ip以及url的区别),只是connection有问题,查阅了很多资料,最后发现是我的电脑上安装了一个代理工具goagent。在最后具体解决的时候是每次将代理打开之后再运行python代码,抓取成功了。
PS:虽然最后成功了,但是我还是不太明白,我的默认浏览器没有设置代理,为什么urllib2在获取网络资源的时候仍然要通过代理呢??我也看了urllib2的相关资料,猜测可能是该库在具体的实现上有代理起优先的算法把?--------有待解决!!
linux chrome+goagent 翻墙设置
不说废话,先说自己的基本情况:ubuntu12.04+chrome,想在此基础上整合一个翻墙的软件,软件的首选是goagent。
配置之前需要准备的:
1. 申请一个Google Appengine:
http://code.google.com/intl/zh-CN/appengine/,
创建一个appid号,备用。这个网站下有具体如何申请,以及各个选项应该填啥的详细注解:
https://code.google.com/p/goagent/wiki/InstallGuide;
2. 注册一个gmail邮箱,将密码重新设置成“专用密码”,而不是你开始输入的扣扣密码啥的,这可以在gmail的设置里面修改设置;
3. 下载Python版的Google App Engine SDK:
https://developers.google.com/appengine/downloads?hl=zh-CN#Google_App_Engine_SDK_for_Python,
要选择linux平台,下载后解压;
4. 下载下载goagent稳定版:
https://code.google.com/p/goagent/,
也就是正是版本,下载后解压到google_appengine下,改名为goagent;
5. 修改goagent/local/proxy.ini文件中的[gae]下的appid=你的appid,多个appid可以用|隔开;修改goagent/server/python/app.yaml
6. 在google_appengine/goagent/l目录下执行:python appcfg.py update goagent/server/python,上传,需要一些时间,注意这里也会需要gmail的邮箱,这里的第2步就比较有用了;
7. 在浏览器上进行配置,并在每次使用时在 goagent/local 下执行 sudo python proxy.py ,然后打开浏览器即可;
上面的浏览器配置,我没有进行过多的说明,是因为有很多方式,其中最简单的就是在设置里面将代理设为127.0.0.1:8087,每次需要翻墙的时候,打开浏览器执行第7步即可(这里的第7步还可以在~/.bashrc中设置,将上述sudo ** 命令简化)。
还有一种方式是,在chrome中加入扩展插件:Proxy SwitchySharp,具体的设置可以参看网页:
http://www.ilovexinji.com/xinji-collision/72-chromeswitchysharp(在最后更新的时候可能会因为网速的原因而导致加载失败,要多尝试);
火狐浏览器也可以类似来设置。
由于在windows下,这个翻墙软件具体执行以及嵌入更加简单,也已经有了很多说明,所以,这里就不做更多的赘述。每个id号每天下载的流量有很多,可以达到几个G,所以,一个id号可以分享使用。
网页抓取中编码问题:UnicodeEncodeError: ‘gbk’ codec can’t encode character ****: illegal multibyte sequence
“UnicodeEncodeError: ‘gbk’ codec can’t encode character ****: illegal multibyte sequence”
上面这个问题是我在练习写python脚本抓取网页信息的时候,遇到的问题!我很郁闷,因为通过察看page info发现它现实的网页编码方式确实是gbk,我的处理是这样的:
def getResult(url): txtfile = open("cnbeta.txt", "w") html = urllib2.urlopen(url).read() html = html.decode('gbk').encode('utf-8') #print html analysisPage(html, txtfile) txtfile.close()
后来甚至将gbk改成gbk1213,gb等等编码方式,都显示错误,查了很多资料都不对。我甚至想到用另一种方式来抓取,但是不经意的搜网页,我发现这样可以解决:
def getResult(url): txtfile = open("cnbeta.txt", "w") html = urllib2.urlopen(url).read() html = html.decode('gbk', 'ignore').encode('utf-8') #print html analysisPage(html, txtfile) txtfile.close()
这段代码只需要修改一点点就ok了。分析原因:
在代码的整体实现上是没有问题的,主要是遇到了非法字符--------特别是在全角空格的问题上,因为其实现方式有很多种,比如\xa3 \xa0,或者\xa4\xa5,这写字符,看起来都像是全角空格,但是他们不是合法的(真正的全角空格是\xa1\xa1),因此在码的转换中出现了问题。而且这样的问题一出现,就会导致整个文件都不能转换。
这时,就不要忘了,decode的函数原型:decode([encoding], [errors='strict'])。默认参数是strict,代表遇到非法字符抛出异常;如果是ignore,则会忽略,直接输出;如果是replace,则会用?取代非法字符;如果设置成xmlcharrefreplace,则使用XML字符引用。
(全文参考:http://www.cnblogs.com/baiyuyang/archive/2011/10/29/2228667.html)