之前写脚本爬斗鱼主播信息时用了一个pymongo的去重语句
db['host_info'].update({'主播': data['主播'], '时间': data['时间']}, {'$set': data}, True):
这句话以主播和时间为索引判断数据库中如果没有同一主播同一时间的数据就更新到数据库。一开始还是很好用的,爬取速度还可以,但是我的计划是每天晚上爬取黄金时间整点段的数据,几个小时过后数据量就达到了十几万条,然后速度越来越慢,mongodb进程占用cpu率很高,可以看到数据是一条条地存进去。毕竟以十几万条数据为基准去重工作量很大,随着数据量的增长会更加慢,直到我的电脑爆掉。
仔细分析了一下,我用主播和时间作为索引,每一个整点爬取一次,所以每一次爬取的时间肯定不一样,也就是每一次爬的过程中可能会有重复数据,次与次之间不会存在重复数据。于是就把数据先放入一个空的临时数据表中,仍然用上面的去重方法做去重,等数据全部爬完后再把临时空数据库中的数据存入主数据库中,这时只需要插入,不需要去重,存入的速度是很快的。
#-*- coding:utf-8 -*- #_author:John #date:2018/12/29 20:11 import time from functools import partial import requests import json from multiprocessing import Pool import pymongo import datetimeclient = pymongo.MongoClient('localhost') db = client['douyu']def single_page_info(page, cur_time):# 防止网络无响应,重试3次for i in range(3):try:respones = requests.get('https://www.douyu.com/gapi/rkc/directory/0_0/{}'.format(page))breakexcept Exception as E:print(E)respones = Nonetime.sleep(10)if respones:datas = json.loads(respones.text)items = datas['data']['rl']for item in items:data = {'标题': item['rn'],'主播': item['nn'],'人气': item['ol'],'类别': item['c2name'],'房间号': item['rid'],'时间': cur_time}# 用临时数据表完成此次爬虫的去重工作,在程序结束前把临时数据表删除if db['host_info_draft'].update({'主播': data['主播']}, {'$set': data}, True):print('Save to Mongo, {}'.format(data))else:print('Save to Mong fail, {}'.format(data))print('已经完成第{}页'.format(page))# 存入主数据表 def write_to_primary_db(data):db['host_infos'].insert_one(data) # 删除临时数据表 def drop_draft():db.drop_collection('host_info_draft')if __name__ == '__main__':pool = Pool()print('start')# 多线程抓200页while True:# 判断当前时间是否为整点,如果是则开始爬虫程序minute = datetime.datetime.now().strftime('%M')if int(minute) < 2:# 把初始时间传入作为此次爬虫统一时间,python3用partial可以在map中传入的函数传递参数cur_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M')pool.map(partial(single_page_info, cur_time=cur_time), [page for page in range(1, 201)])print('Move the temporary table to the primary table')pool.map(write_to_primary_db, [data for data in db['host_info_draft'].find()])print('End with this cycle')drop_draft()sleep_time = 60 - int(datetime.datetime.now().strftime('%M'))time.sleep(sleep_time*60)else:time.sleep(58)# cur_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M')# pool.map(partial(single_page_info, cur_time=cur_time), [page for page in range(1, 201)])# drop_draft()