【问题标题】:Issues with SQLite Table - Specifically foreign keysSQLite 表的问题 - 特别是外键
【发布时间】:2015-01-07 22:55:42
【问题描述】:

好的,我遇到的主要问题是我有一个不应该有任何重复条目的表。这是因为我希望有一个将在单独的表中引用的主键。

根据我对规范化的了解,最好以这种方式设计您的数据库。所以现在我有一个包含一堆重复条目的表(应该只有 6 个唯一条目,但有 30 个条目,每个唯一条目重复 5 次)。

我应该如何解决这个问题?我应该在导入数据时解决这个问题,还是使用 UNIQUE 关键字。请注意,当我这次尝试使用 UNIQUE 功能时,它给了我一个错误,因为我正在导入的数据确实有重复条目。

编辑:

这是我的物品的样子:

from scrapy.item import Item, Field

class TeamStats(Item):
    # define the fields for your item here like:
    # name = scrapy.Field()

    team = Field()
    division = Field()
    rosterurl = Field()
    player_desc = Field()
    playerurl = Field()
    pass

class Player(Item):
    exp = Field()
    pass

这是我的代码的样子:

import scrapy
import string
import re
from scrapy.selector import HtmlXPathSelector                                                                       ##needed to import xpath command
from scrapy.shell import inspect_response                                                                           ##needed for Response object
from nbastats.items import TeamStats, Player                                                                  ##needed to import player stats


class NbastatsSpider(scrapy.Spider):
    name = "nbaStats"

    start_urls = [
        "http://espn.go.com/nba/teams"                                                                              ##only start not allowed because had some issues when navigated to team roster pages
        ]
    def parse(self,response):
        items = []                                                                                                  ##array or list that stores TeamStats item
        i=0                                                                                                         ##counter needed for older code

        for division in response.xpath('//div[@id="content"]//div[contains(@class, "mod-teams-list-medium")]'):     
            for team in division.xpath('.//div[contains(@class, "mod-content")]//li'):
                item = TeamStats()

                item['division'] = division.xpath('.//div[contains(@class, "mod-header")]/h4/text()').extract()[0]            
                item['team'] = team.xpath('.//h5/a/text()').extract()[0]
                item['rosterurl'] = "http://espn.go.com" + team.xpath('.//div/span[2]/a[3]/@href').extract()[0]
                items.append(item)
                print(item['rosterurl'])
                request = scrapy.Request(item['rosterurl'], callback = self.parseWPNow)
                request.meta['play'] = item

                yield request

    def parseWPNow(self, response):
        item = response.meta['play']
        item = self.parseRoster(item, response)
        return item

    def parseRoster(self, item, response):
        players1 = []
        int = 0
        for players in response.xpath("//td[@class='sortcell']"):
            play = {}
            play['name'] = players.xpath("a/text()").extract()[0]
            play['position'] = players.xpath("following-sibling::td[1]").extract()[0]
            play['age'] = players.xpath("following-sibling::td[2]").extract()[0]
            play['height'] = players.xpath("following-sibling::td[3]").extract()[0]
            play['weight'] = players.xpath("following-sibling::td[4]").extract()[0]
            play['college'] = players.xpath("following-sibling::td[5]").extract()[0]
            play['salary'] = players.xpath("following-sibling::td[6]").extract()[0]
            players1.append(play)
        item['playerurl'] = response.xpath("//td[@class='sortcell']/a").extract()
        item['player_desc']=players1
        return item

这是我的管道的样子:

class NbastatsPipeline(object):

    def __init__(self):
        self.setupDBCon()
        self.createTables()

    def setupDBCon(self):
        self.con = lite.connect('test.db')
        self.cur = self.con.cursor()

    def createTables(self):
        self.dropTeamsTable()
        self.dropPlayersTable()
        self.dropDivsTable()

        self.createTeamsTable()
        self.createPlayersTable()
        self.createDivsTable()

    def createTeamsTable(self):
        self.cur.execute("CREATE TABLE IF NOT EXISTS Teams(P_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, \
            team TEXT, \
            DivId INTEGER, \
            FOREIGN KEY (DivId) REFERENCES Divs1(Did) \
            )")

    def createDivsTable(self):
        self.cur.execute("CREATE TABLE IF NOT EXISTS Divs(Did INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, \
            division TEXT)")

    def createPlayersTable(self):
        self.cur.execute("CREATE TABLE IF NOT EXISTS Players(player_name TEXT, \
            salary TEXT, \
            weight INTEGER, \
            age INTEGER, \
            college TEXT )")

    def dropTeamsTable(self):
        self.cur.execute("DROP TABLE IF EXISTS Teams")
    def dropPlayersTable(self):
        self.cur.execute("DROP TABLE IF EXISTS Players")
    def dropDivsTable(self):
        self.cur.execute("DROP TABLE IF EXISTS Divs")

    def closeDB(self):
        self.con.close()

    def __del__(self):
        self.closeDB()

    def process_item(self, item, spider):
        for key, value in item.iteritems():
            if key == "division":
                print(item.get('division', ""))
                self.cur.execute("INSERT INTO Divs( division ) VALUES(?)", (item.get('division', ""),))
                self.con.commit() 
#                self.storeInDb(item) #this is the line you'll use when ready to completely pass item through to storeInDb but it'll be lower in code

        return item

【问题讨论】:

    标签: python database sqlite web-scraping scrapy


    【解决方案1】:

    通常的方法是在数据库级别强制唯一性并在您的应用程序中处理它。

    特别是对于 Scrapy,如果你在管道中插入记录,通常你会有这样的结构:

    import sqlite3
    from scrapy.exceptions import DropItem     
    
    try:
        cursor.execute("""
            INSERT INTO 
                table 
                (field1, field2) 
            VALUES (?, ?)""", (field1, field2))
    except sqlite3.IntegrityError:
        raise DropItem('Duplicate entry')
    

    另见 sqlite 管道示例:


    还有一个名为scrapy-dblite 的项目,它提供了一个很好的抽象——你不需要深入到SQL 查询,它内置了一个简单的ORM。例如:

    from scrapy.exceptions import DropItem
    from myproject.items import Product
    import dblite
    
    class StoreItemsPipeline(object):
        def __init__(self):
            self.ds = None
    
        def open_spider(self, spider):
            self.ds = open(Product, 'sqlite://db/products.sqlite:items', autocommit=True)
    
        def close_spider(self, spider):
            self.ds.commit()
            self.ds.close()
    
        def process_item(self, item, spider):
            if isinstance(item, Product):
                try:
                    self.ds.put(item)
                except dblite.DuplicateItem:
                    raise DropItem("Duplicate item found: %s" % item)
            else:
                raise DropItem("Unknown item type, %s" % type(item))
            return item
    

    【讨论】:

    • 在数据库级别强制唯一性并在我的应用程序中处理它意味着我会做一些事情,比如在我的应用程序中创建一个列表并将其传递给我的表(该表只能容纳这 6 个条目及其主键自动递增)正确吗?我只是有点困惑,因为我有一个包含这 6 个唯一条目的表,然后是另一个通过主键引用此列的表,所以这意味着我必须为了获得数字(外键)而制作此列表在一行中,而不是字符串。
    • @user3042850 好的,让我们尝试解决这个问题。能否请您显示您在数据库中的表和您在 scrapy 项目中的项目?
    • 所以现在我正在尝试用独特的 NBA 分区填充 divs 表,其中有 6 个。但是每个分区有 5 支球队,所以由于正常化,我不想让该列在 Teams 表中具有重复数据,因此我创建了一个 Divs 表来容纳该信息。然后我希望将部门作为外键插入到 Teams 表中。嗯,如果这不是您希望的格式,我绝对可以尝试将其转换为更易于使用的格式。
    • 很抱歉一直用 cmets 轰炸你,但我只需要这件事的帮助,因为我只是不确定如何去实现这个主键的东西,我想我知道如何使用它并且何时使用它,但这样只会迫使我开发我的代码以产生某些格式的输出。
    • @user3042850 这对于单个线程来说问题太多了。尝试将问题分解为步骤并逐步解决,从设计数据库表开始。如果您有困难,请考虑在此处针对 SO 提出单独的问题。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-17
    • 1970-01-01
    • 1970-01-01
    • 2021-06-29
    相关资源
    最近更新 更多