使用Scrapy+SQLAlchemy导出数据到SQLite时遇类型绑定错误
解决Scrapy爬虫导出SQLite时的
sqlalchemy.exc.InterfaceError错误 问题原因
你遇到的错误根源是数据类型不匹配:从报错参数可以看到,每个待插入的值都是列表类型(比如['ROKT']),但你的SQLAlchemy模型字段定义的是Text/String类型,SQLite无法将列表直接存储为文本,因此触发"不支持的类型"错误。
这是因为Scrapy的ItemLoader默认会把通过add_css/add_xpath收集到的结果存储为列表——哪怕只有一个匹配值,也会被包装进列表里。
解决方案
有两种常用方法可以解决这个问题,选择适合你的方式即可:
方法一:在爬虫阶段处理列表转字符串
直接在ItemLoader加载时获取单个值,或者加载后将列表转为字符串:
for etf in etfs: loader = ItemLoader(item=BrandetfsItem(), selector=etf) loader.add_css('ticker', 'a.linkTickerName::text') loader.add_css('name', 'td.col_2::text') loader.add_css('issuer', 'td.col_3::text') loader.add_css('aum', 'td.col_4::text') loader.add_css('expense_ratio', 'td.col_5::text') loader.add_css('tr', 'td.col_6::text') loader.add_css('segment', 'td.col_7::text') item = loader.load_item() # 遍历字段,将列表转为单个字符串,同时处理空值 for field in ['ticker', 'name', 'issuer', 'aum', 'expense_ratio', 'tr', 'segment']: if item.get(field): item[field] = item[field][0].strip() else: item[field] = '' yield item
方法二:在Pipeline阶段处理列表转字符串
如果不想修改爬虫逻辑,也可以在存储前处理数据:
class BrandetfsPipeline(object): def __init__(self): engine = db_connect() create_table(engine) self.Session = sessionmaker(bind=engine) def process_item(self, item, spider): session = self.Session() etf = ETF() # 取列表第一个元素,为空则设默认值,避免索引越界 etf.ticker = item["ticker"][0].strip() if item["ticker"] else '' etf.name = item["name"][0].strip() if item["name"] else '' etf.issuer = item["issuer"][0].strip() if item["issuer"] else '' etf.aum = item["aum"][0].strip() if item["aum"] else '' etf.expense_ratio = item["expense_ratio"][0].strip() if item["expense_ratio"] else '' etf.tr = item["tr"][0].strip() if item["tr"] else '' etf.segment = item["segment"][0].strip() if item["segment"] else '' try: session.add(etf) session.commit() except: session.rollback() raise finally: session.close() return item
注意事项
- 推荐使用方法一,让数据清洗和数据存储职责分离,代码结构更清晰。
- 一定要处理空值判断:如果某个字段抓取失败,对应的列表会是空的,直接取
[0]会触发索引越界错误,所以要先判断列表是否非空。
内容的提问来源于stack exchange,提问作者YungOne
相关产品推荐
相关产品推荐

