Youmai の Blog


  • 首页

  • 分类

  • 关于

  • 归档

  • 标签

http判断缓存是否有效,为什么有了last-modified还需要etag

发表于 2015-02-10 | 分类于 http

If-Modified-Since:
作用: 把浏览器端缓存页面的最后修改时间发送到服务器去,服务器会把这个时间与服务器上实际文件的最后修改时间进行对比。如果时间一致,那么返回304,客户端就直接使用本地缓存文件。如果时间不一致,就会返回200和新的文件内容。客户端接到之后,会丢弃旧文件,把新文件缓存起来,并显示在浏览器中.

ETag:
是实体标签(Entity Tag)的缩写, 根据实体内容生成的一段hash字符串(类似于MD5或者SHA1之后的结果),可以标识资源的状态。 当资源发送改变时,ETag也随之发生变化。

ETag是Web服务端产生的,然后发给浏览器客户端。浏览器客户端是不用关心Etag是如何产生的。

问题是,我们知道last-modified可以用来判断浏览器的本地缓存是否有效,那为什么还要使用ETag呢? 主要是为了解决Last-Modified 无法解决的一些问题。

  1. 某些服务器不能精确得到文件的最后修改时间, 这样就无法通过最后修改时间来判断文件是否更新了。

  2. 某些文件的修改非常频繁,在秒以下的时间内进行修改. Last-Modified只能精确到秒。

  3. 一些文件的最后修改时间改变了,但是内容并未改变。 我们不希望客户端认为这个文件修改了。

为什么http协议是无连接的

发表于 2015-02-10 | 分类于 http

HTTP 协议本身是无连接的,虽然它使用了面向连接的 TCP 向上提供的服务。

TCP的面向连接是传输层的,而HTTP的无连接则是应用层的 。HTTP协议产生于互联网,因此服务器需要处理同时面向全世界数十万、上百万客户端的网页访问,但每个客户端(即浏览器)与服务器之间交换数据的间歇性较大(即传输具有突发性、瞬时性),并且网页浏览的联想性、发散性导致两次传送的数据关联性很低,如果按照上面的方式,则需要在服务器端开的进程和句柄数目都是不可接受的,大部分通道实际上会很空闲、无端占用资源。因此HTTP的设计者有意利用这种特点将协议设计为请求时建连接、请求完释放连接,以尽快将资源释放出来服务其他客户端。HTTP1.1现在设计为长连接,就是在一个连接内可以发生多个请求,避免了多次建立断开连接造成的消耗。

UDP尽管不象TCP那样占用资源,但它不保证数据的完整性、有序性,想像如果服务器返回的HTML错序了或者丢失一部分你将看到什么效果;并且UDP没有流量控制,而互联网的传输质量又不是很好,服务器随便仍出一大堆UDP包可能导致网络风暴,这是相当危险的。

归根结底,TCP的面向连接是传输层的,而HTTP的无连接则是应用层的。


无状态是指协议对于事务处理没有记忆能力,服务器不知道客户端是什么状态。从另一方面讲,打开一个服务器上的网页和你之前打开这个服务器上的网页之间没有任何联系

HTTP是一个无状态的面向连接的协议,无状态不代表HTTP不能保持TCP连接,更不能代表HTTP使用的是UDP协议(无连接)

从HTTP/1.1起,默认都开启了Keep-Alive,保持连接特性,简单地说,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接

Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间

TCP和UDP的工作过程

发表于 2015-02-05 | 分类于 TCP/IP

##TCP和UDP的工作过程
UDP的工作过程是简单的,仅仅将用户数据封装到一个IP数据报中发送到目的地而已,而不关注其他方面。

TCP却是一个极其复杂的协议,以下只是冰山一角

###建立连接的三次握手

  • 主动方发送(SYN J),进入SYN_SENT状态
  • 被动方收到(SYN J),并往回发送(SYN K, ACK J+1),进入SYN_RCVD状态
  • 主动方收到(SYN K, ACK J+1),并往回发送(ACK K+1),进入ESTABLISHED状态
  • 被动方收到(ACK K+1),也进入ESTABLISHED状态
    以上过程如下图所示:

establish

注意到在TCP三次握手的过程中,服务器有这么一条:

