月度归档:2016年12月

你应该学会的python列表去重

列表去重是写Python脚本时常遇问题,因为不管源数据来自哪里,当我们转换成列表的方式时,有可能预期的结果不是我们最终的结果,最常见的就是列表中元素有重复,这时候第一件事我们就要做去重处理,我们先来个最简单的方式,用python内置的数据类型set来实现。

假设我们的列表数据是这样的:

因为集合的元素是不能重复的,所以将列表转换成集合时,会自动去掉重复的元素,这就是基本原理,代码如下:

这种方式缺点是再转换成列表时无法保存之前的列表顺序,如果没这个要求,这种方式是最简答的, 也许有的小伙伴觉得好简单呀,这没什么技术含量吗,没错,所以一般面试题里让你列表去重一般会这么写:

请写出列表去重的方法(不能用set)

人家写明不能用set了,所以呢,这招有时候还不能用,那当然也难不倒我们,我们还有其他方法。

我们都知道列表可以遍历,能遍历问题也就简单了,我们再定义空列表,然后遍历有数据的列表,再遍历时加一个判断,如果在空列表里没有,就加进去,如果有了就丢掉,代码如下:

 

大家觉得这种方式是不是还可以,但这种方式对付一般的小列表是没问题的,但如果遇到一个超级大列表,也会力不从心,因为在the_list列表变的非常大,在判断时候会影响效率,因为列表是按索引顺序去查找的,当数据量很大时会变慢。

也许你要问了,那我遇到大的列表咋办? 有更牛掰点的方法吗?当然有,让我们继续,既然在判断时用列表会影响效率,那我们就转换一个思路,我们用集合,那你可能要问了,那集合就快了?没错,因为set使用的hash函数查找值,虽然set无序,但位置是固定的,只需一次就可以查到特定元素是否存在,网上有人做了列表和set的元素查找对比,相同的数据条件下,用list耗时16分钟,用set耗时是52秒,这一对比看出效果了吧,别的不多说了,贴代码:

列表去重的方法就介绍到这里,欢迎转发~

python按任意键继续程序

在windows下写bat的时候,通过pause命令,可以暂停程序运行,例如经常见的程序会在终端提示”按任意键继续……”,用户在终端回车后程序可以接着运行,这个功能有多大用途今天暂且不说,但我觉得应该有很多人也想在python下实现这个功能,这样当自己写的python程序运行时, 突然给出这么一个提示,然后自己再来个帅气的回车,我觉得肯定给人一种非常专业的感觉,最起码自己肯定被自己迷倒了,所以今天我们就学习下这段代码,这里定义了一个函数,所以你可以嵌入到你的程序中,在任何你希望调用的地方调用它即可,使用非常的方便,代码如下:

#!/usr/bin/env python

# -*- coding:utf-8 -*-

import os

import sys

import termios

def press_any_key_exit(msg):

# 获取标准输入的描述符

fd = sys.stdin.fileno()

# 获取标准输入(终端)的设置

old_ttyinfo = termios.tcgetattr(fd)

# 配置终端

new_ttyinfo = old_ttyinfo[:]

# 使用非规范模式(索引3是c_lflag 也就是本地模式)

new_ttyinfo[3] &= ~termios.ICANON

# 关闭回显(输入不会被显示)

new_ttyinfo[3] &= ~termios.ECHO

# 输出信息

sys.stdout.write(msg)

sys.stdout.flush()

# 使设置生效

termios.tcsetattr(fd, termios.TCSANOW, new_ttyinfo)

# 从终端读取

os.read(fd, 7)

# 还原终端设置

termios.tcsetattr(fd, termios.TCSANOW, old_ttyinfo)

if __name__ == “__main__”:

press_any_key_exit(“按任意键继续…”)

print ‘\n’

代码不多解释,看注释,这里要说一下termios模块,这个模块提供了一个接口可以控制tty终端的Io,它所有函数第一个参数需要一个文件描述符,可以是整型的文件描述符,也可以是个文件对象,因为它可以控制在终端的显示设置,常用场景就是用户在终端输入密码时不要显示出来,就跟我们用root登陆系统时系统给出输入密码提示符一样, 代码实现如下:

