【问题标题】:Python/MySQLdb import from CSV with data encapsulation使用数据封装从 CSV 导入 Python/MySQLdb
【发布时间】:2013-07-12 15:57:47
【问题描述】:

我一直在使用 WAMP 来获取一些 csv 日志,并希望通过编写一些我需要执行的日常操作的脚本来转向更加自动化的流程。

我在 PHPmyadmin 中使用直接 CSV 导入功能来处理 CSV 的方言和细节。

我已经用 Python 编写了一个上传器,使用 MySQLdb 来解析日志文件,但是由于日志包含一些无用的字符,我发现我需要在我可能不想的地方进行大量的清理输入成为...

例如,日志是来自目录扫描器的一些数据,我无法控制人们使用的文件夹命名约定。我有这个文件夹:-

"C:\user\NZ Business Roundtable_Download_13Feb2013, 400 Access"

,char 被读取为新的字段标记(毕竟是 csv)。我真正想要做的是忽略引号内的所有文本:- "......"

我看到' 字符存在类似问题,我相信还会有更多。

我发现了这个:-http://www.tech-recipes.com/rx/2345/import_csv_file_directly_into_mysql/,它显示了我如何编写 Python 脚本来像 PHPmyadmin 加载例程一样运行。主要使用这个sn -p:

load data local infile 'uniq.csv' into table tblUniq fields terminated by ','
enclosed by '"'
lines terminated by '\n'
(uniqName, uniqCity, uniqComments)

但是,我想保护我已经编写脚本的表有一些深度处理和更改,所以想知道是否有办法“告诉”MySQL 我想使用 "" 作为文本封装.我要保护的主要处理是在创建新表时给它一个特定的表名,并在其余的处理过程中使用它。

我的制表脚本示例:-

def make_table(self):
    query ="DROP TABLE IF EXISTS `atl`.`{}`".format(self.table)
    self.cur.execute(query)
    query = "CREATE TABLE IF NOT EXISTS `atl`.`{}` (`PK` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, `ID` varchar(10), `PARENT_ID` varchar(10), `URI` varchar(284), \
        `FILE_PATH` varchar(230), `NAME` varchar(125), `METHOD` varchar(9), `STATUS` varchar(4), `SIZE` varchar(9), \
        `TYPE` varchar(9), `EXT` varchar(11), `LAST_MODIFIED` varchar(19), `EXTENSION_MISMATCH` varchar(20), `MD5_HASH` varchar(32), \
        `FORMAT_COUNT` varchar(2), `PUID` varchar(9), `MIME_TYPE` varchar(71), `FORMAT_NAME` varchar(59), `FORMAT_VERSION` varchar(7), \
        `delete_flag` tinyint, `delete_reason` VARCHAR(80), `move_flag` TINYINT, `move_reason` VARCHAR(80), \
        `ext_change_flag` TINYINT, `ext_change_reason` VARCHAR(80), `ext_change_value` VARCHAR(4), `fname_change_flag` TINYINT, `fname_change_reason` VARCHAR(80),\
        `fname_change_value` VARCHAR(80))".format(self.table)
    self.cur.execute(query)
    self.mydb.commit()

我的摄取脚本示例:-

 def ingest_row(self, row):
    query = "insert"
    # Prepare SQL query to INSERT a record into the database.
    query = "INSERT INTO `atl`.`{0}` (`ID`, `PARENT_ID`, `URI`, `FILE_PATH`, `NAME`, `METHOD`, `STATUS`, `SIZE`, `TYPE`, `EXT`, \
        `EXTENSION_MISMATCH`, `LAST_MODIFIED`, `MD5_HASH`, `FORMAT_COUNT`, `PUID`, `MIME_TYPE`, `FORMAT_NAME`,  `FORMAT_VERSION`) \
        VALUES ('{1}','{2}','{3}','{4}','{5}','{6}','{7}','{8}','{9}','{10}','{11}','{12}','{13}','{14}','{15}','{16}','{17}','{18}')".format(self.table, row[0], row[1], row[2], row[3], row[4], \
         row[5], row[6], row[7], row[8], row[9], row[10], row[11], row[12], row[13], row[14], row[15], row[16], row[17])
    try:
        self.cur.execute(query)
        self.mydb.commit()
    except:
        print query
        quit()

日志示例:-

"ID","PARENT_ID","URI","FILE_PATH","NAME","METHOD","STATUS","SIZE","TYPE","EXT","LAST_MODIFIED","EXTENSION_MISMATCH","MD5_HASH","FORMAT_COUNT","PUID","MIME_TYPE","FORMAT_NAME","FORMAT_VERSION"
"1","","file:/C:/jay/NZ%20Business%20Roundtable_Download_13Feb2013,%20400%20Access/","C:\jay\NZ Business Roundtable_Download_13Feb2013, 400 Access","NZ Business Roundtable_Download_13Feb2013, 400 Access",,"Done","","Folder",,"2013-06-28T11:31:36","false",,"",,"","",""
"2","1","file:/C:/jay/NZ%20Business%20Roundtable_Download_13Feb2013,%20400%20Access/1993/","C:\jay\NZ Business Roundtable_Download_13Feb2013, 400 Access\1993","1993",,"Done","","Folder",,"2013-06-28T11:31:36","false",,"",,"","",""