被动方收到(SYN J),并往回发送(SYN K, ACK J+1),进入SYN_RCVD状态

服务器进入SYN_RCVD状态(此时连接称为半开连接)后,应当期待再收到一个ACK。 如果超时未收到客户端的ACK,服务器将重发(SYN K, ACK J+1)。 于是,就有一种叫做SYN Flooding的攻击方式。 攻击者向服务器高速发送(SYN J)(而且可以将SYN分节中的IP地址设为随机数), 并且在随后收到服务器回复的(SYN K, ACK J+1)之后不再继续回复, 这使得服务器上存在很多的半开连接,这些半开连接一般情况下会持续63秒 (在Linux下,默认重试次数为5次,重试的间隔时间从1s开始每次都翻倍,5次的重试时间间隔为1s, 2s, 4s, 8s, 16s,第5次发出后还要等32s才知道第5次也超时了,所以,总共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 63s,TCP才会把断开这个连接)。 它的危害有两方面,一方面自然是占用了服务器的资源;另一方面是填充了半开连接的队列,使得合法的SYN分节无法排队。

根据SYN Flooding的攻击原理,它的防范主要有以下措施:

  1. 过滤掉最大嫌疑攻击的IP或IP段
  2. 将tcp_synack_retries设为0,表示回应第二个握手包(SYN K, ACK J+1)给客户端后,如果收不到ACK,不进行重试,加快回收“半开连接”。
  3. 将tcp_max_syn_backlog参数根据内存情况适当调大,该参数一般指的是维护的半开连接的队列的长度(不同OS不一样)。
  4. 设置tcp_abort_on_overflow选项,处理不过来就直接拒绝掉。

###断开连接的四次握手

  1. 主动方发送(FIN M),进入FIN_WAIT_1状态
  2. 被动方收到(FIN M),并往回发送(ACK M+1),进入CLOSE_WAIT状态
  3. 主动方收到(ACK M+1),进入FIN_WAIT_2状态
  4. 被动方发送(FIN N),进入LAST_ACK状态
  5. 主动方收到(FIN N),并往回发送(ACK N+1),进入TIME_WAIT状态
  6. 被动方收到(ACK N+1),进入CLOSED状态
  7. 主动方在TIME_WAIT状态中超时后,进入CLOSED状态

以上过程如下图所示:

close

其实就是2次,只不过TCP是全双工的,所以,发送方和接收方都需要FIN和ACK。 只不过,有一方是被动的,所以看上去就成了所谓的4次挥握手。

注意到最后有这么一条涉及到TIME_WAIT的状态

主动方在TIME_WAIT状态中超时后,进入CLOSED状态

需要经过一个TIME_WAIT超时的状态而不是直接进入CLOSED的原因有两个,一是确保有足够的时间让对端收到ACK,二是允许老的分节在网络中慢慢的消逝。

然而,如果系统中存在着大量的短链接,那么大量的TIME_WAIT状态就会成为系统的累赘。网上一些资料提到的tcp_tw_reuse和tcp_tw_recycle选项来解决这个问题,但是最好还是别乱用,好像coolshell中有提到过,可能会出很多诡异的问题。还可以调整tcp_max_tw_buckets,当并发的TIME_WAIT过多时,会直接把多的给destory掉,然后在日志里打一个警告。引用一句“其实,TIME_WAIT表示的是你主动断连接,所以,这就是所谓的"no zuo, no die"。

##TCP连接在“非正常”情况下的工作状况

###服务器进程终止

首先,服务器进程终止(收到SIGKILL信号)。作为进程中止处理的工作之一,该进程所有打开着的描述符将被关闭,这会导致向对端(客户端)发送(FIN N),而客户端则回复(ACK N+1),这就是TCP断开连接的前半部分。

然后,此时客户端收到(FIN N)并不意味着连接断开(虽然在这个例子中,确实断开了),只是意味着服务器不再向客户端发送数据了,客户端还可以继续向服务器发送数据。如果此时客户端还继续向服务器发送数据,服务器TCP将发现之前的打开该套接字的进程已终止,于是回应一个RST。客户端在收到这个RST之前的read操作将会返回EOF,在收到这个RST后的read操作会返回ECONNRESET错误,在收到这个RST后的write操作会使当前进程收到SIGPIPE信号。

