python之Dynamodb: scan扫描全表,分页获取所有结果

Dynamodb

Amazon DynamoDB 是一种完全托管的 NoSQL 数据库服务,提供快速而可预测的性能,能够实现无缝扩展。使用 DynamoDB,您可以免除操作和扩展分布式数据库的管理工作负担,因而无需担心硬件预置、设置和配置、复制、软件修补或集群扩展等问题。

Dynamodb是亚马逊AWS推出的一款数据库存储产品。如下所示,亚马逊AWS的产品还真是丰富无边。

image.png

最近由于公司的技术调整,有些数据库迁移到亚马逊了,同时还有S3服务也启用了。
我也只好学了一些相关的知识,下面,我将自己常用到的一些东西记录一下。

用python连接Dynamodb的服务

你可以使用 AWS 管理控制台与 DynamoDB 交互,也可以通过编程,但是编程访问需要获取相应的秘钥许可。

Boto is the Amazon Web Services (AWS) SDK for Python, which allows Python developers to write software that makes use of Amazon services like S3 and EC2. Boto provides an easy to use, object-oriented API as well as low-level direct service access.

由上可知,python编程是使用boto包来操纵dynamodb的,如今最新版本是 Boto3.

  • 安装boto3
    pip install boto3
  • 导入boto3
    import boto3
  • 获取aws的接入ID和秘钥
    每一个用户名都可以申请唯一的id和秘钥,用于编程访问dynamodb,具体请参考:指南
    一般都是下载到csv文件,然后在编程的时候引入即可,形式如下:
self.AWS_ACCESS_ID = 'AKIAI6xxxxxxxOGDQ7A'
self.AWS_ACCESS_KEY = 'hQ7xxxxLOD2Jxxxxxxx5148TM0mxxxxMR'

由于这是私钥,只要知道上面的这两串内容,便可在互联网上任何位置访问目的dynamodb数据库,所以不可轻易示人,特别是带有write权限的账号。中间已被我用x代替一部分。

创建服务
import boto3
from boto3 import Session
from boto3.dynamodb.conditions import Attr, Key

class DynamoDBService:
    def __init__(self):
        self.this_day = datetime.date.today()
        # 这两个key像是账号和密码一般,需要在后台申请导出,唯一的
        self.AWS_ACCESS_ID = 'AKIxxxAI6KxxxxxxxFGOxxxxDQ7A'
        self.AWS_ACCESS_KEY = 'hQ7FxxxxxxxxxxxxxuMcxxJxrxxxxxxm53UoxxMR'

    def get_service(self, table_name):
        """将service单独拿出来的目的,我为了初始化类的时候不会那么慢"""
        client = boto3.client('dynamodb', region_name='us-west-2',
                              aws_access_key_id=self.AWS_ACCESS_ID,
                              aws_secret_access_key=self.AWS_ACCESS_KEY)
        dynamodb = boto3.resource('dynamodb', region_name='us-west-2',
                                  aws_access_key_id=self.AWS_ACCESS_ID,
                                  aws_secret_access_key=self.AWS_ACCESS_KEY)
        # 通过dynamodb服务获取目标table的操作对象
        table_handle = dynamodb.Table(table_name)
        return table_handle

你只需要将秘钥换成自己的,然后像这样:

table_name="h5_visit_Info"
table_handle_h5_visit_info = self.get_service(table_name)

便获取了目标数据表的控制权,接下来就可以调用相应的方法了。

query
 def operate_table(self, table_name="h5_visit_Info"):
        # 通过dynamodb服务获取目标table的操作对象
        table_handle_h5_visit_info = self.get_service(table_name)

        """查询,根据某一key(column)查询"""
        response = table_handle_h5_visit_info.query(
            KeyConditionExpression=Key('uid').eq('f3d61094c65a42489d0e54d4c30b7e6f')
        )
        print response
        # response中包含了很多内容,response本身是个json字符串,其Items键的内容才是table中的内容
        print type(response)
        items = response['Items']
        print items
        print json.dumps(items)

