Python中使用Scrapy框架写爬虫时,如何安全地关闭数据库游标和连接?
用 scrapy 框架中的 Spider 模版来写爬虫,爬网页过程中将数据插入到数据库里,为了能在正常结束和异常中断的情况下都能关闭数据库游标和数据库连接,我在 start_requests 方法中用了 try 和 finally 语句,在 try 中返回 Request 对象,在 finally 中关闭游标和数据库连接。程序的大致结构如下,其中一些操作用汉字来描述了。
实际运行时,执行到 parse()中的将 item 值插入数据库时报错,提示游标未打开。
发现是因为在执行 yield Request()语句时,会先运行 finally 中的语句,然后才执行 parse()方法,从而使得游标在 finally 中先被关闭,进而报错。
恳请大家指点,大家在用 Spider 模板写爬虫时,为了确保在正常和异常状态下都能关闭游标和数据库连接,这些关闭的操作都是放在哪里实现的呢?
感谢!
class MySpider(Spider):
连接数据库的语句
打开游标的语句
def start_requests(self):
driver.get(url)
try:
yield Request(url=url, callback=self.parse)
finally:
关闭游标的语句
关闭数据库连接的语句
def parse(self, response):
解析页面元素并赋值给 item 字典对象
将 item 的值插入到数据库中
yield item
Python中使用Scrapy框架写爬虫时,如何安全地关闭数据库游标和连接?
pipeline 实现 close_spider 方法
在Scrapy里关数据库连接,最稳的方式是用close_spider信号。比如用sqlite3,直接在爬虫类里定义关闭逻辑就行:
import sqlite3
import scrapy
from scrapy import signals
class MySpider(scrapy.Spider):
name = 'example'
def __init__(self):
self.conn = sqlite3.connect('data.db')
self.cursor = self.conn.cursor()
self.cursor.execute('''CREATE TABLE IF NOT EXISTS items (id INTEGER PRIMARY KEY, data TEXT)''')
@classmethod
def from_crawler(cls, crawler, *args, **kwargs):
spider = super().from_crawler(crawler, *args, **kwargs)
crawler.signals.connect(spider.spider_closed, signal=signals.spider_closed)
return spider
def spider_closed(self, spider):
self.cursor.close()
self.conn.close()
spider.logger.info('Database connection closed.')
def parse(self, response):
# 你的爬取逻辑
data = response.css('div::text').get()
self.cursor.execute("INSERT INTO items (data) VALUES (?)", (data,))
self.conn.commit()
关键就两点:一是在spider_closed里按顺序关游标和连接,二是用信号确保爬虫结束时一定会执行。用try-finally也行,但Scrapy的信号机制更贴合框架的设计。
简单说就是:用spider_closed信号处理关闭,别手动调。
别的语言异常处理都是用 catch
吐槽一下楼主的 md 排版 问题描述至少换个行吧,别人看起来也方便
谢谢指点,关于这个做法,我有疑问。例如下面这三个方法返回的对象,如果在爬虫主程序中和 pipelines.py 中都要访问,要如何才能在两个文件之间正确传递呢?
pymysql.connect()
pymysql.connect().cursor()
webdriver.Chrome()
因为 pipelines.py 和 middlewares.py 中都可以定义这两个方法:open_spider()、close_spider()。
那么如果两个文件中同名方法的处理逻辑出现冲突了怎么办呢?
呵呵,真不好意思,我的主贴中虽然把问题描述分为了三个段落,但是每行内容确实很长,下回我会注意让每行内容不要太长的。
建议分开,如果因为某些原因必须同时持有同一个 connection 对象的话,用 spider 对象持有,pipeline 和 middleware 都已访问到,pipelines 和 middleware 中的方法不会冲突的
您说的 spider 对象就是 pipeline 中的 self 对象吧?
如果不是的话,能否举例告知 spider 对象中的什么属性可以存放上述三个方法返回的对象呢?
关于不同文件中同名方法的冲突,我担心的是 pipelines.py 和 middlewares.py 中都有这两个方法时:open_spider()、close_spider(),比如开发人员一时粗心,在 pipelines.py 的 open 方法中将某个变量赋 A 值,但是在 middlewares.py 中的 open 方法中又将整个变量赋 B 值,此时就会出现冲突了,到底实际运行起来,会以哪个文件中的方法为准呢?