以上过程如下图所示:

server_kill

###服务器主机崩溃

服务器主机崩溃的意思是,没有任何预兆,来不及在网络上发送任何消息,主机就无法工作了。这种情况等价于直接切断网络,或者通俗的说,可以直接拔掉网线来模拟这一情况。

这时,如果客户端向服务器发送数据,后调用read操作,TCP会一直等待服务器的ACK确认消息,并且不断的超时重传(按照Berkeley的实现,重传12次,共需9分钟),直到到达重传次数,返回ETIMEOUT错误。如果是由中间的路由器判定服务器主机不可达,响应“destination unreasonable”的ICMP消息,将返回EHOSTUNREACH和ENETUNREACH错误。

###服务器主机崩溃后重启

重启之后的服务器已经丢失了之前的TCP信息,所以即使收到了客户端发来的TCP数据,也会回复RST,往后的情况和“服务器主机崩溃”中提到的类似。

###服务器主机关机

Unix系统关机时,init进程通常会给其他进程发送SIGTERM信号,然后等待10s左右给仍在运行的进程发送SIGKILL信号。所以如果进程不捕获SIGTERM信号,则将由SIGKILL信号终止,和“服务器进程终止”中提到的类似。

##参考链接
TCP 的那些事儿(上)

TCP 的那些事儿(下)

十个我希望早点知道的python方法

发表于 2015-02-05 | 分类于 python

原文在这里

##在Python 2中使用Python 3式的输出
Python 2与Python 3不兼容,这让我不知道该选择哪个版本的Python。最终我选择了Python 2,因为当时许多我需要用的库都与Python 3不兼容。

但实际上,日常使用中最大的版本差异是输出(print)和除法行为。现在我在Python 2的代码中都用import from future来导入Python 3的输出和除法。现在我用到的几乎所有库都支持Python 3,因此会很快迁移到Python 3中。

mynumber = 5

print "Python 2:"
print "The number is %d" % (mynumber)
print mynumber / 2,
print mynumber // 2

from __future__ import print_function
from __future__ import division