【问题讨论】:

  • 你能提供一个你的日志是什么样子的样本吗?
  • 好点。完成,谢谢。

标签: python mysql csv


【解决方案1】:

您应该使用SQL prepared statements。将数据和 sql 代码与format 混合为SQL injection(几乎总是1st in the top 25 software flaw / security issue)打开了大门。


例如,这是您的数据:

>>> log = """\
... "ID","PARENT_ID","URI","FILE_PATH","NAME","METHOD","STATUS","SIZE","TYPE","EXT","LAST_MODIFIED","EXTENSION_MISMATCH","MD5_HASH","FORMAT_COUNT","PUID","MIME_TYPE","FORMAT_NAME","FORMAT_VERSION"
... "1","","file:/C:/jay/NZ%20Business%20Roundtable_Download_13Feb2013,%20400%20Access/","C:\jay\NZ Business Roundtable_Download_13Feb2013, 400 Access","NZ Business Roundtable_Download_13Feb2013, 400 Access",,"Done","","Folder",,"2013-06-28T11:31:36","false",,"",,"","",""
... "2","1","file:/C:/jay/NZ%20Business%20Roundtable_Download_13Feb2013,%20400%20Access/1993/","C:\jay\NZ Business Roundtable_Download_13Feb2013, 400 Access\1993","1993",,"Done","","Folder",,"2013-06-28T11:31:36","false",,"",,"","",""
... """

我没有文件,所以假设我有:

>>> import StringIO
>>> logfile = StringIO.StringIO(log)

然后让我们构建查询:

>>> import csv
>>> csvreader = csv.reader(logfile)
>>> fields = csvreader.next()
>>> 
>>> table = 'mytable'
>>> 
>>> fields_fmt = ', '.join([ '`%s`' % f for f in fields ])
>>> values_fmt = ', '.join(['%s'] * len(fields))
>>> query = "INSERT INTO `atl`.`{0}` ({1}) VALUES ({2})".format(
... #        self.table, fields_fmt, values_fmt)
...         table, fields_fmt, values_fmt)
>>> query
'INSERT INTO `atl`.`mytable` (`ID`, `PARENT_ID`, `URI`, `FILE_PATH`, `NAME`, `METHOD`, `STATUS`, `SIZE`, `TYPE`, `EXT`, `LAST_MODIFIED`, `EXTENSION_MISMATCH`, `MD5_HASH`, `FORMAT_COUNT`, `PUID`, `MIME_TYPE`, `FORMAT_NAME`, `FORMAT_VERSION`) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)'

那么如果你按摩ingest_row:

def ingest_row(self, row):
    try:
        self.cur.execute(query, row)
        self.mydb.commit()
    except:
        print query
        quit()

然后您可以使用以下命令导入数据:

for row in csvreader:
    ingest_row(row)

【讨论】:

  • 感谢您的 cmets。根据您的建议,我继续进行了更改,并且效果很好。感谢您的宝贵时间。
【解决方案2】:

从不使用字符串格式、连接等来构建 sql 查询!

dbapi 要求所有驱动程序支持参数化查询,参数应提供给游标的execute 方法。对于MySQLdb,它支持格式样式参数化,它看起来像:

cursor.execute('insert into sometable values (%s, %s)', ('spam', 'eggs'))

提供的参数已被库正确转义,因此您的字符串是否包含必须转义的字符并不重要。

在您的特殊情况下,唯一的例外是表名,因为转义会产生非法 sql。

【讨论】:

  • 啊,好的,谢谢。我认为使用字符串format 很聪明。因为它永远不会离开我的机器,所以我不关心 sql 注入等。在你的例子中,你给出的是 execute 文本,而不是 var,我可以安全地做到这一点吗? cursor.execute('insert into sometable values (%s, %s)', (row[1], row[2])).
  • 是的,完全一样。在这种情况下,它实际上不是关于注入,而是关于正确转义插入的文本 - 但两者的原因和解决方案是相同的。如果您的一条记录包含',您不想产生非法的sql;
  • 优秀和理解。谢谢大家,我会按照你的建议去恢复所有的数据库调用,看看我的进展如何!赞赏。谢谢。
  • @JayGattuso - 顺便说一句,LOAD DATA INFILE 帖子正在谈论宁愿将 csv 直接上传到服务器,然后让服务器直接导入它 - 但在你的代码中你是使用 python 脚本插入数据...两者都应该没问题,但是如果您想使用第一种方法,则不需要第二种方法。
  • 是的,明白了,我对 lOAD DATA INFILE 方法中使用的文本转义感兴趣。谢谢。
猜你喜欢
  • 2013-05-22
  • 2020-08-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-22
  • 2022-01-21
  • 2017-03-11
相关资源
最近更新 更多