一、使用场景:
定时爬取某网站的数据,每次爬取只爬取并保存新增的数据到数据库中,之前存过的数据不再入库。
scrapy官方文档的去重模块,只能实现对当前抓取数据的去重,并不会和数据库里的数据做对比。当有一天需求变了,在你向mysql 数据库保存的时候,发现已经有一部分已经存在,有一部分新的数据,你又需要添加到mysql数据库中,这时候你就需要通过redis来作为中间件,通过url来确保爬过的数据不会再爬,做到增量爬取。
二、准备工作:
1、电脑端安装和启动Redis服务:(参考http://www.runoob.com/redis/redis-install.html) 不启动将无法访问redis服务器
2、安装python的Redis模块:pip install Redis
三、需要知道的知识点
1、pymysql 如何连接、操作数据库
2、Pandas 如何读取mysql数据
pandas.read_sql(sql,connection)
3、Redis hash 如何保存数据的以及一些方法
Redis是一个键值对的数据库;Redis hash 是一个string类型的field和value的映射表,是redis提供的一种数据结构存储方式。
redis哈希结构:
结构:key field(字段) value
对应:redis_data_dict url(实际的url) 0(代码里设置成了0)
key field value三者的关系:把key想象成一个字典的名称,field想象成键,value想象成值,不过这里我们关心的是field字段,也就是关心的是键。这个value值设置有什么用处没搞清楚。
还需要搞清楚flushdb、Hlen、Hset、hexists。参考http://www.runoob.com/redis/redis-hashes.html
四、代码实现
Redis数据库其实就是一个中间件,因为爬虫爬取的数据并不能直接拿去和mysql中的数据进行比较。
实现原理就相当于将mysql数据库现有数据备份出来保存在一个有键值对的Redis数据库中,再将爬取到的数据和redis数据库中的数据进行比较,若redis数据库中的已经存在数据则丢弃,若redis数据库中不存在该条数据则保存进入mysql数据库。每执行一次redis数据库就会被重置一次。
分为几个步骤:
-
导入模块
-
连接redis
-
连接mysql
-
清空redis里的key,将mysql数据库中的现有数据写入redis
-
将新爬到的数据与redis中的数据进行比对,如重复则丢弃,如不重复则写入mysql数据库。
import pymysql
import redis
import pandas
redis_db = redis.Redis(host='127.0.0.1',port=6379,db=1) # 连接本地redis,db数据库默认连接到0号库,写的是索引值
redis_data_dict = '' # key的名字,里面的内容随便写,这里的key相当于字典名称,而不是key值。为了后面引用而建的
class MysqlRemovePipeline(object):
def __init__(self):
self.conn = pymysql.connect('localhost','root','Abcd1234','test') # 连接mysql
self.cursor = self.conn.cursor() # 建立游标
# print(redis_db)
redis_db.flushdb() # 清空当前数据库中的所有 key,为了后面将mysql数据库中的数据全部保存进去
# print(redis_db)
if redis_db.hlen(redis_data_dict) == 0: # 判断redis数据库中的key,若不存在就读取mysql数据并临时保存在redis中
sql = 'select url from test_zxf' # 查询表中的现有数据
df = pandas.read_sql(sql,self.conn) # 读取mysql中的数据
# print(df)
for url in df['url'].get_values():
redis_db.hset(redis_data_dict,url,0) # 把每个url写入field中,value值随便设,我设置的0 key field value 三者的关系
def process_item(self,item,spider):
"""
比较爬取的数据在数据库中是否存在,不存在则插入数据库
:param item: 爬取到的数据
:param spider: /
"""
if redis_db.hexists(redis_data_dict,item['url']): # 比较的是redis_data_dict里面的field
print("数据库已经存在该条数据,不再继续追加")
else:
self.do_insert(item)
def do_insert(self, item):
insert_sql = """
insert into test_zxf(quote,author,tags,url,born_date,born_location) VALUES(%s,%s,%s,%s,%s,%s)
"""
self.cursor.execute(insert_sql, (item['quote'], item['author'], item['tags'], item['url'],
item['born_date'], item['born_location']))
self.conn.commit() # 提交操作,提交了才真正保存到数据库中
return item
def close_spider(self,spider):
self.cursor.close() # 关闭游标
self.conn.close() # 关闭连接
再配置一下settings文件即可。
这里每次抓取数据都需要保存下url,尽管不是你所需要的数据。然后去比对url来判断是否重复爬取,爬取过的url将不再爬取。
实现增量爬取的方式还有很多,其它的有时间再研究,后续还需要比较下这些方式的不同之处,使用场景分别是什么。加油,吼
源码地址:https://github.com/fang-king/spider/tree/master/quotes_toscrape