print('nPython 3:')
print("The number is {}".format(mynumber))
print(mynumber / 2, end=' ')
print(mynumber // 2)

输出:
Python 2:
The number is 5
2 2

Python 3:
The number is 5
2.5 2

##enumerate(list)
很明显,迭代列表时,应该同时迭代其中的元素及其索引,但在很长一段时间内,我都尴尬的使用计数变量或切片。

mylist = ["It's", 'only', 'a', 'model']

for index, item in enumerate(mylist):
    print(index, item)

输出:
0 It's
1 only
2 a
3 model

##链式比较操作符
由于我以前使用的是静态语言(在这些语言中该用法有二义性),从来没有将两个比较操作符放在一个表达式中。在许多语言中,4 > 3 > 2会返回False,因为4 > 3的结果是布尔值,而True > 2将得出False。

mynumber = 3

if 4 > mynumber > 2:
    print("Chained comparison operators work! n" * 3)

输出:
Chained comparison operators work!
Chained comparison operators work!
Chained comparison operators work!

##collections.Counter
Python的集合库看上去是最好的。在计算需要集合中元素的个数时,StackOverflow找到的答案是创建有序字典,但我坚持使用一个代码片段来创建字典,计算结果中元素出现的频率。直到有一天,我发现可以用collections.deque。

from collections import Counter
from random import randrange
import pprint

mycounter = Counter()

for i in range(100):
    random_number = randrange(10)
    mycounter[random_number] += 1

for i in range(10):
    print(i, mycounter[i])

输出:
0 10
1 10
2 13
3 6
4 6
5 11
6 10
7 14
8 12
9 8

##字典推导

Python开发者的一个重要标志就是理解列表推导,但最终我发现字典推导也很有用,特别是在交换字典的键和值的时候。

my_phrase = ["No", "one", "expects", "the", "Spanish", "Inquisition"]
my_dict = {key: value for value, key in enumerate(my_phrase)}
print(my_dict)
reversed_dict = {value: key for key, value in my_dict.items()}
print(reversed_dict)

输出:
{'Inquisition': 5, 'No': 0, 'expects': 2, 'one': 1, 'Spanish': 4, 'the': 3}
{0: 'No', 1: 'one', 2: 'expects', 3: 'the', 4: 'Spanish', 5: 'Inquisition'}

##用subprocess执行shell命令

import subprocess
output = subprocess.check_output('dir', shell=True)
print(output)

注意,用os库完成这个特定命令比用subprocess更好。我只想有一个大家都熟悉的命令。同时,一般来说,在subprocess中使用shell=True参数是非常糟糕的主意,在这里使用这个参数仅仅是为了能在一个IPython notebook单元中放置命令的输出。不要自己使用这个参数!下面是用os模块执行shell命令

import os
os.system('dir')

注意,这里的dir命令会立刻在shell中输出,不能够保存到文件(变量)中,如果想要保存到变量中,可以使用popen:

import os
output = os.popen('dir')
print output.read()

##字典的.get()和.iteritems()方法
字典的get()方法可以设置默认值,当用get()查找的键不存在时,返回方法中的默认值参数是很有用的。与列表中的enumerate()相同,可以用键值元组迭代字典中的元素。
my_dict = {‘name’: ‘Lancelot’, ‘quest’: ‘Holy Grail’, ‘favourite_color’: ‘blue’}

print(my_dict.get('airspeed velocity of an unladen swallow', 'African or European?n'))

for key, value in my_dict.iteritems():
    print(key, value, sep=": ")

输出:
African or European?

quest: Holy Grail
name: Lancelot
favourite_color: blue

如果要用for迭代输出字典,就要用到字典的iteritems()方法,这个方法在python3.x中已经废除了,取代的是items()方法,items()方法在python2.x中也存在

##用于交换元素的元组解包
在VB中,每当需要交换两个变量时,都要用要一个愚蠢的临时变量:c = a; a = b; b = c

a = 'Spam'
b = 'Eggs'

print(a, b)

a, b = b, a
print(a, b)

输出:
Spam Eggs
Eggs Spam

##内省工具Introspection tools
我知道dir()方法,我本以为help()方法和IPython中的?魔法命令是一样的,但help()的功能更强大。

my_dict = {'That': 'an ex-parrot!'}

help(my_dict)

输出:
Help on dict object:

class dict(object)
 | dict() -> new empty dictionary
 | dict(mapping) -> new dictionary initialized from a mapping object's
 | (key, value) pairs
 | dict(iterable) -> new dictionary initialized as if via:
 | d = {}
 | for k, v in iterable:
 | d[k] = v
 | dict(**kwargs) -> new dictionary initialized with the name=value pairs
 | in the keyword argument list. For example: dict(one=1, two=2)
 |
 | Methods defined here:
 |
 | __cmp__(...)
 | x.__cmp__(y) <==> cmp(x,y)
 |
 | __contains__(...)
 | D.__contains__(k) -> True if D has a key k, else False
 |
 | __delitem__(...)
 | x.__delitem__(y) <==> del x[y]
 |
 | __eq__(...)
 | x.__eq__(y) <==> x==y
 |

[TRUNCATED FOR SPACE]

 | 
 | update(...)
 | D.update([E, ]**F) -> None. Update D from dict/iterable E and F.
 | If E present and has a .keys() method, does: for k in E: D[k] = E[k]
 | If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v
 | In either case, this is followed by: for k in F: D[k] = F[k]
 |
 | values(...)
 | D.values() -> list of D's values
 |
 | viewitems(...)
 | D.viewitems() -> a set-like object providing a view on D's items
 |
 | viewkeys(...)
 | D.viewkeys() -> a set-like object providing a view on D's keys
 |
 | viewvalues(...)
 | D.viewvalues() -> an object providing a view on D's values
 |
 | ----------------------------------------------------------------------
 | Data and other attributes defined here:
 |
 | __hash__ = None
 |
 | __new__ =
 | T.__new__(S, ...) -> a new object with type S, a subtype of T

##PEP-8兼容的字符串连接
PEP8是Python编码样式指南。撇开其他的不看,PEP8要求每行不能超过80个字符,超过的部分要换行并缩进。

可以通过反斜杠、带逗号“,”的圆括号“()”、或者额外的加号“+”来完成换行。但对于多行字符串,这些解决方案都不够优雅。Python有个多行字符串记号,即三个引号,但这样无法换行后保持缩进。

还有一个方法,那就是不带逗号的圆括号。我不知道为什么这种方式能工作,但能用就行。

my_long_text = ("We are no longer the knights who say Ni! "
                "We are now the knights who say ekki-ekki-"
                "ekki-p'tang-zoom-boing-z'nourrwringmm!")
print(my_long_text)

输出:
We are no longer the knights who say Ni! We are now the knights who say ekki-ekki-ekki-p'tang-zoom-boing-z'nourrwringmm!

scrapy的一些知识点

发表于 2015-02-04 | 分类于 scrapy
  • ###运行spider,将爬取到的信息用JSON形式存到scraped_data.json文件中

    scrapy crawl my_spider -o scraped_data.json
    
  • ###在scrapy shell里面打印返回的html

    response.body
    
  • ###创建一个Spider,我们必须继承scrapy.Spider类(或者别的爬虫类),并且定义以下3个属性

    • name:用于区别Spider。 该名字必须是唯一的,您不可以为不同的Spider设定相同的名字。
    • start_urls:包含了Spider在启动时进行爬取的url列表。 因此,第一个被获取到的页面将是其中之一(每个url都会被爬取)。 后续的URL则从初始的URL获取到的数据中提取。
    • parse() 是spider的一个方法。 被调用时,每个初始URL完成下载后生成的 Response 对象将会作为唯一的参数传递给该函数。 该方法负责解析返回的数据(response data),提取数据(生成item)以及生成需要进一步处理的URL的 Request 对象。parse只是初始url的回调函数,可以定义别的回调函数来处理后序的链接。
  • ###scrapy shell

    scrapy shell "http://www.dmoz.org/Computers/Programming/Languages/Python/Books/"
    

    当您在终端运行Scrapy时,请一定记得给url地址加上引号,否则包含参数的url(例如 & 字符)会导致Scrapy运行失败。

  • ###Item 对象是自定义的python字典。 您可以使用标准的字典语法来获取到其每个字段的值。(字段即是我们之前用Field赋值的属性):

    >>> item = DmozItem()
    >>> item['title'] = 'Example title'
    >>> item['title']
    'Example title'
    
  • ###scrapy.cfg 存放的目录被认为是 项目的根目录 。该文件中包含python模块名的字段定义了项目的设置

  • Link Extractors 是用于从网页(scrapy.http.Response )中抽取会被follow的链接的对象。

    scrapy.contrib.linkextractors import LinkExtractor
    
  • ###log服务
    Scrapy提供了log功能。您可以通过 scrapy.log 模块使用。当前底层实现使用了 Twisted logging ,不过可能在之后会有所变化。

  • ###使用FormRequest.from_response()方法模拟用户登录
    通常网站通过 实现对某些表单字段(如数据或是登录界面中的认证令牌等)的预填充。 使用Scrapy抓取网页时,如果想要预填充或重写像用户名、用户密码这些表单字段, 可以使用 FormRequest.from_response() 方法实现。下面是使用这种方法的爬虫例子:

    import scrapy
    
    class LoginSpider(scrapy.Spider):
        name = 'example.com'
        start_urls = ['http://www.example.com/users/login.php']
    
        def start_requests(self):
            return scrapy.FormRequest.from_response(
                "url",
                formdata={'username': 'john', 'password': 'secret'},
                callback=self.after_login
            )
    
        def after_login(self, response):
            # check login succeed before going on
            if "authentication failed" in response.body:
                self.log("Login failed", level=log.ERROR)
                return
    
            # continue scraping with authenticated session...
    
  • ###Scrapy是以广度优先还是深度优先进行爬取的呢
    默认情况下,Scrapy使用 LIFO 队列来存储等待的请求。简单的说,就是 深度优先顺序 。深度优先对大多数情况下是更方便的。如果您想以 广度优先顺序 进行爬取,你可以设置以下的设定:

    DEPTH_PRIORITY = 1
    SCHEDULER_DISK_QUEUE = 'scrapy.squeue.PickleFifoDiskQueue'
    SCHEDULER_MEMORY_QUEUE = 'scrapy.squeue.FifoMemoryQueue'
    
  • ###避免被禁止(ban)
    有些网站实现了特定的机制,以一定规则来避免被爬虫爬取。 与这些规则打交道并不容易,需要技巧,有时候也需要些特别的基础。 如果有疑问请考虑联系 商业支持 。

    下面是些处理这些站点的建议(tips):

    • 使用user agent池,轮流选择之一来作为user agent。池中包含常见的浏览器的user agent(google一下一大堆)
    • 禁止cookies(参考 COOKIES_ENABLED),有些站点会使用cookies来发现爬虫的轨迹。
    • 设置下载延迟(2或更高)。参考 DOWNLOAD_DELAY 设置。
    • 如果可行,使用 Google cache 来爬取数据,而不是直接访问站点。
    • 使用IP池。例如免费的 Tor项目 或付费服务(ProxyMesh)。
    • 使用高度分布式的下载器(downloader)来绕过禁止(ban),您就只需要专注分析处理页面。这样的例子有: Crawlera
  • ###禁止重试
    对失败的HTTP请求进行重试会减慢爬取的效率,尤其是当站点响应很慢(甚至失败)时, 访问这样的站点会造成超时并重试多次。这是不必要的,同时也占用了爬虫爬取其他站点的能力。

    禁止重试:

    RETRY_ENABLED = False
    
  • ###使用相对Xpaths

如果你使用嵌套的选择器,并使用起始为 / 的XPath,那么该XPath将对文档使用绝对路径,而且对于你调用的 Selector 不是相对路径。

比如,假设你想提取在 <div>元素中的所有 <p>元素。首先,你将先得到所有的 <div> 元素:

>>> divs = response.xpath('//div')

开始时,你可能会尝试使用下面的错误的方法,因为它其实是从整篇文档中,而不仅仅是从那些 <div> 元素内部提取所有的 <p>元素:

>>> for p in divs.xpath('//p'):  # this is wrong - gets all <p> from the whole document
...     print p.extract()

下面是比较合适的处理方法(注意 .//p XPath的点前缀):

>>> for p in divs.xpath('.//p'):  # extracts all <p> inside
...     print p.extract()

另一种常见的情况将是提取所有直系 <p>的结果:

>>> for p in divs.xpath('p'):
...     print p.extract()

每天一个python小程序(7)--敏感词

发表于 2015-02-03 | 分类于 python

敏感词文本文件 filtered_words.txt,里面的内容为以下内容,当用户输入敏感词语时,则打印出 Freedom,否则打印出 Human Rights。

love

sex

jiangge

bitch

代码中有两种从txt文件中获取敏感词的方法,有一种被注释掉了,因为用这种方法获得的list最后会有一个空的字符串,导致下面find的时候一直会匹配成功,感兴趣的同学可以去掉注释试一下(同时需要注释掉注释下面的4行)

#!/usr/bin/python
#encoding=utf-8

def get_sensitive_word(filepath):
    f = open(filepath, 'r')
    #data = f.read()
    #filt = data.split('\n') 这个方法得到的序列中最后有一个空的字符串,导致匹配一直成功
    words = []
    for word in f:
        #strip删除字符序列,这里是删除word中的'\n'
        words.append(word.strip('\n'))
    f.close()
    return words

def filter_word(words):
    flag = False
    text = raw_input('please input: ')
    for word in words:
        if text.find(word) != -1:
            flag = True

    if flag:
        print 'freedom'
    else:
        print 'human rights'

if __name__ == '__main__':
    filepath = 'filtered_words.txt'
    words = get_sensitive_word(filepath)
    filter_word(words)

完整项目见这里

每天一个python小程序(6)--统计代码行数

发表于 2015-02-02 | 分类于 python

有个目录,里面是你自己写过的程序,统计一下你写过多少行代码。包括空行和注释,但是要分别列出来。

#!/usr/bin/python
#encoding=utf-8

import re
import os

total = 0
blank = 0
comment = 0

#要想使用全局变量,必须要用global来申明
def count_code_lines(filename):
    global total
    global blank
    global comment
    f = open(filename)
    lines = f.readlines()
    total += len(lines)

    pattern_blank = re.compile(r'^\s*$')
    pattern_comment = re.compile(r'^\s*\#+')

    for line in lines:
        if pattern_blank.match(line):
            blank += 1
        elif pattern_comment.match(line):
            comment += 1


def walk_dir(file_path):
    global total
    global blank
    global comment
    #os.walk返回值为3元tupple,第一个是起始路径,第二个是起始下的文件夹,第三个是起始路径下的文件
    for root, dirs, files in os.walk(file_path):
        for file in files:
            if file.lower().endswith('.py'):
                full_path = os.path.join(root, file)
                count_code_lines(full_path)

#在main函数里不要用global来申明全局变量
if __name__ == '__main__':
    walk_dir('../')
    print 'total line is %d, blank is %d, comment is %d' % (total, blank, comment)

每天一个python小程序(5)--找出最重要的单词

发表于 2015-02-02 | 分类于 python

你有一个目录,放了你一个月的日记,都是 txt,为了避免分词的问题,假设内容都是英文,请统计出你认为每篇日记最重要的词。

#!/usr/bin/env python
#encoding=utf-8

import collections
import re
import os

#这些单词在所有文章中都会频繁出现,不能看做最重要的词
useless_words = ('the', 'a', 'an', 'and', 'by', 'of', 'in', 'on', 'is', 'to')

'''


def get_important_word(file):
    f = open(file)
    word_counter = collections.Counter()

    for line in f:
        words = re.findall('\w+', line.lower())
        word_counter.update(words)

    f.close()
    #按次序取出出现次序最多的词,和useless_words中的单词比较,如果在use_less表中,就取次一个高频的单词
    most_important_word = word_counter.most_common(1)[0][0]
    count = 2
    while (most_important_word in useless_words):
        most_important_word = word_counter.most_common(count)[count-1][0]
        count += 1
    num = word_counter.most_common(count)[count-1][1]
    print 'the most important word in %s is %s, it appears %d times' % (file, most_important_word, num)


if __name__ == '__main__':
    filepath = 'diary'
    for dirpath, dirname, dirfiles in os.walk(filepath):
        for file in dirfiles:
            abspath = os.path.join(dirpath, file)
            if os.path.isfile(abspath):
                get_important_word(abspath)

每天一个python小程序(4)--统计单词频数

发表于 2015-02-02 | 分类于 python

任一个英文的纯文本文件,统计其中每个单词出现的频数。

这一题使用collections模块会非常简单

#!/usr/bin/python
#encoding=utf-8

import collections
import re

#增加统计单词总数和最长行
def count_word(file_name):
    f = open(file_name)

    word_counter = collections.Counter()

    #文件对象使用迭代器
    for line in f:
        words = re.findall('\w+', line.lower())
        word_counter.update(words)

    #迭代器对象只能遍历一次,欲再次遍历需要重现构建迭代器对象
    f = open(file_name)
    #计算单词总数 p206
    word_sum = len([word for line in f for word in line.split()])


    #最长行
    f = open(file_name)
    longest = max(len(line.split()) for line in f)

    print "the longest line's length is %d" % longest
    f.close()

    return word_counter, word_sum

if __name__ == '__main__':
    world_counter, word_sum = count_word('test.txt')
    print 'there are %d words in this article' % word_sum
    #most_common打印出现次数最多的单词,这里most_common(3)表示出现次数最多的3个单词
    for key,count in world_counter.most_common(3):
        print '%s: %d' % (key, count)

代码在这里

每天一个python小程序(3)--保存激活码

发表于 2015-02-02 | 分类于 python

将这一题生成的 200 个激活码(或者优惠券)保存到 mongodb 非关系型数据库中

python连接mongodb需要用到pymongo模块,对数据库操作和单独操作mongodb是一样的

#usr/bin/python
#encoding=utf-8

import pymongo
import uuid
#连接到mongodb,这里的mongodb在本地运行,默认监听27017端口
connection = pymongo.Connection('localhost', 27017)
#连接到指定数据库,不存在即创建
db = connection.python_study
#指定collection,不存在即创建
collection = db.uuid

for i in range(200):
    #向指定的collection中插入数据
    collection.insert({"id": i, "value": uuid.uuid4()})

代码可以在这里下载

1…131415…18
You Wangqiu

You Wangqiu

世之奇伟、瑰怪,非常之观,常在于险远,而人之所罕至焉,故非有志者不能至也

171 日志
21 分类
24 标签
GitHub 知乎 E-Mail
© 2018 You Wangqiu
由 Hexo 强力驱动
主题 - NexT.Muse