Elasticsearch索引基本操作

在介绍索引之前,需要介绍下数据在Elasticsearch中的存储方式:数据是以文档的形式存储,文档本质上来讲是一个JSON对象,不同的是包括两部分:
- 元数据(metadata),用来描述文档的信息。如_index,_type,_id,_version,_source
- 实际存储对象的JSON格式,主要在_source中体现

一个典型的索引文档

{
  "_index" :   "website",
  "_type" :    "blog",
  "_id" :      "123",
  "_version" : 1,
  "found" :    true,
  "_source" :  {
      "title": "My first blog entry",
      "text":  "Just trying this out...",
      "date":  "2014/01/01"
  }
}

文档的元数据中有3个重要的属性:_index,_type,_idElasticsearch通过这三个属性来唯一确定一个文档。与关系型数据库相比,对应数据库(_index)和表(_type)

新增索引

索引的过程就是将文档存储的过程。前面已经说了,需要通过_index_type_id来唯一确定一个索引,其API如下

PUT /{index}/{type}/{id}  {JSON}

PUT /website/blog/123
{
  "title": "My first blog entry",
  "text":  "Just trying this out...",
  "date":  "2014/01/01"
}

返回

{
   "_index":    "website",
   "_type":     "blog",
   "_id":       "123",
   "_version":  1,
   "created":   true
}

每个文档中都有一个版本号(_version),而且在每次对该文档进行操作如更新、删除该版本号都会增加。上面的API指定了id,适用索引对象会自动生成id。如果不提供该编号,ES也会默认返回一个UUIDS的编号,同样API会发生变化:

POST /{index}/{type}/ {JSON}

请注意比较两种方式的Method和URL的区别

PUT  /{index}/{type}/{id}   {JSON}
POST /{index}/{type}/       {JSON}

检查索引是否存在

采用HEAD请求,同时返回HTTP状态码来确定

HEAD /website/blog/123

存在返回200,不存在404

HTTP/1.1 200 OK
Content-Type: text/plain; charset=UTF-8
Content-Length: 0

或者

HTTP/1.1 404 Not Found
Content-Type: text/plain; charset=UTF-8
Content-Length: 0

重建索引

和创建索引采用相同的API

PUT /{index}/{type}/{id}  {JSON}

不同的是,返回版本号发生变化,_create属性为false:

{
  "_index" :   "website",
  "_type" :    "blog",
  "_id" :      "123",
  "_version" : 2,
  "created":   false
}   

由此看来,如果创建用该API存在弊端: 不知道该id对应的文档是否已经创建。通常在创建的场景是如果已经存在那么就不要创建成功,而不是覆盖。可以通过参数来达到:

PUT /website/blog/123?op_type=create
PUT /website/blog/123/_create

这样如果实际创建成功返回201 Created否则返回409


Python中魔术方法call的使用

最近工作中使用到了Python的MagicMethod __call()__将他的一些使用方法记录下来。简单地讲,该函数可以将对象的实例作为一个函数来调用,并且可以传入参数等操作。下面见一个例子

class User(object):
    
    def __init__(self, name, age):
        self._name = name
        self._age = age
        
    def __call__(self):
        print "name: %s, age: %s" % (self._name, self._age)


def test_user_call():
    u = User('robin', 'aa')
    u()

在这个对象中实现了函数__call()__,该函数打印了私有变量。在函数test_user_call中对该对象的实例进行了调用:u()那么将打印如下结果

name: robin, age: aa

当然函数__call()__中可以传入参数,也可以调用其他函数

def __call__(self, msg):
    self.say_hello(msg)
    
def say_hello(self, msg):
    print "User[name=%s, age=%s] ...%s" % (self._name, self._age, msg)

那么这个时候调用只需要传入参数如u('Hello')即可:

User[name=robin, age=aa] ...Hello

以上都是一些通常的使用方法,当然我们并不满足这个层面的使用。再见下面的例子,结合高价函数的使用实现了一个简单的AOP功能

