一、前言
因为本人比较菜,在学习Scrapy 框架之前都是自己手写爬虫,最多也只是开线程解决,最多的数据量也只有2W+,
先说下我以前怎么解决多线程数据库存储到本地(受到倍增启发),因为数据量比较小,没使用数据库,而是生成的excel表格
解决办法 一:写个全局锁,如果有人在写入,那么他就暂停,等待一下,然后再次访问.(后来学了计网,想再加上个二进制指针退避算法23333)
解决办法二:让他们在不同的时间段写入文件
如何保存这种情况呢,倍增的思想,首先将需要爬取的页面分为n,n*2, n*3,n*4,n*5.....。保证一个n 的时间大于文件写入的时间即可。这样每个n 的时间都有一个线程在写入,并且不会冲突(后来发现python 的线程是伪线程233333)
因为数据量比较小,做了有了部分优化,开启,然后处理下其他的事情也就爬完了。但是还是不够!
二、使用Scrapy框架,异步存储到数据库中
因为数据库有个唯一的主键,所以直接异步会出现问题。
我们需要在paplines里面设置存储方:
使用 adbapi 数据库池 解决
代码如下有注释:
# -*- coding: utf-8 -*-
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html
import pymysql
from twisted.enterprise import adbapi
class ScdemoPipeline(object):
"""
这里是用来处理数据的
"""
datalies = []
def __init__(self, dbpool):
print("*" * 80)
self.dbpool = dbpool
@classmethod
def from_settings(cls, settings):
# 连接数据库池
dbpool = adbapi.ConnectionPool('pymysql', host="localhost", port=3306, user="root", passwd="数据库密码",
db="python_database")
return cls(dbpool)
def process_item(self, item, spider):
# 在该函数内,利用连接池对象,开始操作数据,将数据写入到数据库中。
# pool.map(self.insert_db, [1,2,3])
# 同步阻塞的方式: cursor.execute() commit()
# 异步非阻塞的方式
# 参数1:在异步任务中要执行的函数insert_db;
# 参数2:给该函数insert_db传递的参数
query = self.dbpool.runInteraction(self.insert_db, item)
# 如果异步任务执行失败的话,可以通过ErrBack()进行监听, 给insert_db添加一个执行失败的回调事件
query.addErrback(self.handle_error)
return item
def handle_error(self, field):
print('-----数据库写入失败:', field)
def insert_db(self, cursor, item):
insert_sql = "INSERT INTO `python_database`.`lecture` (`lecture_url`, `lecture_time`, `lecture_name`, " \
"`man`, `place`, `time`) " \
"VALUES (%s, %s, %s, %s, %s, %s)"
cursor.execute(insert_sql,
(item["link"], item["lutime"], item["luname"], item["man"], item["place"], item["time"]))
总算把以前没解决的问题解决了。
不过数据库池还需要深入,还有个玩意叫:线程池等我。