hashlib是个专门提供hash算法的库,现在里面包括md5, sha1, sha224, sha256, sha384, sha512,使用非常简单、方便。
md5经常用来做用户密码的存储。而sha1则经常用作数字签名。下面看看代码吧:
#-*- encoding:utf-8 -*-
import hashlib
a = "hello,world"
print hashlib.md5(a).hexdigest()
print hashlib.sha1(a).hexdigest()
print hashlib.sha224(a).hexdigest()
print hashlib.sha256(a).hexdigest()
print hashlib.sha384(a).hexdigest()
print hashlib.sha512(a).hexdigest()
ly:http://blog.csdn.net/oyd/archive/2007/07/19/1699237.aspx 我这里介绍一个极适合大量URL快速排重的方法 ,这个算法被称为Bloom filter,基本上,它也只适合这样的场合。 这里的大量是指有5000万至1亿的URL,更大的数据量可能也不合适了。 一开始我使用了一个最复杂的做法,是有一个单独的daemon程序负责排重,数据和排重结果通过socket传输。 所以,把目标锁定在单机排重,一开始,试验了perl中的hash,非常简单的代码 从标准输入或文件中每行一个URL读入,插入到perl内置的hash表中,这就成了,需要输出结果则预先判断一下插入的key是否存在。 这个方法速度很快,可惜的是,它占用内存太大,假设1个URL平均50字节,5000万个URL需要2.5G内存。 于是又想到一个方法,把部分数据放入硬盘空间,perl中也提供一个现成的模块DB_File,把上面代码中的注释去掉,就可使用DB_File了,用法与hash一样,只是内部用数据库实现的。 测试了一下,速度明显下降了一个档次,仅40万的数据就要1分钟,关键还在于随着数据量的增加,速度下降加快,两者不呈线性关系。 数据量大的时候,有可能用MySQL的性能会比DB_File好,但是总体上应该是一丘之貉,我已经不抱期望了。 也许DB_File可以优化一下,使用更多的内存和少量的硬盘空间,不过这个方案还是太复杂,留给专家解决吧,一般来说,我认为简单的方法才有可能做到高效。 下面我们的重点对象隆重登场:Bloom filter。简单的说是这样一种方法:在内存中开辟一块区域,对其中所有位置0,然后对数据做10种不同的hash,每个hash值对内存bit数求模,求模得到的数在内存对应的位上置1。置位之前会先判断是否已经置位,每次插入一个URL,只有当全部10个位都已经置1了才认为是重复的。 如果对上面这段话不太理解,可以换个简单的比喻:有10个桶,一个桶只能容纳1个球,每次往这些桶中扔两个球,如果两个桶都已经有球,才认为是重复,问问为了不重复总共能扔多少次球? 10次,是这个答案吧?每次扔1个球的话,也是10次。表面上看一次扔几个球没有区别,事实上一次两个球的情况下,重复概率比一次一个球要低。Bloom filter算法正式借助这一点,仅仅用少量的空间就可以进行大量URL的排重,并且使误判率极低。 有人宣称为每个URL分配两个字节就可以达到0冲突,我比较保守,为每个URL分配了4个字节,对于5000万的数量级,它只占用了100多M的空间,并且排重速度超快,一遍下来不到两分钟,极大得满足了我的欲望。
后来发现不行,仅仅几百万数据要做好几个小时,5000万不把人都急疯了?至于daemon中具体用什么算法就次要了,因为一涉及到网络通讯,速度再快也被拉下来(这里针对的是发送一条记录/返回一条结果的模式,一次传送一批数据则与网络状况有关了)
use DB_File;
my %db;
#tie %db, 'DB_File', "createdb.dat", or die "Can't initialize db:: $! ";
while(<>) {
chomp $_;
$db{$_} = 1;# add code here
}
#untie %db;
top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器。下面详细介绍它的使用方法。
top - 01:06:48 up 1:22, 1 user, load average: 0.06, 0.60, 0.48
Tasks: 29 total, 1 running, 28 sleeping, 0 stopped, 0 zombie
Cpu(s): 0.3% us, 1.0% sy, 0.0% ni, 98.7% id, 0.0% wa, 0.0% hi, 0.0% si
Mem: 191272k total, 173656k used, 17616k free, 22052k buffers
Swap: 192772k total, 0k used, 192772k free, 123988k cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1379 root 16 0 7976 2456 1980 S 0.7 1.3 0:11.03 sshd
14704 root 16 0 2128 980 796 R 0.7 0.5 0:02.72 top
1 root 16 0 1992 632 544 S 0.0 0.3 0:00.90 init
2 root 34 19 0 0 0 S 0.0 0.0 0:00.00 ksoftirqd/0
3 root RT 0 0 0 0 S 0.0 0.0 0:00.00 watchdog/0
统计信息区
前五行是系统整体的统计信息。第一行是任务队列信息,同 uptime 命令的执行结果。其内容如下:
01:06:48 当前时间
up 1:22 系统运行时间,格式为时:分
1 user 当前登录用户数
load average: 0.06, 0.60, 0.48 系统负载,即任务队列的平均长度。
三个数值分别为 1分钟、5分钟、15分钟前到现在的平均值。
第二、三行为进程和CPU的信息。当有多个CPU时,这些内容可能会超过两行。内容如下:
Tasks: 29 total 进程总数
1 running 正在运行的进程数
28 sleeping 睡眠的进程数
0 stopped 停止的进程数
0 zombie 僵尸进程数
Cpu(s): 0.3% us 用户空间占用CPU百分比
1.0% sy 内核空间占用CPU百分比
0.0% ni 用户进程空间内改变过优先级的进程占用CPU百分比
98.7% id 空闲CPU百分比
0.0% wa 等待输入输出的CPU时间百分比
0.0% hi
0.0% si
最后两行为内存信息。内容如下:
Mem: 191272k total 物理内存总量
173656k used 使用的物理内存总量
17616k free 空闲内存总量
22052k buffers 用作内核缓存的内存量
Swap: 192772k total 交换区总量
0k used 使用的交换区总量
192772k free 空闲交换区总量
123988k cached 缓冲的交换区总量。
内存中的内容被换出到交换区,而后又被换入到内存,但使用过的交换区尚未被覆盖,
该数值即为这些内容已存在于内存中的交换区的大小。
相应的内存再次被换出时可不必再对交换区写入。
进程信息区
统计信息区域的下方显示了各个进程的详细信息。首先来认识一下各列的含义。
序号 列名 含义
a PID 进程id
b PPID 父进程id
c RUSER Real user name
d UID 进程所有者的用户id
e USER 进程所有者的用户名
f GROUP 进程所有者的组名
g TTY 启动进程的终端名。不是从终端启动的进程则显示为 ?
h PR 优先级
i NI nice值。负值表示高优先级,正值表示低优先级
j P 最后使用的CPU,仅在多CPU环境下有意义
k %CPU 上次更新到现在的CPU时间占用百分比
l TIME 进程使用的CPU时间总计,单位秒
m TIME+ 进程使用的CPU时间总计,单位1/100秒
n %MEM 进程使用的物理内存百分比
o VIRT 进程使用的虚拟内存总量,单位kb。VIRT=SWAP+RES
p SWAP 进程使用的虚拟内存中,被换出的大小,单位kb。
q RES 进程使用的、未被换出的物理内存大小,单位kb。RES=CODE+DATA
r CODE 可执行代码占用的物理内存大小,单位kb
s DATA 可执行代码以外的部分(数据段+栈)占用的物理内存大小,单位kb
t SHR 共享内存大小,单位kb
u nFLT 页面错误次数
v nDRT 最后一次写入到现在,被修改过的页面数。
w S 进程状态。
D=不可中断的睡眠状态
R=运行
S=睡眠
T=跟踪/停止
Z=僵尸进程
x COMMAND 命令名/命令行
y WCHAN 若该进程在睡眠,则显示睡眠中的系统函数名
z Flags 任务标志,参考 sched.h
默认情况下仅显示比较重要的 PID、USER、PR、NI、VIRT、RES、SHR、S、%CPU、%MEM、TIME+、COMMAND 列。可以通过下面的快捷键来更改显示内容。
更改显示内容
通过 f 键可以选择显示的内容。按 f 键之后会显示列的列表,按 a-z 即可显示或隐藏对应的列,最后按回车键确定。
按 o 键可以改变列的显示顺序。按小写的 a-z 可以将相应的列向右移动,而大写的 A-Z 可以将相应的列向左移动。最后按回车键确定。
按大写的 F 或 O 键,然后按 a-z 可以将进程按照相应的列进行排序。而大写的 R 键可以将当前的排序倒转。
命令使用
1. 工具(命令)名称
top
2.工具(命令)作用
显 示系统当前的进程和其他状况; top是一个动态显示过程,即可以通过用户按键来不断刷新当前状态.如果在前台执行该命令,它将独占前台,直到用户终止该程序为止. 比较准确的说,top命令提供了实时的对系统处理器的状态监视.它将显示系统中CPU最“敏感”的任务列表.该命令可以按CPU使用.内存使用和执行时间 对任务进行排序;而且该命令的很多特性都可以通过交互式命令或者在个人定制文件中进行设定.
3.环境设置
在Linux下使用。
4.使用方法
4.1使用格式
top [-] [d] [p] [q] [c] [C] [S] [s] [n]
4.2参数说明
d 指定每两次屏幕信息刷新之间的时间间隔。当然用户可以使用s交互命令来改变之。
p 通过指定监控进程ID来仅仅监控某个进程的状态。
q该选项将使top没有任何延迟的进行刷新。如果调用程序有超级用户权限,那么top将以尽可能高的优先级运行。
S 指定累计模式
s 使top命令在安全模式中运行。这将去除交互命令所带来的潜在危险。
i 使top不显示任何闲置或者僵死进程。
c 显示整个命令行而不只是显示命令名
4.3其他
下面介绍在top命令执行过程中可以使用的一些交互命令。从使用角度来看,熟练的掌握这些命令比掌握选项还重要一些。这些命令都是单字母的,如果在命令行选项中使用了s选项,则可能其中一些命令会被屏蔽掉。
Ctrl+L 擦除并且重写屏幕。
h或者? 显示帮助画面,给出一些简短的命令总结说明。
k 终止一个进程。系统将提示用户输入需要终止的进程PID,以及需要发送给该进程什么样的信号。一般的终止进程可以使用15信号;如果不能正常结束那就使用信号9强制结束该进程。默认值是信号15。在安全模式中此命令被屏蔽。
i 忽略闲置和僵死进程。这是一个开关式命令。
q 退出程序。
r 重新安排一个进程的优先级别。系统提示用户输入需要改变的进程PID以及需要设置的进程优先级值。输入一个正值将使优先级降低,反之则可以使该进程拥有更高的优先权。默认值是10。
S 切换到累计模式。
s 改变两次刷新之间的延迟时间。系统将提示用户输入新的时间,单位为s。如果有小数,就换算成m s。输入0值则系统将不断刷新,默认值是5 s。需要注意的是如果设置太小的时间,很可能会引起不断刷新,从而根本来不及看清显示的情况,而且系统负载也会大大增加。
f或者F 从当前显示中添加或者删除项目。
o或者O 改变显示项目的顺序。
l 切换显示平均负载和启动时间信息。
m 切换显示内存信息。
t 切换显示进程和CPU状态信息。
c 切换显示命令名称和完整命令行。
M 根据驻留内存大小进行排序。
P 根据CPU使用百分比大小进行排序。
T 根据时间/累计时间进行排序。
W 将当前设置写入~/.toprc文件中。这是写top配置文件的推荐方法。
原文出处:http://blog.csdn.net/tinydust/archive/2006/07/16/930496.aspx
一个Python程序的6次改进
有 位朋友用CString类写了一个Email列表去重程序,结果发现处理78000 行的数据居然用了7分多种。于是他用Python的map(hash实现的)重写了这个程序,最后只用了300ms。CString一定会慢,因为实现中 没有使用hash。于是有人用c写了一个hash函数来实现这个功能,最后花了400ms。但是代码长度是Python代码的数十倍。详情请看这里。
qyb利用glib的hashtable功能也实现了这个功能,速度超过了Python,但是代码也很简洁。qyb说,“这个例子再一次教育我们,熟练掌握优秀的第三方库是多么的重要.”
我这里没有现成的gcc和glib,不好测试qyb的程序。但是仅就那个用来测试Python程序来说,存在很多降低效率的误区,所以,我把这些问题都用效率更加的方式改写了,从而把程序的效率提高了三倍左右,现在与大家分享。
原程序:运行时间0:00:00.609000
import datetime
if __name__ == "__main__":
t1=datetime.datetime.today()
hashtable = {}
f = file("email.txt","r")
f2 = file("email_new.txt","w")
line = f.readline();
while len(line)>0:
if not hashtable.has_key(line):
hashtable[line] = 1
f2.write(line)
line = f.readline();
f.close()
f2.close()
t2=datetime.datetime.today()
print str(t2-t1)
显而易见,用f.readline()是不明智的,这样输入速度大减。我们用readlines()来加速。
改进一:运行时间0:00:00.375000
import datetime
if __name__ == "__main__":
t1=datetime.datetime.today()
hashtable = {}
f = file("email.txt","r")
f2 = file("email_new.txt","w")
lines = f.readlines()
for line in lines:
if not hashtable.has_key(line):
hashtable[line] = 1
f2.write(line)
f.close()
f2.close()
t2=datetime.datetime.today()
print str(t2-t1)
然 后我们看输出,很显然f2.write(line)也是很不明智的,一行一行的输出无论如何都是低效的。writelines可以解决这个问题,我们知道 map的keys()函数可以返回map的键列表,所以, f2.writelines(hashtable.keys())看来是个好选择。
改进二:运行时间0:00:00.281000
import datetime
if __name__ == "__main__":
t1=datetime.datetime.today()
hashtable = {}
f = file("email.txt","r")
f2 = file("email_new.txt","w")
lines = f.readlines()
for line in lines:
if not hashtable.has_key(line):
hashtable[line] = 1
f.close()
f2.writelines(hashtable.keys())
f2.close()
t2=datetime.datetime.today()
print str(t2-t1)
实际上我们知道采用hashtable[line] = 1方式的时候,如果键已经存在,仅仅会更新键的值而已。所以判断语句if not hashtable.has_key(line)实际上是无用的,应该去掉。
改进三:运行时间0:00:00.250000
import datetime
if __name__ == "__main__":
t1=datetime.datetime.today()
hashtable = {}
f = file("email.txt","r")
f2 = file("email_new.txt","w")
lines = f.readlines()
for line in lines:
hashtable[line] = 1
f.close()
f2.writelines(hashtable.keys())
f2.close()
t2=datetime.datetime.today()
print str(t2-t1)
到这里我们发现另外一个问题,我们实际上只需要map的键,而不需要它的值。所以我们直接用操作map的键列表的方式应该可以加速程序的运行。幸好,运行的结果证明了我的推论。
改进四:运行时间0:00:00.188000
import datetime
if __name__ == "__main__":
t1=datetime.datetime.today()
hashtable = {}
f = file("email.txt","r")
f2 = file("email_new.txt","w")
lines = f.readlines()
for line in lines:
hashtable.keys().append(line)
f.close()
f2.writelines(hashtable.keys())
f2.close()
t2=datetime.datetime.today()
print str(t2-t1)
然后,我遭遇了一个小挫败,我记得有人告诉过我用map函数处理列表应该比for in方式更加高效。但是运行结果没有遵循这个说法,也许是我哪里写错了。不过至少map和lamdba可以让程序看起来简洁一些,或者说更酷,更加让人难懂,哈哈。
改进五:运行时间0:00:00.250000
import datetime
if __name__ == "__main__":
t1=datetime.datetime.today()
hashtable = {}
f = file("email.txt","r")
f2 = file("email_new.txt","w")
lines = f.readlines()
map(lambda x:hashtable.keys().append(x),lines)
f.close()
f2.writelines(hashtable.keys())
f2.close()
t2=datetime.datetime.today()
print str(t2-t1)
这个方法不是我写的,而是qyb文章后一个匿名网友的留言。应该说这个方法我没有想到的主要原因是我确实不知道set类型,-____-#。这个方法无疑是最简洁的一种,虽然效率不如我的第四种改进方式,也许这是因为一些实现上的问题造成的。
改进六:运行时间0:00:00.219000
import datetime
if __name__ == "__main__":
t1=datetime.datetime.today()
f = file("email.txt","r")
f2 = file("email_new.txt","w")
f2.write("".join(set(f.readlines())))
f.close()
f2.close()
t2=datetime.datetime.today()
print str(t2-t1)
LY:http://fengzl.javaeye.com/blog/137515
互联网是一个庞大的非结构化的数据库,将数据有效的检索并组织呈现出来有着巨大的应用前景,尤其是类似RSS的以XML为基础的结构化的数据越来越 多,内容的组织方式越来越灵活,检索组织并呈现会有着越来越广泛的应用范围,同时在时效性和可读性上也会有越来越高的要求。这一切的基础是爬虫,信息的来源入口。一个高效,灵活可扩展的爬虫对以上应用都有着无可替代的重要意义。
要设计一个爬虫,首先需要考虑的效率。对于网络而言,基于TCP/IP的通信编程有几种方法。
第一种是单线程阻塞,这是最简单也最容易实现的一种,一个例子:在Shell中通过curl,pcregrep等一系统命令可以直接实现一个简单的爬虫,但同时它的效率问题也显而易见:由于是阻塞方式读取,dns解析,建立连接,写入请求,读取结果这些步骤上都会产生时间的延迟,从而无法有效的利用服务器的全部资源。
第二种是多线程阻塞。建立多个阻塞的线程,分别请求不同的url。相对于第一种方法,它可以更有效的利用机器的资源,特别是网络资源,因为无数线程 在同时工作,所以网络会比较充分的利用,但同时对机器CPU资源的消耗也是比较大,在用户级多线程间的频繁切换对于性能的影响已经值得我们考虑。
第三种是单线程非阻塞。这是目前使用的比较多的一种做法,无论在client还是server都有着广泛的应用。在一个线程内打开多个非阻塞的连 接,通过poll/epoll/select对连接状态进行判断,在第一时间响应请求,不但充分利用了网络资源,同时也将本机CPU资源的消耗降至最低。 这种方法需要对dns请求,连接,读写操作都采用异步非阻塞操作,其中第一种比较复杂,可以采用adns作为解决方案,后面三个操作相对简单可以直接在程 序内实现。
效率问题解决后就需要考虑具体的设计问题了。
url肯定需要一个单独的类进行处理,包括显示,分析url,得到主机,端口,文件数据。
然后需要对url进行排重,需要一个比较大的url Hash表。
如果还要对网页内容进行排重,则还需要一个Document Hash表。
爬过的url需要记录下来,由于量比较大,我们将它写到磁盘上,所以还需要一个FIFO的类(记作urlsDisk)。
现在需要爬的url同样需要一个FIFO类来处理,重新开始时,url会从定时从爬过的url FIFO里取出来,写到这个FIFO里。正在运行的爬虫需要从这个FIFO里读数据出来,加入到主机类的url列表里。当然,也会从前一个FIFO里直接读url出来,不过优先级应该比这个里面出来的url低,毕竟是已经爬过的。
爬虫一般是对多个网站进行爬取,但在同时站点内dns的请求可以只做一次,这就需要将主机名独立于url,单独有一个类进行处理。
主机名解析完成后需要有一个解析完成的IP类与之应用,用于connect的时候使用。
HTML文档的解析类也要有一个,用来分析网页,取出里面的url,加入到urlsDisk。
再加上一些字符串,调度类,一个简单的爬虫基本上就完成了。
以上基本上是Larbin的设计思路,Larbin在具体实现上还有一些特殊的处理,例如带了一个webserver,以及对特殊文件的处理。 Larbin有一点设计不不太好,就是慢的访问会越来越多,占用大量的连接,需要改进,另外如果对于大规模的爬虫,这仅仅实现了抓取的部分,要分布式的扩展还需要增加url的集中管理与调度以及前台spider的分布式算法。
用python编写分布式爬虫
1、 网络连接需要持续连接(persistent connection),DNS解析的瓶颈(先查本地DNS缓存)
2、 多线程:机器任务的分配及站点任务的分配。
3、 对web文件树遍历过程更好的控制,对web文件树在广度优先遍历时层次的判断。(整个网络是一个图,而某个站点的模型更接近于一棵树)
4、 利用robotparser解析robots.txt
5、 单个机器spider的作用:
6、 中央控制器的作用:
7、 解析html(超级链接的提取)搞定(用python的sgmllib)缺点:速度太慢(可能会造成瓶颈,要好好包装好,以后有机会换掉它)
证下的自由软件,其作者为Hrvoje Niksic <hniksic@srce.hr>。wget支持HTTP和FTP
协议,支持代理服务器和断点续传功能,能够自动递归远程主机的目录,找到合乎条
件的文件并将其下载到本地硬盘上;如果必要,wget将恰当地转换页面中的超级连接
以在本地生成可浏览的镜像。由于没有交互式界面,wget可在后台运行,截获并忽略
HANGUP信号,因此在用户推出登录以后,仍可继续运行。通常,wget用于成批量地下
载Internet网站上的文件,或制作远程网站的镜像。
语法:
wget [options] [URL-list]
URL地址格式说明:可以使用如下格式的URL:
http://host[:port]/path
例如:
http://fly.cc.fer.hr/
ftp://ftp.xemacs.org/pub/xemacs/xemacs-19.14.tar.gz
ftp://username:password@host/dir/file
在最后一种形式中,以URL编码形式为FTP主机提供了用户名和密码(当然,也可以使
用参数提供该信息,见后)。
参数说明:
wget的参数较多,但大部分应用只需要如下几个常用的参数:
-r 递归;对于HTTP主机,wget首先下载URL指定的文件,然后(如果该文件是
一个HTML文档的话)递归下载该文件所引用(超级连接)的所有文件(递归深度
由参数-l指定)。对FTP主机,该参数意味着要下载URL指定的目录中的所有文件,
递归方法与HTTP主机类似。
-N 时间戳:该参数指定wget只下载更新的文件,也就是说,与本地目录中的对
应文件的长度和最后修改日期一样的文件将不被下载。
-m 镜像:相当于同时使用-r和-N参数。
-l 设置递归级数;默认为5。-l1相当于不递归;-l0为无穷递归;注意,当递
归深度增加时,文件数量将呈指数级增长。
-t 设置重试次数。当连接中断(或超时)时,wget将试图重新连接。如果指
定-t0,则重试次数设为无穷多。
-c 指定断点续传功能。实际上,wget默认具有断点续传功能,只有当你使用别
的ftp工具下载了某一文件的一部分,并希望wget接着完成此工作的时候,才需要
指定此参数。
使用举例:
wget -m -l4 -t0 http://oneweb.com.cn/
将在本地硬盘建立http://oneweb.com.cn/的镜像,镜像文件存入当前目录下一个名为
oneweb.com.cn的子目录中(你也可以使用-nH参数指定不建立该子目录,而直接在当前
目录下建立镜像的目录结构),递归深度为4,重试次数为无穷(若连接出现问题,
wget将坚韧不拔地永远重试下去,知道任务完成!)
另外一些使用频率稍低的参数如下:
-A acclist / -R rejlist:
这两个参数用于指定wget接受或排除的文件扩展名,多个名称之间用逗号隔开。例如,
假设我们不想下载MPEG视频影像文件和.AU声音文件,可使用如下参数:
-R mpg,mpeg,au
其它参数还有:
-L 只扩展相对连接,该参数对于抓取指定站点很有用,可以避免向宿主主机
的其他目录扩散。例如,某个人网站地址为:http://www.xys.org/~ppfl/,使用
如下命令行:
wget -L http://www.xys.org/~ppfl/
则只提取该个人网站,而不涉及主机www.xys.org上的其他目录。
-k 转换连接:HTML文件存盘时,将其中的非相对连接转换成为相对连接。
-X 在下载FTP主机上的文件时,排除若干指定的目录
另外,下面参数用于设置wget的工作界面:
-v 设置wget输出详细的工作信息。
-q 设置wget不输出任何信息。
如果我们已经在一个HTML文档(或普通文本文档)中存储了所要提取的文件的连接,
可以让wget直接从该文件中提取信息,而不用在命令行中提供URL地址,参数格式为:
-i filename
地址文件也可以不是HTML文档,例如,一个普通的文本文件,其中有需要下载的URL列
表即可。
我们可以用以下技巧提高下载速度:由于Linux是一个多任务系统,我们可以同时运行
多个wget进程以提高下载速度,例如,先下载某主页文件(index.html),然后将该
文件所列出的所有地址分别用一个独立的wget进程进行下载。
至于其他的参数,可参考wget的man手册页,命令为:
man wget
WGet使用指南
wget是一个从网络上自动下载文件的自由工具。它支持HTTP,HTTPS和FTP协议,可以使用HTTP代理.
所谓的自动下载是指,wget可以在用户退出系统的之后在后台执行。这意味这你可以登录系统,启动一个wget下载任务,然后退出系统,wget将在后台执行直到任务完成,相对于其它大部分浏览器在下载大量数据时需要用户一直的参与,这省去了极大的麻烦。
wget可以跟踪HTML页面上的链接依次下载来创建远程服务器的本地版本,完全重建原始站点的目录结构。这又常被称作”递归下载”。在递归下载的时 候,wget遵循Robot Exclusion标准(/robots.txt). wget可以在下载的同时,将链接转换成指向本地文件,以方便离线浏览。
wget非常稳定,它在带宽很窄的情况下和不稳定网络中有很强的适应性.如果是由于网络的原因下载失败,wget会不断的尝试,直到整个文件下载 完毕。如果是服务器打断下载过程,它会再次联到服务器上从停止的地方继续下载。这对从那些限定了链接时间的服务器上下载大文件非常有用。
wget的常见用法
wget的使用格式
Usage: wget [OPTION]... [URL]...
* 用wget做站点镜像:
wget -r -p -np -k http://dsec.pku.edu.cn/~usr_name/
# 或者
wget -m http://www.tldp.org/LDP/abs/html/
* 在不稳定的网络上下载一个部分下载的文件,以及在空闲时段下载
wget -t 0 -w 31 -c http://dsec.pku.edu.cn/BBC.avi -o down.log &
# 或者从filelist读入要下载的文件列表
wget -t 0 -w 31 -c -B ftp://dsec.pku.edu.cn/linuxsoft -i filelist.txt -o down.log &
上面的代码还可以用来在网络比较空闲的时段进行下载。我的用法是:在mozilla中将不方便当时下载的URL链接拷贝到内存中然后粘贴到文件filelist.txt中,在晚上要出去系统前执行上面代码的第二条。
* 使用代理下载
wget -Y on -p -k https://sourceforge.net/projects/wvware/
代理可以在环境变量或wgetrc文件中设定
# 在环境变量中设定代理
export PROXY=http://211.90.168.94:8080/
# 在~/.wgetrc中设定代理
http_proxy = http://proxy.yoyodyne.com:18023/
ftp_proxy = http://proxy.yoyodyne.com:18023/
wget各种选项分类列表
* 启动
-V, --version 显示wget的版本后退出
-h, --help 打印语法帮助
-b, --background 启动后转入后台执行
-e, --execute=COMMAND 执行`.wgetrc'格式的命令,wgetrc格式参见/etc/wgetrc或~/.wgetrc
* 记录和输入文件
-o, --output-file=FILE 把记录写到FILE文件中
-a, --append-output=FILE 把记录追加到FILE文件中
-d, --debug 打印调试输出
-q, --quiet 安静模式(没有输出)
-v, --verbose 冗长模式(这是缺省设置)
-nv, --non-verbose 关掉冗长模式,但不是安静模式
-i, --input-file=FILE 下载在FILE文件中出现的URLs
-F, --force-html 把输入文件当作HTML格式文件对待
-B, --base=URL 将URL作为在-F -i参数指定的文件中出现的相对链接的前缀
--sslcertfile=FILE 可选客户端证书
--sslcertkey=KEYFILE 可选客户端证书的KEYFILE
--egd-file=FILE 指定EGD socket的文件名
* 下载
--bind-address=ADDRESS 指定本地使用地址(主机名或IP,当本地有多个IP或名字时使用)
-t, --tries=NUMBER 设定最大尝试链接次数(0 表示无限制).
-O --output-document=FILE 把文档写到FILE文件中
-nc, --no-clobber 不要覆盖存在的文件或使用.#前缀
-c, --continue 接着下载没下载完的文件
--progress=TYPE 设定进程条标记
-N, --timestamping 不要重新下载文件除非比本地文件新
-S, --server-response 打印服务器的回应
--spider 不下载任何东西
-T, --timeout=SECONDS 设定响应超时的秒数
-w, --wait=SECONDS 两次尝试之间间隔SECONDS秒
--waitretry=SECONDS 在重新链接之间等待1...SECONDS秒
--random-wait 在下载之间等待0...2*WAIT秒
-Y, --proxy=on/off 打开或关闭代理
-Q, --quota=NUMBER 设置下载的容量限制
--limit-rate=RATE 限定下载输率
* 目录
-nd --no-directories 不创建目录
-x, --force-directories 强制创建目录
-nH, --no-host-directories 不创建主机目录
-P, --directory-prefix=PREFIX 将文件保存到目录 PREFIX/...
--cut-dirs=NUMBER 忽略 NUMBER层远程目录
* HTTP 选项
--http-user=USER 设定HTTP用户名为 USER.
--http-passwd=PASS 设定http密码为 PASS.
-C, --cache=on/off 允许/不允许服务器端的数据缓存 (一般情况下允许).
-E, --html-extension 将所有text/html文档以.html扩展名保存
--ignore-length 忽略 `Content-Length'头域
--header=STRING 在headers中插入字符串 STRING
--proxy-user=USER 设定代理的用户名为 USER
--proxy-passwd=PASS 设定代理的密码为 PASS
--referer=URL 在HTTP请求中包含 `Referer: URL'头
-s, --save-headers 保存HTTP头到文件
-U, --user-agent=AGENT 设定代理的名称为 AGENT而不是 Wget/VERSION.
--no-http-keep-alive 关闭 HTTP活动链接 (永远链接).
--cookies=off 不使用 cookies.
--load-cookies=FILE 在开始会话前从文件 FILE中加载cookie
--save-cookies=FILE 在会话结束后将 cookies保存到 FILE文件中
* FTP 选项
-nr, --dont-remove-listing 不移走 `.listing'文件
-g, --glob=on/off 打开或关闭文件名的 globbing机制
--passive-ftp 使用被动传输模式 (缺省值).
--active-ftp 使用主动传输模式
--retr-symlinks 在递归的时候,将链接指向文件(而不是目录)
* 递归下载
-r, --recursive 递归下载--慎用!
-l, --level=NUMBER 最大递归深度 (inf 或 0 代表无穷).
--delete-after 在现在完毕后局部删除文件
-k, --convert-links 转换非相对链接为相对链接
-K, --backup-converted 在转换文件X之前,将之备份为 X.orig
-m, --mirror 等价于 -r -N -l inf -nr.
-p, --page-requisites 下载显示HTML文件的所有图片
* 递归下载中的包含和不包含(accept/reject)
-A, --accept=LIST 分号分隔的被接受扩展名的列表
-R, --reject=LIST 分号分隔的不被接受的扩展名的列表
-D, --domains=LIST 分号分隔的被接受域的列表
--exclude-domains=LIST 分号分隔的不被接受的域的列表
--follow-ftp 跟踪HTML文档中的FTP链接
--follow-tags=LIST 分号分隔的被跟踪的HTML标签的列表
-G, --ignore-tags=LIST 分号分隔的被忽略的HTML标签的列表
-H, --span-hosts 当递归时转到外部主机
-L, --relative 仅仅跟踪相对链接
-I, --include-directories=LIST 允许目录的列表
-X, --exclude-directories=LIST 不被包含目录的列表
-np, --no-parent 不要追溯到父目录
_________________
参数太多让人眼花缭乱,我来献丑列举几个常见的用法:
1, wget www.aaa.com/bbb.zip
最简单,一目了然。
2, wget www.aaa.com/bbb.zip --ref=www.aaa.com/ccc.html
碰到需要引用相应页面的文件,主要是防止盗链。
3, wget -k -np -r www.aaa.com/bbb/
抓取www.aaa.com网站下bbb目录的文件并将相应链接转换成本地可浏览的链接形式,用于本地镜像一个网站。
4, 上次下载漫画时用的一个脚本,很简陋:
#!/bin/bash
for i in `seq -w 100`
do
wget www.aaa.com/bbb"$i".jpg --ref www.aaa.com/ccc.html
done
可以使用下面的命令 wget -r -p -k -np http://hi.baidu.com/phps , -r 表示递归下载,会下载所有的链接,不过要注意的是,不要单独使用这个参数,因为如果你要下载的网站也有别的网站的链接,wget也会把别的网站的东西下载 下来,由于互联网的特性,很有可能你会把整个互联网给下载下来 --,所以要加上 -np这个参数,表示不下载别的站点的链接. -k表示将下载的网页里的链接修改为本地链接.-p获得所以显示网页所需的元素,比如图片什么的.
另外还有其他的一些参数可以使用:
-c表示断点续传
-t 100表示重试100次,-t 0表示无穷次重试
另外可以将要下载的url写到一个文件中,每个url一行,使用这样的命令 wget -i download.txt.
--reject=avi,rmvb 表示不下载avi,rmvb的文件,--accept=jpg,jpeg,表示只下载jpg,jpeg的文件.
可以在用户目录下建立一个.wgetrc的文件(windows里面好像不能直接建立这样的文件,windows会认为没有文件名--),里面写上 http-proxy = 123.456.78.9:80,然后在加上参数 --proxy=on,如果需要密码,再加上下面的参数 --proxy-user=username, --proxy-passwd=password
python可以做shell脚本吗?当然可以的。首先介绍一些相关函数:
os.system(command)
这个函数可以调用shell运行命令行command并且返回它的返回值。试一下在python的解释器里输入os.system(”ls -l”),就可以看到”ls”列出了当前目录下的文件。可以说,通过这个函数,python就拥有了shell的所有能力。呵呵。。不过,通常这条命令不需要用到。因为shell常用的那些命令在python中通常有对应而且同样简洁的写法。
shell中最常用的是ls命令,python对应的写法是:os.listdir(dirname),这个函数返回字符串列表,里面是所有的文件名,不过不包含”.”和”..”。如果要遍历整个目录的话就会比较复杂一点。我们等下再说吧。先在解释器里试一下:
>>> os.listdir(”/”)
[’tmp’, ‘misc’, ‘opt’, ‘root’, ‘.autorelabel’, ’sbin’, ’srv’, ‘.autofsck’, ‘mnt’, ‘usr’, ‘var’, ‘etc’, ’selinux’, ‘lib’, ‘net’, ‘lost+found’, ’sys’, ‘media’, ‘dev’, ‘proc’, ‘boot’, ‘home’, ‘bin’]
就像这样,接下去所有命令都可以在python的解释器里直接运行观看结果。
对应于cp命令的是:shutil.copy(src,dest),这个函数有两个参数,参数src是指源文件的名字,参数dest则是目标文件或者目标目录的名字。 如果dest是一个目录名,就会在那个目录下创建一个相同名字的文件。与shutil.copy函数相类似的是shutil.copy2(src,dest),不过copy2还会复制最后存取时间和最后更新时间。
不过,shell的cp命令还可以复制目录,python的shutil.copy却不行,第一个参数只能是一个文件。这怎么办?其实,python还有个shutil.copytree(src,dst[,symlinks]) 。参数多了一个symlinks,它是一个布尔值,如果是True的话就创建符号链接。
移动或者重命名文件和目录呢?估计被聪明的朋友猜到了,shutil.move(src,dst),呵呵。。与mv命令类似,如果src和dst在同一个文件系统上,shutil.move只是简单改一下名字,如果src和dst在不同的文件系统上,shutil.move会先把src复制到dst,然后删除src文件。看到现在,大多数朋友应该已经对python的能力有点眉目了,接下来我就列个表,介绍一下其它的函数:
os.chdir(dirname)
把当前工作目录切换到dirname下
os.getcwd()
返回当前的工作目录路径
os.chroot(dirname)
把dirname作为进程的根目录。和*nix下的chroot命令类似
os.chmod(path,mode)
更改path的权限位。mode可以是以下值(使用or)的组合:
os.S_ISUID
os.S_ISGID
os.S_ENFMT
os.S_ISVTX
os.S_IREAD
os.S_IWRITE
os.S_IEXEC
os.S_IRWXU
os.S_IRUSR
os.S_IWUSR
os.S_IXUSR
os.S_IRWXG
os.S_IRGRP
os.S_IWGRP
os.S_IXGRP
os.S_IRWXO
os.S_IROTH
os.S_IWOTH
os.S_IXOTH
具体它们是什么含义,就不仔细说了,基本上就是R代表读,W代表写,X代表执行权限。USR代表用户,GRP代表组,OTH代表其它。
os.chown(path,uid,gid)
改变文件的属主。uid和gid为-1的时候不改变原来的属主。
os.link(src,dst)
创建硬连接
os.mkdir(path,[mode])
创建目录。mode的意义参见os.chmod(),默认是0777
os.makedirs(path,[mode])
和os.mkdir()类似,不过会先创建不存在的父目录。
os.readlink(path)
返回path这个符号链接所指向的路径
os.remove(path)
删除文件,不能用于删除目录
os.rmdir(path)
删除文件夹,不能用于删除文件
os.symlink(src,dst)
创建符号链接
shutil.rmtree(path[,ignore_errors[,onerror]])
删除文件夹
介绍了这么多,其实只要查一下os和shutil两个模块的文档就有了,呵呵。。真正编写shell脚本的时候还需要注意:
1.环境变量。python的环境变量保存在os.environ这个字典里,可以用普通字典的方法修改它,使用system启动其它程序的时候会自动被继承。比如:
os.environ[”fish”]=”nothing”
不过也要注意,环境变量的值只能是字符串。和shell有些不同的是,python没有export环境变量这个概念。为什么没有呢?因为python没有必要有:-)
2.os.path这个模块里包含了很多关于路径名处理的函数。在shell里路径名处理好像不是很重要,但是在python里经常需要用到。最常用的两个是分离和合并目录名和文件名:
os.path.split(path) -> (dirname,basename)
这个函数会把一个路径分离为两部分,比如:os.path.split(”/foo/bar.dat”)会返回(”/foo”,”bar.dat”)
os.path.join(dirname,basename)
这个函数会把目录名和文件名组合成一个完整的路径名,比如:os.path.join(”/foo”,”bar.dat”)会返回”/foo/bar.dat”。这个函数和os.path.split()刚好相反。
还有这些函数:
os.path.abspath(path)
把path转成绝对路径
os.path.expanduser(path)
把path中包含的”~”和”~user”转换成用户目录
os.path.expandvars(path)
根据环境变量的值替换path中包含的”$name”和”${name}”,比如环境变量FISH=nothing,那os.path.expandvars(”$FISH/abc”)会返回”nothing/abc”
os.path.normpath(path)
去掉path中包含的”.”和”..”
os.path.splitext(path)
把path分离成基本名和扩展名。比如:os.path.splitext(”/foo/bar.tar.bz2″)返回(’/foo/bar.tar’, ‘.bz2′)。要注意它和os.path.split()的区别
3.在os模块有一个很好用的函数叫os.stat()没有介绍,因为os.path模块里包含了一组和它具有同样功能的函数,但是名字更好记一点。
os.path.exists(path)
判断文件或者目录是否存在
os.path.isfile(path)
判断path所指向的是否是一个普通文件,而不是目录
os.path.isdir(path)
判断path所指向的是否是一个目录,而不是普通文件
os.path.islink(path)
判断path所指向的是否是一个符号链接
os.path.ismount(path)
判断path所指向的是否是一个挂接点(mount point)
os.path.getatime(path)
返回path所指向的文件或者目录的最后存取时间。
os.path.getmtime(path)
返回path所指向的文件或者目录的最后修改时间
os.path.getctime(path)
返回path所指向的文件的创建时间
os.path.getsize(path)
返回path所指向的文件的大小
4.应用python编写shell脚本经常要用到os,shutil,glob(正则表达式的文件名),tempfile(临时文件),pwd(操作/etc/passwd文件),grp(操作/etc/group文件),commands(取得一个命令的输出)。前面两个已经基本上介绍完了,后面几个很简单,看一下文档就可以了。
5.sys.argv是一个列表,保存了python程序的命令行参数。其中sys.argv[0]是程序本身的名字。
不能光说不练,接下来我们就编写一个用于复制文件的简单脚本。前两天叫我写脚本的同事有个几万个文件的目录,他想复制这些文件到其它的目录,又不能直接复制目录本身。他试了一下”cp src/* dest/”结果报了一个命令行太长的错误,让我帮他写一个脚本。操起python来:
import sys,os.path,shutil
for f in os.listdir(sys.argv[1]):
shutil.copy(os.path.join(sys.argv[1],f),sys.argv[2])
再试一下linuxapp版里的帖子——把一个文件夹下的所有文件重命名成10001~10999。可以这样写:
import os.path,sys
dirname=sys.argv[1]
i=10001
for f in os.listdir(dirname):
src=os.path.join(dirname,f)
if os.path.isdir(src):
continue
os.rename(src,str(i))
i+=1