from decorator import decorator
class UserMethodDecrator(object):
    
    def __init__(self, name, age):
        self._name = name
        self._age = age
        
    def __call__(self, method):
        self.method = method
        return decorator(self.lookup, method)

    def lookup(self, method, *args, **kwargs):
        print "something happen before"
        print args
        print kwargs
        value = self.method(*args, **kwargs)
        print "something happen after"
        return value

@UserMethodDecrator('robin','aaa')        
def do_something(name):
    print "%s is doing something..."%(name,)
    return "HELLO"

if __name__ == '__main__':
    print do_something('LUCK')

在执行函数do_something的时候,使用了高阶函数UserMethodDecrator(这里是对象当作函数使用,当然函数也一样没问题,只是如果需要植入的业务太复杂可能需要好好封装)。在该函数执行前后进行了一些操作,并将结果返回。整个执行的结果如下

something happen before
('LUCK',)
{}
LUCK is doing something...
something happen after
HELLO

当然这里涉及了decorator的用法,见这里


Python入门指南(二)

数据结构

1、 序列

序列是Python中最基本的元素,列表中的每个元素下标从0开始

a = [1, 'abc', 2, 3]
b = [1, 2, [1, 2]]

索引与分片

a[0]       --> 1
a[4]       --> IndexError
a[1:3]     --> ['abc', 2]
a[::-1]    --> [3, 2, 'abc', 1]
a[0:20]    --> [1, 'abc', 2, 3]

追加元素
这里主要介绍3种方式

a = [1,'a',2,'c']

a.append(3)       --> a = [1, 'a', 2, 'c', 3]

a[len(a):] = [3]   --> a = [1, 'a', 2, 'c', 3, 3]
a[len(a):] = 'abc' --> a = [1, 'a', 2, 'c', 3, 3, 'a', 'b', 'c']

a.extend([1,2,3])
a.extend('abc')

a.insert(0,'a')
a.insert(16,'a')

注意对于a[len(a):] = [3]这种追加方式需要注意
- 不能使用a[len(a)+1]来指定下标,这样会抛出IndexError - 赋予的新值必须是可迭代的,如a[len(a):] = 3就会抛出TypeError,当然也支持多个元素的 a[len(a):] = [3, 4, 5]

对于extend参数也是接受可迭代的类型如列表[1,2,3]或者字符串abc。而对于insert(i, x)则是对指定的下标插入元素,注意下标可以大于列表的实际长度,但是也只是在列表后面追加,相应的长度增加1。

常用方法

a = [1, 2, 1, 'a', 'b']
len(a)  -->5 //长度
a.count(1) --> 2 //元素个数
a.count(3) --> 0 //元素不存在不报错返回0

a.index(1) --> 0 //元素在列表中的下标,多个返回第一个
a.index(3) -->   //元素不存在抛异常ValueError 

a.remove(1) --> 多个元素只删除第一个
a.remove(3) --> 不存在的元素抛出异常ValueError 

a.pop(1)    --> 删除指定下标的元素,返回该元素这里为2
a.pop(10)   --> 下标越界抛出IndexError

列表循环
对于列表的循环,我们通常采用这种方式

a = [1, 'a', 2, 'b']
for i in a:
    print i

或者

for i in range(len(a)):
    print a[i]

如果对列表只是一些简单的运算,可以考虑使用列表推导

[x * x for x in range(3)]-->[0, 1, 4]

也可以用if进行过滤

[x * x for x in range(3) if x % 2 == 0]-->[0, 4]

对多个for语句进行

[(x, y) for x in range(3) for y in range(3)]

有时我们希望将列表处理为一个枚举,可采用列表推导和函数enumerate([])