上面是一个query方法,用来查询特定的记录,其参数很复杂,但常用的也就是条件表达式KeyConditionExpression,需要引入key:
from boto3.dynamodb.conditions import Key
很显然,Key('uid').eq('f3d61094c65a42489d0e54d4c30b7e6f')的意思便是uid='f3d61094c65a42489d0e54d4c30b7e6f',类似的表达还有gt、lt、gtq、between等,详见:Key

scan

下面详细说一下scan,scan函数就是扫描,将扫描整个表:

response = table_handle.scan(
    FilterExpression=Attr('first_name').begins_with('J') & Attr('account_type').eq('super_user')
)
items = response['Items']
print(items)

如上就是一个简单的用法,但是,aws每次只允许获取1M上限的结果,如果你的条件产生的结果超出了1M,那么你就得分页。同样,分页的参数也是放在scan里面的:ExclusiveStartKey
我要扫描的表名称叫做:h5_visit_Info
我想要获取整张表,

    def get_table_info(self, table_name="h5_visit_Info"):
        """
        对表进行分页扫描,这里尝试的是对visit_info表进行扫描
        """
        # 通过dynamodb服务获取目标table的操作对象
        table_handle = self.get_service(table_name)

        # 查询表的内容,利用scan分页,先将所有数据拿出来再说
        table_info = DataFrame()
        # 这个值是在分页查询的时候,用来记录页面的最后一个主键的下一个,以方便下一个页面的开启
        last_evaluated_key = None
        i = 0
        while True:
            # 刚开始,不需要传入startkey
            if last_evaluated_key is None:
                response = table_handle.scan()
            else:
                # 构建分页的起点,传入下一页面的起点,这是由主键来控制的,last_evaluated_key的值就是本表中的uid
                try:
                    response = table_handle.scan(ExclusiveStartKey=last_evaluated_key)
                except:
                    break
            # response 有一个标准的json格式,包含了这次scan结果的各种信息
            # print type(response) #字典

            # 获取结果中的内容,其中键Items的值就是表的内容
            # 首先提取info,要对info里面的内容进行进一步解析出来,info里面的也是一个json格式,所以可以直接转为json
            info = [json.loads(item['info']) for item in response['Items']]
            info = DataFrame(info)
            # 从新设置index,方便后面合并contact
            info.index = range(0, len(info))
            # print info.head(5)

            # 获取基本信息,包含创建时间和uid
            basic = [[str(item['createTime']), item['uid']] for item in response['Items']]
            # 这里好绕,本来以为item是一个字典的,结果没法用字典的函数来执行,只能如此了
            basic = DataFrame(basic)
            basic.columns = [['createTime', 'uid']]
            basic.index = range(0, len(basic))
            # print basic.head(5)

            #  合并表
            basic_info = pd.concat([info, basic], axis=1)
            print len(basic_info)
            # 下面是将Unix时间戳转化成日期,目标lvt是以毫秒来计算的,所以要先除以1000才能带入函数utcfromtimestamp
            basic_info['lvt'] = basic_info['lvt'].map(
                lambda time_stamp: datetime.datetime.utcfromtimestamp(long(time_stamp) / 1000).strftime("%Y-%m-%d"))
            basic_info['createTime'] = basic_info['createTime'].map(
                lambda time_stamp: datetime.datetime.utcfromtimestamp(long(time_stamp) / 1000).strftime("%Y-%m-%d"))
            # 这里其实有数据需要清洗的,比如vt里面有空值,要将vt转化成数字才能筛选出vt大于等于2的
            # basic_info['vt'] = basic_info['vt'].astype(long)
            basic_info = basic_info[basic_info['vt'] >= 2]
            print len(basic_info)
            print "----" * 30, i
            i += 1
            table_info = table_info.append(basic_info)
            # 判断有没有这个LastEvaluatedKey,如果有,那么还有页面没有拉取玩,将这个值带入request的ExclusiveStartKey中,继续读取页面
            if response.has_key('LastEvaluatedKey'):
                last_evaluated_key = response['LastEvaluatedKey']
                print last_evaluated_key
                continue
            else:
                break
        print len(table_info)
        table_info.to_excel('visit_info_{}.xlsx'.format(self.this_day))
        pass

当然,上面的程序还有存在bug,待我下一次更新

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343