您的位置:首页 > 编程语言

pyspider 爬虫学习-代码精读:database:basedb.py

2017-09-13 00:00 253 查看
摘要: pyspider 爬虫学习-代码精读:basedb.py,数据处理基础文件

文件路径:pyspider>database>basedb.py

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# vim: set et sw=4 ts=4 sts=4 ff=unix fenc=utf8:
# Author: Binux<i@binux.com>
#         http://binux.me # Created on 2012-08-30 17:43:49

# 通过__future__将新版本的特性导入进来,这样做的目的是让旧的版本也能使用新版本的特性
# unicode_literals,这个模块的作用就是把你当前模块所有的字符串(string literals)转为unicode
# division(精确除法),当我们没有在程序中导入该特征时,"/"操作符执行的是截断除法(Truncating Division),当我们导入精确除法之后,"/"执行的是精确除法
# absolute_import 绝对导入,具体用法还不是很理解
from __future__ import unicode_literals, division, absolute_import
# 导入日志模块
import logging

'''
Python的logging模块提供了通用的日志系统,可以方便第三方模块或者是应用使用。这个模块提供不同的日志级别,并可以采用不同的方式记录日志,比如文件,HTTP GET/POST,SMTP,Socket等,甚至可以自己实现具体的日志记录方式。
logging模块与log4j的机制是一样的,只是具体的实现细节不同。模块提供logger,handler,filter,formatter。
logger:提供日志接口,供应用代码使用。logger最长用的操作有两类:配置和发送日志消息。可以通过logging.getLogger(name)获取logger对象,如果不指定name则返回root对象,多次使用相同的name调用getLogger方法返回同一个logger对象。
handler:将日志记录(log record)发送到合适的目的地(destination),比如文件,socket等。一个logger对象可以通过addHandler方法添加0到多个handler,每个handler又可以定义不同日志级别,以实现日志分级过滤显示。
filter:提供一种优雅的方式决定一个日志记录是否发送到handler。
formatter:指定日志记录输出的具体格式。formatter的构造方法需要两个参数:消息的格式字符串和日期字符串,这两个参数都是可选的。
与log4j类似,logger,handler和日志消息的调用可以有具体的日志级别(Level),只有在日志消息的级别大于logger和handler的级别。
'''
logger = logging.getLogger('database.basedb')
# Six提供了Python 2和Python 3的兼容库,itervalues遍历字典,已经被python3支持
from six import itervalues

# 创建BaseDB类
class BaseDB:
'''
BaseDB

dbcur should be overwirte
'''
# 定义表名,默认值为:None,表示__tablename__初始化为一个空对象 ,参考文档:http://blog.csdn.net/Li_Danny/article/details/49815761
__tablename__ = None
# placeholder 翻译为“占位符”的意思,初始化为字符型
placeholder = '%s'
# 最大限制,默认-1,暂时不明白是什么意思
maxlimit = -1

# staticmethod静态函数,调用时不用隐试传入类名,此方法作用为将传入的字符串转换一遍,参考文档:http://www.cnblogs.com/hopeworld/archive/2011/08/16/2140145.html
# %s用str()方法处理对象,escape翻译为:逃避,避开
# escape 是给string添加``符号。比如你创建的table或者column里有空白字符时。
@staticmethod
def escape(string):
return '`%s`' % string

# @property装饰器就是负责把一个方法变成属性调用的,参考文档:http://www.cnblogs.com/ghgyj/p/3997585.html
# 这里dbcur定义为一个只读属性
# dbcur这个函数抛出未实现这个异常,目的是为了充当接口,由子类去实现。Python里面没有接口这个概念,所以定义接口时,可以采用这种方式。DbBase只付责构建sql语句,具体使用何种数据库由子类实现,好处是可以适配不同的数据库。
@property
def dbcur(self):
# 引发NotImplementedError异常
raise NotImplementedError

# sql语句执行并返回结果对象
def _execute(self, sql_query, values=[]):
# 获取实例dbcur对象
dbcur = self.dbcur
# 调用dbcur对象的execute方法,values我猜测是在dbcur子类中定义的方法,用来执行sql语句并返回值
dbcur.execute(sql_query, values)
# 返回执行execute方法后的dbcur对象
return dbcur

'''
功能:查询
参数:
tablename:表名
what:查询内容
where:查询条件
where_values:暂时不知道是干什么的,推测是存储查询结果的参数
offset:查询偏移数(起始值)
limit:查询条数
'''

def _select(self, tablename=None, what="*", where="", where_values=[], offset=0, limit=None):
# 将表名加上引号,防止有空格时出错
tablename = self.escape(tablename or self.__tablename__)
# 判断what是否为列表或者what是否为元组或者what为None
if isinstance(what, list) or isinstance(what, tuple) or what is None:
# 将what拆分并包装成带引号并利用逗号分隔的字符串,如果what没有值,则为*
what = ','.join(self.escape(f) for f in what) if what else '*'
# 拼接查询前半部分
sql_query = "SELECT %s FROM %s" % (what, tablename)
# 判断查询条件是否拼接
if where:
sql_query += " WHERE %s" % where
# 判断是否限制了查询条数,如果限制了则拼接偏移数与查询条数条件,如果没有限制条数但是限制了偏移量则拼接偏移量与最大查询条数条件
if limit:
sql_query += " LIMIT %d, %d" % (offset, limit)
elif offset:
sql_query += " LIMIT %d, %d" % (offset, self.maxlimit)
# 打印调试信息sql查询语句
logger.debug("<sql: %s>", sql_query)
# 循环获取查询结果行,并yield中断试返回
for row in self._execute(sql_query, where_values):
yield row

'''
功能:字典查询
参数:
tablename:表名
what:查询内容
where:查询条件
where_values:暂时不知道是干什么的,推测是存储查询结果的参数
order:查询排序字段
offset:查询偏移数(起始值)
limit:查询条数
'''

def _select2dic(self, tablename=None, what="*", where="", where_values=[],
order=None, offset=0, limit=None):
# 将表名加上引号,防止有空格时出错
tablename = self.escape(tablename or self.__tablename__)
# 判断what是否为列表或者what是否为元组或者what为None
if isinstance(what, list) or isinstance(what, tuple) or what is None:
# 将what拆分并包装成带引号并利用逗号分隔的字符串,如果what没有值,则为*
what = ','.join(self.escape(f) for f in what) if what else '*'
# 查询语句与查询条件拼接
sql_query = "SELECT %s FROM %s" % (what, tablename)
if where:
sql_query += " WHERE %s" % where
if order:
sql_query += ' ORDER BY %s' % order
if limit:
sql_query += " LIMIT %d, %d" % (offset, limit)
elif offset:
sql_query += " LIMIT %d, %d" % (offset, self.maxlimit)
# 查询语句打印
logger.debug("<sql: %s>", sql_query)
# 执行查询
dbcur = self._execute(sql_query, where_values)
# 遍历dbcur.description并提取结果列表第一个元素
fields = [f[0] for f in dbcur.description]
# 循环获取查询结果行,并yield中断试返回
for row in dbcur:
# zip函数,将接受到的列表序列转化为元组 学习参考:http://www.cnblogs.com/frydsh/archive/2012/07/10/2585370.html
# dict函数用来创建一个字典,学习参考:http://www.cnblogs.com/guyuyuan/p/6952442.html
yield dict(zip(fields, row))

'''
功能:更新或者插入
参数:
tablename:表名
**values:带key:value形式的不定数量参数
'''

def _replace(self, tablename=None, **values):
tablename = self.escape(tablename or self.__tablename__)
if values:
# 获取字段名字并处理格式
_keys = ", ".join(self.escape(k) for k in values)
# 获取字段值并处理格式,处理为:(?,?,?,?)格式,问号个数由values元素个数确定
_values = ", ".join([self.placeholder, ] * len(values))
# 拼接sql语句
sql_query = "REPLACE INTO %s (%s) VALUES (%s)" % (tablename, _keys, _values)
else:
# 没有值的情况下拼接查sql询语句
sql_query = "REPLACE INTO %s DEFAULT VALUES" % tablename
# 打印sql语句
logger.debug("<sql: %s>", sql_query)
# 执行sql语句
if values:
dbcur = self._execute(sql_query, list(itervalues(values)))
else:
dbcur = self._execute(sql_query)
# 返回最后插入行的主键ID
return dbcur.lastrowid