a = ['a', 'b', 'c', 'd', 'e']
b = [(index, item) for index, item in enumerate(a)]
outputs
[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd'), (4, 'e')]

这里有必要介绍下函数range(start,end,step)

range(start,end,step)
返回指定范围的列表,包括起止范围和步长

range(5)     --> [0, 1, 2, 3, 4]
range(0,5)   --> [0, 1, 2, 3, 4]
range(0,5,2) --> [0, 2, 4]

与str的区别
通过上面的讲解我们会发现列表和字符串存在一定的相似地方,如索引,下标操作等。如:

a = 'abc'
b = ['a', 'b', 'c']

a[0:1]
b[0:1]

a[::-1]
b[::-1]

a+"d"
b + ['d']

a*3
b*3

以上操作都很类似只是结果返回不一样。当然也有不同的地方,列表本质是可变长的,因此对列表的操作中如append, insert等就不适合字符串。当然两者也可以很方便转换

list(a) --> ['a', 'b', 'c']
''.join(b) --> abc

元组

不可变序列即为元组

(1,2,3)
('a','b','c')
(1,)

如果只有一个元素需要有末尾的逗号(,)注意比较

3 * (10 + 2)
3 * (10 + 2,)

可以通过函数tuple来实现列表和元组的转换

tuple('1bs3')
tuple([1, 2, 3, 4])
tuple((1, 2, 3,))
tuple([1, 2, 3, 4, [1, 2]])

注意上面最后元素是可以包含可变元素的。对其中可变元素[1,2]操作与普通的列表没什么差别

t = tuple([1, 2, 3, 4, [1, 2]])
t[4].insert(0, 0)
print t --> (1, 2, 3, 4, [0, 1, 2])

元组的循环可以使用上面的列表介绍的方法,但是由于元组不可变那么改变元组的方法是不可用的。会抛出异常AttributeError

集合(set)

与其他语言一样,是一个无序不可重复的元素集。因此不支持下标、分片等类序列操作的方法

1、 创建集合

s0 = {'h', 'e', 'l', 'l', 'o'}
s1 = set('hello')
s2 = set(['h', 'e', 'l', 'l', 'o'])

都返回set(['h', 'e', 'l', 'o'])与期望的一样不包括重复元素。同样作为集合元素也不能包含可变元素。像这样使用没问题set(((1, 2), (3,)))set([1, 2, 3, [1, 2]])则会抛出异常TypeError。虽然集合不能包含可变元素,但作为本身是可变的(注意与元组比较)。

s1.add('a')         --> set(['a', 'h', 'e', 'l', 'o'])
s1.add(['a','b'])   --> TypeError不支持可变元素

当然也有不可变的集合frozenset

>>> cities = frozenset(["Frankfurt", "Basel","Freiburg"])
>>> cities.add("Strasbourg")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'frozenset' object has no attribute 'add'

2、 常用方法 一张图来回忆下集合的常用操作

![sets_with_notations.png](C:/Users/dong/Desktop/sets_with_notations.png “”)

s0.remove('h')   -->s0: set(['e', 'l', 'o'])
s0.remove('hs')  -->不存在的元素抛出KeyError

s0.discard('h')  -->s0: set(['e', 'l', 'o'])
s0.discard('hs') -->不存在的元素不出错原来一致

s0.clear()       -->s0: set([])

交集(difference())

>>> x = {"a","b","c","d","e"}
>>> y = {"b","c"}
>>> z = {"c","d"}
>>> x.difference(y)
set(['a', 'e', 'd'])
>>> x.difference(y).difference(z)
set(['a', 'e'])

也可以更简单的处理方式

>>> x - y
set(['a', 'e', 'd'])
>>> x - y - z
set(['a', 'e'])

补集(difference_update)

x = {"a","b","c","d","e"}
y = {"b","c"}
x.difference_update(y)
x --> set(['a', 'e', 'd'])
z = x - y
z --> set(['a', 'e', 'd'])

或者

x = {"a", "b", "c", }
y = {"c", "d", "e"}
print x - y
print x | y
print x & y

outputs
set(['a', 'b'])
set(['a', 'c', 'b', 'e', 'd'])
set(['c'])

字典

字典与java中的map作用类似首先来看如何创建一个字典

d1 = {}
d2 = {'name':'python', 'version':'2.7.6'}
d3 = dict((['name', 'python'], ['version', 2.7]))
d4 = dict(name='python', version=2.7)
d5 = {}.fromkeys(('x', 'y'), -1)

fromkeys接收两个参数,前面作为key后面相应的为value,如果只有一个参数那么就是这样: d5 = {}.fromkeys(('x', 'y')) #{'x': None, 'y': None}

1、 遍历访问

d2 = {'name':'python', 'version':'2.7.6'}
for key in d2:
    print "key=%s, value=%s" % (key, d2[key])

for key in d2.iterkeys():
    print "key=%s, value=%s" % (key, d2[key])

for key in d2.keys():
    print "key=%s, value=%s" % (key, d2[key])

for key in iter(d2):
    print "key=%s, value=%s" % (key, d2[key])

for key, item in d2.items():
    print "key=%s, value=%s" % (key, item) 

以上的遍历是通过key,value或iter的访问。当然也支持对单个key的访问,如d2['name']或d2.get(name)对于后者也可以传入默认的value,如果key不存在时返回该值,此时该字典并不改变

d2.get('age', 10) >> 10
print d2          >> {'version': '2.7.6', 'name': 'python'}

2、 转换 常见的转换,可以将字典转换为列表(list)和迭代器(iterator)

dict={'name':'python', 'version':'2.7.6'}
#list
dict.items()  >>[('version', '2.7.6'), ('name', 'python')]
dict.keys()   >>['version', 'name']
dict.values() >>['2.7.6', 'python']
#iterator
dict.iteritems()
dict.iterkeys()
dict.itervalues()

3、 常见方法

  • dict.copy() #返回字典(浅复制)的一个副本:原字典发生改变不影响新的字典
  • dict.clear() #清空字典中所有元素:{}
  • dict1.has_key(“name”) #判断是否有键,返回True或False
  • dict.pop(key[, default]) #如果字典中key存在,删除并返回dic[key],如果key不存在,default没赋值将抛出KeyError

    dict = {‘name’:’python’, ‘version’:’2.7.6’} dict.pop(“name”) dict.pop(“name”,”python”) dict.pop(“name”)

  • dict.setdefault(key,default=None) 和方法set()相似,如果字典中不存在key 键,由dict[key]=default为它赋值
  • dict.update(dict2) 将字典dict2 的键-值对添加到字典dict

4、 创建进阶的字典

针对字典中key是列表,可这样操作dict = {'name':['python','java']}可用setdefault()来实现

dict = {};
dict.setdefault("name",[]).append("python");
dict.setdefault("name",[]).append("java");

或者用集合中的defaultdict

from collections import defaultdict
d = defaultdict(list);
d['name'].append('python')
d['name'].append('java')

上面使用的集合模块同样提供了有序集合OrderedDict

d = OrderedDict()
d['name'] = 'python'
d['age'] = 20

5、 字典的计算

  • 几种计算

      d = {
      	 'iphone5':5000,
      	 'g4':4000,
      	 'mi3':1999,
      	 'mi2s':1699
      	 }
      min(d)  //key按照字母顺序排列的最小
      min(d.values())  # values最小
        			
      min(zip(d.values(), d.keys()))  # 最小的序列,按照values排序。
      max(zip(d.values(), d.keys()))
      sorted(zip(d.values(), d.keys()))  # 按照values排序
      min(d, key=lambda k:d[k])#获取values最小的key
    

    上面用到了几个内置函数:zip,min,max,sorted:
    zip:接受可迭代的参数转换为元组或列表,如果长度不一样返回最小的列

    d = {
           'iphone5':5000,
           'g4':4000
          }
      	zip(d, d.values())
      	zip([1,2],[1,2,3])
    
  • 找到相同的

    a = {‘x’:1,’y’:2,’z’:3}
      b = {'a':4,'y':2,'z':4}
      set(a.keys()) & set(b.keys()) #{'y', 'z'}
      set(a.keys()) - set(b.keys()) #{'x'}
      set(a.items()) & set(b.items()) #{('y', 2)}
    
  • 根据条件生产新的字典

    a = {‘x’:1, ‘y’:2, ‘z’:3} {key:a[key] for key in set(a.keys()) - {‘z’}} #{‘x’: 1, ‘y’: 2}

    生成新的字典中将不包含key:z

参考


Python入门指南(一)

变量与数据类型

1、 变量的声明

作为弱类型语言,并不需要在变量声明时指定具体的类型。下面是一个声明的例子

a = 1
b = -100
c = 12.3
d = 341341325234234523433333333333338888888888888L
e = 1235324523452346 * 999999999999999999999999

my_name = 'python'
_my_name = "Life is short, I use Python"
_my_name2 = 'I\'m "OK"!'

a1,b1,c1 = (1,2,3)
a2,b2,c2 = [1,2,3]

对于批量的命名需要注意变量个数和序列个数保存一致,否则会抛出ValueError更多用法

a,b,c,d,e='hello'  ##必须为strings,files,iterators
_,age,_=['robin',10,('aaa',222,'ccc')] ##只关注age

或者

a,b,*c=[1,2,3,4,5] #c=[3,4,5]
a,*b,c=[1,2,3,4,5] #b=[2,3,4]

2、 bool

采用True, False表示

>>> True
True
>>> False
False
>>> 3 > 2
True
>>> 3 > 5
False

3、 布尔运算

True and True ##True
True and False ## False
False and False ## False

True or True ##True
True or False ##True
False or False ##False

not True ## False
not False ## True

数字与运算

1、 类型

  • int 整数,正整数或负整数 type(10)--> <type 'int'>
  • long 长整数,无限大小。可以l或L结尾 type(3452345234523452345234) --> <type 'long'>
  • float 浮点数 type(2.1)--><type 'float'>, type(32.3e18) --> <type 'float'>
  • complex 复数 type(1+2j)--><type 'complex'>

2、 算术运算

7 + 2  >> 9
7 - 2  >> 5
7 * 2  >> 14
7 / 2  >> 3
7 % 2  >> 1   

注意/是取整,可以用浮点数除法或from __future__ import division在python3中处理为一样

7.0 / 2  >> 3.5  
7 / 2.0  >> 3.5 
7. /2    >> 3.5

>>> from __future__ import division
>>> 7/2
3.5

再介绍一种运算//返回除法的整数部分

7 // 2   >> 3
7.0 // 2 >> 3.0

3、 比较、逻辑、布尔运算

常用比较运算(==, !=, >, <, >=, <=)
布尔运算 and, or, not

>>> 4>3 and 4>5
False
>>> 4>3 or 4>5
True
>>> not 4>3
False
>>> not 4>5
True

当然在python中比较并不只是这么简单,下面来个链式比较:

>>> x = 5
>>> 1 < x < 10
True
>>> x < 10 < x*10 < 100
True

这才是你希望的书写方式

4、 不存在整数溢出问题

>>>123456789870987654321122343445567678890098*123345566778999009987654333238766544
15227847719352756287004435258757627686452840919334677759119167301324455281312L

5、 神奇的*

>>> a = 'abc'
>>> a*4
'abcabcabcabc'
>>> b = (1,2)
>>> b*2
(1, 2, 1, 2)
>>> c = [1,2]
>>> c*3
[1, 2, 1, 2, 1, 2]

字符串

1、 申明

a0 = "Hello"
a1 = 'world'
//运算
a = a0 + a1
a*2

//转义或doc方式
b = "what's your name?"
c = 'what\'s your name'
d = """what's your name?"""

//unicode
>>> e = u'你好'
>>> e
u'\xc4\xe3\xba\xc3'

2、 字符串连接

a = "my name is %s, and %d years old"%('abc',1)
b = "my name is %s"%'abc'

3、 常用方法

a = 'Hello World'
len(a)
//大小写转换    
a.upper()
a.lower()
a.capitalize()
//下标与数量   
a.index('e')
a.count('l')
a.strip();
a.rstrip()

4、 分组、切片与其他

索引

name = 'Hello'
name[0]  --> H
name[-2] --> l
name[6]  -->IndexError

切片

name[0:3] --> 'Hel'
name[3:]  --> 'lo'
name[:]   --> 'Hello'
name[0:3:2] --> 'Hl' 步长为2
name[::-1]  --> 'olleH'

其他

list(name) --> ['H', 'e', 'l', 'l', 'o']返回用"".join([])
>>> 'a' in name
False
>>> 'a' not in name
True

函数

1、 函数定义

import random

def guess_a_number(x):
    a = random.randrange(1, 100)
    if x > a:
        print 'Larger'
    elif x == a:
        print 'equal'
    else:
        pass

几点说明 - 用关键字def申明,不使用{}用缩进代替,注意后面的冒号(:) - 注意参数个数和类型 - pass表示空实现

是否可以返回多个参数?答案是肯定的

def return_multi_param(x):
    return x + 1, x + 2
    
a,b = return_multi_param(1)

2、 可变参数

def foo(name, *args, **kwds):  
    print "name= ", name  
    print "args= ", args  
    print "kwds= ",kwds  

可简单理解*args为可变列表, **kwds为可变字典

foo("python", "10", "chengdu", a="aaaaaaaaa", b="vvvvvvvvvvvvv")
## outputs
name=  python  
args=  ('10', 'chengdu')  
kwds=  {'a': 'aaaaaaaaa', 'b': 'vvvvvvvvvvvvv'}   

3、 默认参数

def foo(name = 'abc'):  
    print "name= ", name 
## outputs
foo()         #name= abc
foo("lala")   #name= lala

注意如果默认参数是可变参数,那么可能导致和你预期不一样的结果

def foo(bar=[]):
    bar.append('bar')
    return bar
foo() >>['bar']
foo() >>['bar', 'bar']
foo() >>['bar', 'bar', 'bar']

这里调用三次后结果并不是预期那样:一个函数参数的默认值,仅仅在该函数定义的时候,被赋值一次。如此,只有当函数foo()第一次被定义的时候,才讲参数bar的默认值初始化到它的默认值(即一个空的列表)。当调用foo()的时候(不给参数bar),会继续使用bar最早初始化时的那个列表。 当然也有方式来避免这种方式

def foo(bar=[]):
    if not bar:
        bar = []
    bar.append('bar')
    return bar

更多信息见http://blog.jobbole.com/40088/

4、 函数作为参数传递

def get_text(name):
    return "lorem ipsum, {0} dolor sit amet".format(name)

def p_decorate(func):
    def func_wrapper(name):
        return "<p>{0}</p>".format(func(name))
    return func_wrapper

my_get_text = p_decorate(get_text)
## outputs
<p>lorem ipsum, John dolor sit amet</p>

这也可以用语法糖@来修饰

@p_decorate 
def get_text(name):
    return "lorem ipsum, {0} dolor sit amet".format(name)

这样就和普通的调用一样即可get_text("John")。这就是Python中的修饰器,更多信息可参考 - http://thecodeship.com/patterns/guide-to-python-function-decorators/ - http://simeonfranklin.com/blog/2012/jul/1/python-decorators-in-12-steps

几个内置函数

1、 lambda函数

lambda函数即匿名函数,没有函数名通常直接调用。考虑下面这个普通函数:

def squ(x):
    return x*x 

采用关键字lambda的实现

g = lambda x:x*x

2、 filter(function, sequence)

对sequence中的item依次执行function(item),将执行结果为True的item组成一个List/String/Tuple(取决于sequence的类型)返回

过滤序列中大于5小于10的数

def largerThan(x):
    return x > 5 and x < 10
    
if __name__ == '__main__':
    x = filter(largerThan, [1,6,4,8,11])
    print x

通常与lambda结合使用

filter(lambda i:i > 5 and i < 10, [1, 6, 4, 8, 11])

返回的结果与sequence的类型有关

filter(lambda i:i > 5 and i < 10, [1, 6, 4, 8, 11])
filter(lambda i:i > 5 and i < 10, (1, 6, 4, 8, 11,))
filter(lambda x: x != 'a' and x != 'c', 'abcdefadc')

3、 map(function, sequence)

对sequence中的item依次执行function(item),将执行结果组成一个List返回.与filter很类似,比较下两者的用法

filter(lambda i:i > 5 and i < 10, [1, 6, 4, 8, 11])-->[6, 8]
map(lambda i:i > 5 and i < 10, [1, 6, 4, 8, 11])-->[False, True, False, True, False]

当然也支持多种参数的输入

map(lambda i:i * i, [1, 6, 4, 8, 11])
map(lambda i: i + i, 'abcd')

可以看到map返回的序列和远序列的长度一致,只是类型会根据条件变化.同样支持多个序列

map(lambda x, y: x + y, range(8), range(8))

如果函数为None时,返回序列

map(None, range(3), range(3)) -->[(0, 0), (1, 1), (2, 2)]
map(None, range(2), range(3)) -->[(0, 0), (1, 1), (None, 2)]

4、 reduce(function, sequence, starting_value)

对sequence中的item顺序迭代调用function,如果有starting_value,还可以作为初始值调用.需要注意与上诉用法的区别,是对列表中的元素依次调用对应的function

reduce(lambda x, y:x + y, range(4)) --> 0+1+2+3
reduce(lambda x, y:x + y, ['a','b','c','d'])
reduce(lambda x, y:x + y, range(4), 10) --> 0+1+2+3+10

python requests

说明

Requests allow you to send HTTP/1.1 requests. You can add headers, form data, multipart files, and parameters with simple Python dictionaries, and access the response data in the same way. It’s powered by httplib and urllib3, but it does all the hard work and crazy hacks for you.

安装

多种方式安装,这里采用setuptools

git clone git://github.com/kennethreitz/requests.git
python setup.py install

快速入门

用Requests来创建一个HTTP请求很简单

import requests
r = requests.get('https://www.google.com/search?q=python')

这里创建了一个get请求,同时返回了一个Response对象.当然也可以传递参数的方式发送请求

queryparams = {'q':'python'}
r = requests.get('https://www.google.com/search', params=queryparams)

当然对于其他的HTTP请求类型也同样支持:

r = requests.post('https://www.somesite.com/post')
r = requests.put('https://www.somesite.com/put')
r = requests.delete('https://www.somesite.com/delete')
r = requests.head('https://www.somesite.com/head')
r = requests.options('https://www.somesite.com/options')

Response

Response表示了一个HTTP响应对象,可以通过text的方式返回r.text,也可以是二进制的内容r.content。同样也可以返回原生的内容r.raw.此时是requests.packages.urllib3.response.HTTPResponse对象。

r = requests.get('https://www.google.com/search?q=python',stream=True)
r.raw
>>> <requests.packages.urllib3.response.HTTPResponse object at 0x02367470>

with open('sss.html','wb') as fd:
	for chunk in r.iter_content(1000):
		fd.write(chunk)

上面的例子将请求的内容原生返回并写入了指定的文件。对应返回的内容,也可以指定编码方式r.encoding.下表展示了Response中的属性和方法

status_code返回的HTTP状态码
headers以字典的形式返回响应的Headers,如r.headers['content-type']
raw类文件的形式返回请求对象,需要设置请求参数stream=True
url请求后的最终url
encoding设置解析 r.text的编码
cookies服务端响应的Cookies,CookieJar对象返回,如r.cookies['cookie_name']
iter_content(chunk_size=1, decode_unicode=False)迭代原生的相应数据,见上例
iter_lines(chunk_size=ITER_CHUNK_SIZE, decode_unicode=None)iter_content类似,只是按行读取。在返回大量数据时减少内存占用
content()用byte返回响应的内容
text用unicode返回响应的内容,可通过r.encoding设置编码
json(**kwargs)json格式返回

再看Request

从前面知道提供了get,post,put等7个方法。其实这些都依赖于一个入口requests.request(method, url, **kwargs)来看看一个方法:

def get(url, **kwargs):
    kwargs.setdefault('allow_redirects', True)
    return request('get', url, **kwargs)

而在主要的入口方法request(method, url, **kwargs)中有如下的参数:

method必选HTTP方法,get,post,put,delete
url必选当前请求的URL
params可选当前请求的查询参数(get),字典或byte格式
data可选Form表单的请求参数(post),可以是字典、byte或文件
headers可选请求的headers
cookies可选请求的cookies
files可选迭代原生的相应数据,见上例
auth可选Auth tuple to enable Basic/Digest/Custom HTTP Auth
timeout可选超时设置
allow_redirects可选Boolean. Set to True if POST/PUT/DELETE redirect following is allowed
proxies可选Dictionary mapping protocol to the URL of the proxy
verify可选 if True, the SSL cert will be verified. A CA_BUNDLE path can also be provided
stream可选if False, the response content will be immediately downloaded
cert可选if String, path to ssl client cert file (.pem). If Tuple, (‘cert’, ‘key’) pair