写爬虫的时候,习惯性会把爬取到的数据存入mongodb
。为了方便,也为了每次抓取完数据,不在后面接插入数据的生涩代码。最好的选择是简单的封装mongodb
的增删改查功能,如果后面使用mongodb
的时候可以直接调用它,一劳永逸。
pymongo
其实pymongo
这个库已经为我们很好的封装了对数据库的操作,但是我总是很容易忘记或者搞混他的 api。我想实现如果创建数据库,只需要配置好数据库的name
,host
,port
,增加数据:put(data)
,删除数据:delete(data)
。
这样的话,每次需要使用mongodb
的时候,只需要简单配置,不再需要谷歌他的文档 api,上手即用。
封装
首先我们创建一个MongodbClient
类,在向类里面增加对应的功能。
# db.py
from pymongo import MongoClient, ASCENDING
# 我们把配置文件放入 config.py 文件内
from config import NAME, HOST, PORT
class MongodbClient(object):
def __init__(self, table=TABLE, host=HOST, port=PORT):
self.table = table
self.client = MongoClient(host, port)
self.db = self.client.NAME
@property
def get_counts(self):
"""获取表里的数据总数"""
return self.db[self.table].count()
def change_table(self, table):
"""切换数据库表"""
self.table = table
def set_num(self):
"""给每条数据设置唯一自增num"""
nums = []
datas = self.get_all()
if datas:
for data in datas:
nums.append(data['num'])
return max(nums)
return 0
def get_new(self):
"""获取最新一条数据"""
data = self.get_all()[-1] if self.get_all() else None
return data
def get_data(self, num):
"""获取指定num的数据"""
datas = self.get_all()
if datas:
data = [i for i in datas if i['num']==num][0]
return data
return None
def get_all(self):
"""获取全部数据"""
# 判断表里是否有数据
if self.get_counts != 0:
# 先排序
self.sort()
datas = [i for i in self.db[self.table].find()]
return datas
return None
def put(self, data):
"""添加数据"""
num = self.set_num() + 1
# 判断是否重复
if self.db[self.table].find_one(data):
# 重复则删除,重新插入
self.delete(data)
data['num'] = num
self.db[self.table].insert_one(data)
else:
data['num'] = num
self.db[self.table].insert_one(data)
def delete(self, data):
"""删除数据"""
self.db[self.table].remove(data)
def clear(self):
"""清空表"""
self.client.drop_database(self.table)
def sort(self):
"""按num键排序"""
self.db[self.table].find().sort('num', ASCENDING)
常用的功能大致就这么多,如果不够用可以自由扩展。
简述
上面的封装其实主要是对pymongo
库的封装,给每个数据插入自增的num
键是我在抓取IP代理的时候加的,这样做并且基于num
键排序。在IP代理池的爬虫项目中,实际上就是实现了一个栈的数据结构。添加代理进数据库,每次获取取最新添加的代理(get_new()),后进先出。当然我写的很多爬虫都是用的这个封装,方便就好。
在插入数据库的方法上,我选择先对它进行去重操作,其实如果重复可以直接选择不插入,而我是选择删除旧的插入新的。其实这里可以不用去判断整个数据是否重复,如果是某一类数据,比如所有数据不可以出现重复的名字,我们就需要判断名字是否和库中的某条数据重复。这样我们可以这么改:
def put(self, data):
"""添加数据"""
if self.db[self.table].find_one({'name': data['name']}):
...
是不是很方便。
翻页数据
如果使用flask
结合mongodb
数据库使用,如果数据库中有50条数据,而我们只需要每页显示10条。这样我们可以对封装添加分页显示数据的操作:
def get_page(self, page, count):
"""
分页数据
:param page: 页数
:param count: 每页条数
:return: 每页数据
"""
# 判断是否有数据
if self.get_nums != 0:
# 排序
self.sort()
# 如果整除count为0,就直接返回为第一页
pages = self.get_nums // count + 1 if self.get_nums >= 10 else 1
paginate = []
for p in range(1, pages+1):
if p > 1:
datas = [i for i in self.db[self.table].find().limit(count).skip((p-1)*count)]
else:
datas = [i for i in self.db[self.table].find().limit(count)]
paginate.append({'page': p, 'data': datas})
return [j['data'] for j in paginate if j['page'] == page]
return None
我们使用self.db[self.table].find().limit(10)
来保证只显示10条数据,.skip(10)
可以帮我们过滤掉前面的10条数据。这样是不是相当于显示第二页的数据了。
这是我写爬虫时对数据库操作进行的简单的封装,显然对于很多简单的项目已经很满足使用了,如果需要更多功能,添加起来也是很简单的。当然有很多可以改进的地方,比如列表生成式可以改用生成器,这样实现更好些。