'''
功能:插入
参数:
tablename:表名
**values:带key:value形式的不定数量参数
注:程序结构和上面_replace差不多这里再单独注释
'''

def _insert(self, tablename=None, **values):
tablename = self.escape(tablename or self.__tablename__)
if values:
_keys = ", ".join((self.escape(k) for k in values))
_values = ", ".join([self.placeholder, ] * len(values))
sql_query = "INSERT INTO %s (%s) VALUES (%s)" % (tablename, _keys, _values)
else:
sql_query = "INSERT INTO %s DEFAULT VALUES" % tablename
logger.debug("<sql: %s>", sql_query)

if values:
dbcur = self._execute(sql_query, list(itervalues(values)))
else:
dbcur = self._execute(sql_query)
return dbcur.lastrowid

'''
功能:修改
参数:
tablename:表名
where:查询条件,默认值为1=0,意思是默认为false
where_values:
**values:带key:value形式的不定数量参数
'''

def _update(self, tablename=None, where="1=0", where_values=[], **values):
tablename = self.escape(tablename or self.__tablename__)
_key_values = ", ".join([
"%s = %s" % (self.escape(k), self.placeholder) for k in values
])
sql_query = "UPDATE %s SET %s WHERE %s" % (tablename, _key_values, where)
logger.debug("<sql: %s>", sql_query)

return self._execute(sql_query, list(itervalues(values)) + list(where_values))

'''
功能:删除
参数:
tablename:表名
where:查询条件,默认值为1=0,意思是默认为false
where_values:
**values:带key:value形式的不定数量参数
'''

def _delete(self, tablename=None, where="1=0", where_values=[]):
tablename = self.escape(tablename or self.__tablename__)
sql_query = "DELETE FROM %s" % tablename
if where:
sql_query += " WHERE %s" % where
logger.debug("<sql: %s>", sql_query)

return self._execute(sql_query, where_values)

# 主函数
if __name__ == "__main__":
# 导入sqlite3
import sqlite3

# DB类继承BaseDB
class DB(BaseDB):
# 表名初始化
__tablename__ = "test"
# 占位符初始化
placeholder = "?"

# 初始化函数
def __init__(self):
# 连接数据库
self.conn = sqlite3.connect(":memory:")
# 获取游标
cursor = self.conn.cursor()
# 执行创建测试表语句
cursor.execute(
'''CREATE TABLE `%s` (id INTEGER PRIMARY KEY AUTOINCREMENT, name, age)'''
% self.__tablename__
)

# @property装饰器就是负责把一个方法变成属性调用的,参考文档:http://www.cnblogs.com/ghgyj/p/3997585.html
@property
def dbcur(self):
return self.conn.cursor()

# 构造一个db对象
db = DB()
# assert断言插入
assert db._insert(db.__tablename__, name="binux", age=23) == 1
# assert断言查询
assert db._select(db.__tablename__, "name, age").next() == ("binux", 23)
# assert断言查询字典
assert db._select2dic(db.__tablename__, "name, age").next()["name"] == "binux"
# assert断言查询字典
assert db._select2dic(db.__tablename__, "name, age").next()["age"] == 23
db._replace(db.__tablename__, id=1, age=24)
# assert断言查询
assert db._select(db.__tablename__, "name, age").next() == (None, 24)
db._update(db.__tablename__, "id = 1", age=16)
# assert断言查询
assert db._select(db.__tablename__, "name, age").next() == (None, 16)
db._delete(db.__tablename__, "id = 1")
# assert断言查询
assert [row for row in db._select(db.__tablename__)] == []
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Python PySpider