Scrapy插入SQLite遇NameError,如何将字典数据写入SQLite?
解决Scrapy爬虫写入SQLite时的NameError问题
问题描述
我尝试用Scrapy把字典数据写入SQLite数据库,但执行失败,报错:
NameError: name 'url' is not defined
触发错误的代码行是:
cursor.execute('INSERT INTO posts(url, First_and_Last_Name) VALUES("%s", "%s")' % (url, First_and_Last_Name))
我的完整爬虫代码如下:
from scrapy.crawler import CrawlerProcess import scrapy import sqlite3 class Wellness(scrapy.Spider): name = "wellness" start_urls = ['https://www.wellness.com/dir/6022571/acupuncturist/ri/east-greenwich/hwasook-lee-phoenix-fertility-center-dac'] def parse(self, response): item = { 'url' : response.request.url, 'First_and_Last_Name' : response.css('h1::text').get(), } directions_link = response.css('#directions_tab a::attr(href)').get() yield scrapy.Request(response.urljoin(directions_link), callback=self.parse_directions, meta={'item': item}) def parse_directions(self, response): item = response.meta['item'] item['Phone'] = response.css('.tel::text').get() yield item db = sqlite3.connect('posts.db') db.execute('''CREATE TABLE IF NOT EXISTS posts(id INTEGER PRIMARY KEY AUTOINCREMENT,url TEXT, First_and_Last_Name TEXT);''') db.commit() cursor = db.cursor() cursor.execute('INSERT INTO posts(url, First_and_Last_Name) VALUES("%s", "%s")' % (url, First_and_Last_Name)) db.commit() db.close() if __name__ == '__main__': process = CrawlerProcess() process.crawl(Wellness) process.start()
请问我哪里操作错误?恳请各位提供帮助,谢谢!
问题根源分析
你遇到的NameError本质上是两个问题导致的:
- 作用域错误:你把SQL插入代码写在了全局作用域里,这部分代码会在爬虫启动前就执行,但此时爬虫还没开始爬取,
url和First_and_Last_Name这两个变量根本不存在——它们是爬虫在parse方法里生成的Item字段,只有爬虫运行时才会产生。 - SQL注入风险:直接用
%s字符串拼接构造SQL语句是不安全的,不仅可能引发语法错误,还会带来SQL注入隐患。
正确解决方案:用Scrapy Item Pipeline处理数据库存储
Scrapy专门设计了Pipeline组件来处理爬取到的Item数据,这是存储数据的标准且规范的方式。以下是修改后的完整代码:
1. 调整爬虫代码(可选:定义Item类,让数据结构更清晰)
from scrapy.crawler import CrawlerProcess import scrapy import sqlite3 from scrapy.item import Item, Field # 定义Item类(可选,但推荐用这种方式规范数据结构) class WellnessItem(Item): url = Field() First_and_Last_Name = Field() Phone = Field() class Wellness(scrapy.Spider): name = "wellness" start_urls = ['https://www.wellness.com/dir/6022571/acupuncturist/ri/east-greenwich/hwasook-lee-phoenix-fertility-center-dac'] def parse(self, response): item = WellnessItem() item['url'] = response.request.url item['First_and_Last_Name'] = response.css('h1::text').get() directions_link = response.css('#directions_tab a::attr(href)').get() yield scrapy.Request(response.urljoin(directions_link), callback=self.parse_directions, meta={'item': item}) def parse_directions(self, response): item = response.meta['item'] item['Phone'] = response.css('.tel::text').get() yield item
2. 编写数据库Pipeline
class SQLitePipeline: def open_spider(self, spider): # 爬虫启动时初始化数据库连接 self.conn = sqlite3.connect('posts.db') self.cursor = self.conn.cursor() # 创建表(如果不存在),新增Phone字段适配你的Item self.cursor.execute('''CREATE TABLE IF NOT EXISTS posts( id INTEGER PRIMARY KEY AUTOINCREMENT, url TEXT, First_and_Last_Name TEXT, Phone TEXT);''') self.conn.commit() def close_spider(self, spider): # 爬虫关闭时关闭数据库连接,避免资源泄漏 self.conn.close() def process_item(self, item, spider): # 使用参数化查询插入数据,彻底避免SQL注入和字符串转义问题 self.cursor.execute('''INSERT INTO posts(url, First_and_Last_Name, Phone) VALUES(?, ?, ?)''', (item['url'], item['First_and_Last_Name'], item['Phone'])) self.conn.commit() return item
3. 配置并启动爬虫
if __name__ == '__main__': process = CrawlerProcess(settings={ 'ITEM_PIPELINES': { # 启用我们编写的Pipeline,优先级数字越小越先执行 '__main__.SQLitePipeline': 300, } }) process.crawl(Wellness) process.start()
关键改进点
- 数据流向正确:只有当爬虫真正爬取到数据并生成Item时,Pipeline的
process_item方法才会执行插入操作,确保能获取到有效的字段值。 - 安全的SQL操作:用
?作为占位符的参数化查询,既避免了SQL注入,也解决了特殊字符串(比如含引号的内容)的转义问题。 - 资源管理规范:通过
open_spider和close_spider统一管理数据库连接的生命周期,避免不必要的资源浪费。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