def getpass(prompt=”Password: “):

import termios, sys

fd = sys.stdin.fileno()

old = termios.tcgetattr(fd)

new = termios.tcgetattr(fd)

new[3] = new[3] & ~termios.ECHO

try:

termios.tcsetattr(fd, termios.TCSADRAIN, new)

passwd = raw_input(prompt)

finally:

termios.tcsetattr(fd, termios.TCSADRAIN, old)

return passwd

passwd = getpass()

print passwd

这个脚本运行会提示你输入密码,输入后会打印出刚输入的密码,给这个2个例子也是说明termios的简单用法,大家可以自己运行程序去体会下。

最简单的多线程爬虫实例

写这个例子主要还是了解多线程的使用和运行方式,因为爬虫是用多线程的实现的典型应用场景,基本写爬虫的没有不用多线程的,因为太多的网页或内容你不可能一一去获取,如果爬的数据量太大而不去并发执行,那时间估计是人无法忍受的,如果对python了解多一些的小伙伴可能知道GIL, 全称Global Interpreter Lock,  也就是python的全局锁,这把锁是为了 解决多线程之间数据完整性和状态同步的,也正因为这把锁的存在,python无法实现真正意义上的多线程,因为在某个时刻只能有一个线程在占用CPU资源,这才能保证数据的决对完整性,当然这是对cpytho来说的,像jpython就没有gIL, 所以如果Python运行在多核cpu上推荐用多进程模式,线程模式运用场景是IO密集型的,对于cpu计算密集型来说效率会降低,就爬虫程序来说,它就是一个典型的IO密集型操作,所以用Python最好不过,如果不知道上面那些这些内容也没关系,就记住在什么场景下使用就可以了,今天主要是给爬虫例子,代码如下:

这个脚本只是个简单例子,定义了一个数据保存函数save(),一个获取网页内容函数crawl(),模块用的第三方模块requests, MyCrawler类中调用父类的初始化方法,然后重新run函数,就是要把我们要执行的代码放到这个函数里,之前我写过一篇文章,详细写了python线程的使用方式,一般来说我们经常使用的就是函数或者用类来包装线程对象,例子里这次我们用的是类包装的方法,就是直接从threading.Thread继承,然后重写__init__方法和run方法, 例子很简单,不详细解释代码含义了,有兴趣的可以再这个代码上进行修改,实现更复杂的爬虫程序。

 

python分析nginx日志

上周咳嗽比较厉害,暂停了一周更新,收到很多小伙伴的留言关心,真是非常感动,非常感谢大家支持,我会尽我努力给大家分享干货, 现在吃了几天药,基本好的差不多了,这周接着聊python在运维中的实践,今天的脚本是分析nginx的访问日志, 主要为了检查站点uri的访问次数的,检查的结果会提供给研发人员做参考,因为谈到分析嘛,那肯定要用到正则表达式了,所以请没有接触过正则的小伙伴自行补脑,因为涉及正则的内容,实在没法展开写,正则的内容太过庞大,根本不是一篇两篇能写清楚的,开始前,我们先看看要分析的日志结构:

这是修改过的日志内容,敏感内容都以删除或替换了,不过不影响我们的分析结果,当然格式什么的这都不重要,Nginx访问日志是可以自定义的,每家公司可能都会稍有不同,所以要能理解脚本内容,并通过自己修改应用到了自己工作中才是重点,我给的日志格式也就是个参考,我打赌你在你公司服务器上看到的日志格式肯定跟我的格式不一样, 看完日志格式,我们开始要写我们的脚本了,我先贴代码,稍后解释:

脚本解释,parser_logfile()函数功能是分析日志,返回匹配的行列表,正则部分就不解释了,大家看注释应该知道它是匹配什么内容的,parser_urllist()函数功能是将获取用户访问的url,get_urldict()函数功能是返回一个字典,以url为键,如果键相同值增1,返回的字典是每个url和最大的访问次数,url_count()函数功能就是调用了之前定义的函数,主函数部分,就说说itemgetter,它可以实现按指定元素进行排序,举例就明白了:

reverse=True参数表示降序排序,就是从大到小排序,脚本运行结果: