首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >psycopg2.ProgrammingError:不完整的占位符:'%(‘没有’)

psycopg2.ProgrammingError:不完整的占位符:'%(‘没有’)
EN

Stack Overflow用户
提问于 2020-02-02 13:02:52
回答 1查看 1.9K关注 0票数 2

我有几个不同的函数,它们使用熊猫刮取不同的表,将每个表保存到一个dataframe中,并将它们保存到PostgreSQL数据库中。我能够成功地抓取每个表,并将其保存为一个dataframe,但在将其保存到SQL中时,我遇到了一些问题。我试图这样做如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
from sqlalchemy import create_engine

# Opening sql connection
engine = create_engine('postgresql://postgres:pw@localhost/name')
con = engine.connect()

def df1():
    df = scraped_data
    df.to_sql(table_name, con, if_exists='replace')
df1()

def df2():
    df = scraped_data
    df.to_sql(table_name, con, if_exists='replace')
df2()

# Closing connection
con.close()

我能够成功地将df1保存到SQL,但在运行df2时会出现错误。这两个函数之间唯一真正的区别是它们从不同的源抓取数据,其他的一切本质上都是一样的。

对于其他数据文件,我还有几个其他函数,但是不管调用函数的顺序如何,只有第一个函数才能工作。

对于我调用的所有其他函数,我总是得到相同的错误:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
psycopg2.ProgrammingError: incomplete placeholder: '%(' without ')'

他们还链接了一个关于错误背景的页面:http://sqlalche.me/e/f405),尽管我仍然不知道如何处理它。

我只是觉得奇怪的是,它是如何工作的一个功能,而不是其他,当唯一的变化是我正在从url中删除。

编辑

我正在从NFL的网站上抓取数据。

df1http://www.nfl.com/stats/categorystats?archive=false&conference=null&role=TM&offensiveStatisticCategory=GAME_STATS&defensiveStatisticCategory=null&season=2019&seasonType=REG&tabSeq=2&qualified=false&Submit=Go的表中遍历数年。

df2做了一件非常类似的事情,但是从http://www.nfl.com/stats/categorystats?archive=false&conference=null&role=TM&offensiveStatisticCategory=TEAM_PASSING&defensiveStatisticCategory=null&season=2019&seasonType=REG&tabSeq=2&qualified=false&Submit=Go中提取数据。

看起来,主要的区别是df1使用Pct来表示列标题中的百分比,而df2使用%

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-02-02 23:38:53

TL;博士:您有一个潜在的SQL注入孔。

问题是,您的一个列名包含%。下面是一个最小的可重现性示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
In [8]: df = pd.DataFrame({"%A": ['x', 'y', 'z']})

In [9]: df.to_sql('foo', engine, if_exists='replace')

它生成以下日志和跟踪:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
...
INFO:sqlalchemy.engine.base.Engine:
DROP TABLE foo
INFO:sqlalchemy.engine.base.Engine:{}
INFO:sqlalchemy.engine.base.Engine:COMMIT
INFO:sqlalchemy.engine.base.Engine:
CREATE TABLE foo (
        index BIGINT, 
        "%%A" TEXT
)


INFO:sqlalchemy.engine.base.Engine:{}
INFO:sqlalchemy.engine.base.Engine:COMMIT
INFO:sqlalchemy.engine.base.Engine:BEGIN (implicit)
INFO:sqlalchemy.engine.base.Engine:INSERT INTO foo (index, "%%A") VALUES (%(index)s, %(%A)s)
INFO:sqlalchemy.engine.base.Engine:({'index': 0, '%A': 'x'}, {'index': 1, '%A': 'y'}, {'index': 2, '%A': 'z'})
INFO:sqlalchemy.engine.base.Engine:ROLLBACK
---------------------------------------------------------------------------
ProgrammingError                          Traceback (most recent call last)
~/Work/sqlalchemy/lib/sqlalchemy/engine/base.py in _execute_context(self, dialect, constructor, statement, parameters, *args)
   1239                     self.dialect.do_executemany(
-> 1240                         cursor, statement, parameters, context
   1241                     )

~/Work/sqlalchemy/lib/sqlalchemy/dialects/postgresql/psycopg2.py in do_executemany(self, cursor, statement, parameters, context)
    854         if self.executemany_mode is EXECUTEMANY_DEFAULT:
--> 855             cursor.executemany(statement, parameters)
    856             return

ProgrammingError: incomplete placeholder: '%(' without ')'

The above exception was the direct cause of the following exception:

ProgrammingError                          Traceback (most recent call last)
<ipython-input-9-88cf8a93ad8c> in <module>()
----> 1 df.to_sql('foo', engine, if_exists='replace')

...

ProgrammingError: (psycopg2.ProgrammingError) incomplete placeholder: '%(' without ')'
[SQL: INSERT INTO foo (index, "%%A") VALUES (%(index)s, %(%A)s)]
[parameters: ({'index': 0, '%A': 'x'}, {'index': 1, '%A': 'y'}, {'index': 2, '%A': 'z'})]
(Background on this error at: http://sqlalche.me/e/f405)

可以看到,SQLAlchemy/Pandas使用列名作为占位符键:%(%A)s--这意味着您可能对since 开放,特别是在处理刮取的数据时:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
In [3]: df = pd.DataFrame({"A": [1, 2, 3], """A)s);
   ...: DO $$
   ...: BEGIN
   ...: RAISE 'HELLO, BOBBY!';
   ...: END;$$ --""": ['x', 'y', 'z']})

In [4]: df.to_sql('foo', engine, if_exists='replace')

结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
...
INFO sqlalchemy.engine.base.Engine INSERT INTO foo (index, "A", "A)s);
DO $$
BEGIN
RAISE 'HELLO, BOBBY!';
END;$$ --") VALUES (%(index)s, %(A)s, %(A)s);
DO $$
BEGIN
RAISE 'HELLO, BOBBY!';
END;$$ --)s)
INFO sqlalchemy.engine.base.Engine ({'index': 0, 'A': 1, "A)s);\nDO $$\nBEGIN\nRAISE 'HELLO, BOBBY!';\nEND;$$ --": 'x'}, {'index': 1, 'A': 2, "A)s);\nDO $$\nBEGIN\nRAISE 'HELLO, BOBBY!';\nEND;$$ --": 'y'}, {'index': 2, 'A': 3, "A)s);\nDO $$\nBEGIN\nRAISE 'HELLO, BOBBY!';\nEND;$$ --": 'z'})
INFO sqlalchemy.engine.base.Engine ROLLBACK
---------------------------------------------------------------------------
RaiseException                            Traceback (most recent call last)
...

InternalError: (psycopg2.errors.RaiseException) HELLO, BOBBY!
CONTEXT:  PL/pgSQL function inline_code_block line 3 at RAISE

[SQL: INSERT INTO foo (index, "A", "A)s);
DO $$
BEGIN
RAISE 'HELLO, BOBBY!';
END;$$ --") VALUES (%(index)s, %(A)s, %(A)s);
DO $$
BEGIN
RAISE 'HELLO, BOBBY!';
END;$$ --)s)]
[parameters: ({'index': 0, 'A': 1, "A)s);\nDO $$\nBEGIN\nRAISE 'HELLO, BOBBY!';\nEND;$$ --": 'x'}, {'index': 1, 'A': 2, "A)s);\nDO $$\nBEGIN\nRAISE 'HELLO, BOBBY!';\nEND;$$ --": 'y'}, {'index': 2, 'A': 3, "A)s);\nDO $$\nBEGIN\nRAISE 'HELLO, BOBBY!';\nEND;$$ --": 'z'})]
(Background on this error at: http://sqlalche.me/e/2j85)

如果您使用的是具有足够权限的数据库用户,这允许例如在计算机上执行任意命令:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
In [11]: df = pd.DataFrame({"A": [1, 2, 3], """A)s);
    ...: CREATE TEMPORARY TABLE IF NOT EXISTS evil (state text);
    ...: DO $$
    ...: BEGIN
    ...: IF NOT EXISTS (SELECT * FROM evil) THEN
    ...: COPY evil (state) FROM PROGRAM 'send_ssh_keys | echo done';
    ...: END IF;
    ...: END;$$ --""": ['x', 'y', 'z']})

这似乎是SQLAlchemy(和/或Pandas')部分的疏忽,但通常不允许用户或外部数据定义模式,因此表名和列名是“受信任的”。在这种情况下,唯一合适的解决方案是白名单列,即检查已知的数据列只有允许的列。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60030570

复制
相关文章
Sass占位符
在Sass中,我们引入了占位符“%placeholder”来进一步优化“@extend”。我们先来看一个例子。
Qwe7
2022/05/14
1.1K0
SQL注入、占位符拼接符
所谓SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。具体来说,它是利用现有应用程序,将(恶意的)SQL命令注入到后台数据库引擎执行的能力,它可以通过在Web表单中输入(恶意)SQL语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句。
小马哥学JAVA
2022/11/21
2.2K0
golang-占位符
Printf 格式化输出 通用占位符: v 值的默认格式。 %+v 添加字段名(如结构体) %#v  相应值的Go语法表示 %T 相应值的类型的Go语法表示 %% 字面上的百分号,并非值的占位符  布尔值: %t true 或 false 整数值: %b 二进制表示 %c 相应Unicode码点所表示的字符 %d 十进制表示 %o 八进制表示 %q 单引号围绕的字符字面值,由Go语法安全地转义 %x 十六进制表示,字母形式为小写 a-f %X 十六进制表示,字母形式为大写 A-F %U Unicode格式:U+1234,等同于 "U+%04X" 浮点数及复数: %b 无小数部分的,指数为二的幂的科学计数法,与 strconv.FormatFloat中的 'b' 转换格式一致。例如 -123456p-78 %e 科学计数法,例如 -1234.456e+78 %E 科学计数法,例如 -1234.456E+78 %f 有小数点而无指数,例如 123.456 %g 根据情况选择 %e 或 %f 以产生更紧凑的(无末尾的0)输出 %G 根据情况选择 %E 或 %f 以产生更紧凑的(无末尾的0)输出 字符串和bytes的slice表示: %s 字符串或切片的无解译字节 %q 双引号围绕的字符串,由Go语法安全地转义 %x 十六进制,小写字母,每字节两个字符 %X 十六进制,大写字母,每字节两个字符 指针: %p 十六进制表示,前缀 0x 这里没有 'u' 标记。若整数为无符号类型,他们就会被打印成无符号的。类似地,这里也不需要指定操作数的大小(int8,int64)。 对于%v来说默认的格式是: bool: %t int, int8 etc.: %d uint, uint8 etc.: %d, %x if printed with %#v float32, complex64, etc: %g string: %s chan: %p pointer: %p 由此可以看出,默认的输出格式可以使用%v进行指定,除非输出其他与默认不同的格式,否则都可以使用%v进行替代(但是不推荐使用)
nbsp-nbsp
2020/09/22
1.6K0
浅谈mybatis中的占位符
浅谈mybatis中的占位符 #{}占位符 ​ 把传入的数据都当成字符串,会对传入的数据自动加上引号 例如: select * from emp where name=#{name} --会被解析转义成 select * from emp where name="name" ${}占位符 ​ 不会经过转义,直接把值传入sql中 例如: select * from emp where name=${name} --不会被转义 select * from emp where name=name 但是需要注意的
许喜朝
2020/08/11
1.6K0
[Go]fmt Sprintf的格式占位符%
普通占位符 占位符 说明 举例 输出 %v 相应值的默认格式。 Printf("%v", people) {zhangsan} %+v 打印结构体时,会添加字段名 Printf("%+v", people) {Name:zhangsan} %#v 相应值的Go语法表示 Printf("#v", people) main.Human{Name:"zhangsan"} %T 相应值的类型的Go语法表示 Printf("%T", people) main.Human %% 字面上的百分号,并非值的占位符 Prin
唯一Chat
2021/04/09
3.5K0
Swift 中的类型占位符
Swift 的类型推断能力从一开始就是语言的核心部分,它极大地减少了我们在声明有默认值的变量和属性时手动指定类型的工作。例如,表达式var number = 7不需要包含任何类型注释,因为编译器能够推断出值7是一个Int,我们的number变量应该被相应的类型化。
Swift社区
2022/07/05
1.7K0
【Python】字符串 ③ ( Python 字符串格式化 | 单个占位符 | 多个占位符 | 不同类型的占位符 )
在上一篇博客 【Python】字符串 ② ( 字符串拼接 | 字符串与非字符串不能直接拼接 | TypeError: can only concatenate str (not “int“) to str ) 中 , 介绍了 使用 + 运算符拼接字符串 的方法 , 该方法有一定的弊端
韩曙亮
2023/04/08
1.5K0
【Python】字符串 ③ ( Python 字符串格式化 | 单个占位符 | 多个占位符 | 不同类型的占位符 )
Swift 中的类型占位符
Swift 的类型推断能力从一开始就是语言的核心部分,它极大地减少了我们在声明有默认值的变量和属性时手动指定类型的工作。例如,表达式var number = 7不需要包含任何类型注释,因为编译器能够推断出值7是一个Int,我们的number变量应该被相应的类型化。
韦弦zhy
2022/04/26
1.5K0
python中占位符的使用
小明的成绩从去年的72分提升到了今年的85分,请计算小明成绩提升的百分点,并用字符串格式化显示出'xx.x%',只保留小数点后1位:
py3study
2020/01/10
2.2K0
React 中的占位符 Fragment
在 React 项目中, render 方法只能有一个根元素,一般都是 <div> <div/> ,然后在里面写上我们的组件,渲染到浏览器一看,除了我们想要显示的组件,外面还套着一层 div ,如果在写项目的时候,套了很多曾组件,那么每一层都会多出来一个父级元素 div ,不美观,而且在调整样式的时候会有些麻烦
子舒
2022/06/09
1.7K0
配置文件占位符
1、随机数 ${random.value}、${random.int}、${random.long} ${random.int(10)}、${random.int[1024,65536]} 2、占位符获取之前配置的值,如果没有可以用:指定默认值 person.lastName=张三${random.uuid} person.age=${random.int(10)} person.birth=2017/12/15 person.boss=false person.maps.k1=v1 person.maps
桑鱼
2020/03/18
9810
图片加载失败占位符[通俗易懂]
当网络不佳加载图片时会出现加载失败或者延时加载的情况,此时原本的图片位置会显示空白状态,这造成了不好的用户体验,所以我们需要加一个图片占位符。 有两种方式可以实现:
全栈程序员站长
2022/09/05
3K0
函数或条件子句的占位符
于一般不推荐在遍历数据集合时直接修改原数据集合来获取我们想要的数据集合,这样不安全且不够灵活。推荐在遍历原数据集合时根据条件创建一个新的数据集合,遴选公务员而这正是Python语言中for语句的强大之处。
用户7737280
2021/12/02
8160
MyBatis实现SQL占位符替换
博主记得在一个周五快下班的下午,产品找到我(为什么总感觉周五快下班就来活 😂),跟我说有几个业务列表查询需要加上时间条件过滤数据,这个条件可能会变,不保证以后不修改,这个改动涉及到多个列表查询,于是博主思考了一会想了几种实现方案,
wayn
2023/08/28
1.1K0
MyBatis实现SQL占位符替换
【Python】f-String 占位符
%格式化相较于f-string格式化不够自由。但是%格式化把所有需要填入的信息放到待格式化字符串的后面,在一些时候是更加合适的方式。
杨丝儿
2022/02/25
2K0
继承、占位符和混合宏
很多初学者刚刚接触的时候,都容易纠结什么时候用混合宏,什么时候用继承,然后什么时候用占位符。其实,这3个都有它们自身的优缺点。这一节我们来详细探讨一下这3个在实际开发中的不同。
Qwe7
2022/05/15
8610
Golang之fmt格式“占位符”
golang的fmt包实现了格式化I/O函数: package main import "fmt" type Human struct { Name string } func main() { //普通占位符 var people = Human{Name: "咸鱼兆"} // %+v 添加字段名,输出相应值的默认格式 fmt.Printf("%+v\n", people) //相应值用go语法形式输出 fmt.Printf("%#v", pe
超蛋lhy
2018/08/31
1.1K0
java之mybatis之占位符
2. #{} 占位符是为了获取值,获取的值用在 where 语句后,insert 语句后,update 语句。
Vincent-yuan
2019/09/11
1.6K0
Spring中PropertyPlaceholderConfigurer替换占位符的问题
多个Maven项目聚合的时候,每个maven都有自己的配置文件,并且都用了PropertyPlaceholderConfigurer替换占位符,然后启动的时候一直报错,说替换失败;问题症结就是 spirng配置多个PropertyPlaceholderConfigurer的问题
石臻臻的杂货铺[同名公众号]
2021/07/14
1.4K0
Mybatis处理动态占位符实现
老乡式打招呼 -> “你好,我也是 xxx 的,我们是老乡呀!”(老乡见老乡,少来这套,来了就是深圳人)
GreizLiao
2020/03/28
1.7K0

相似问题

没有填充的输入占位符

11

没有jquery占位符回退?

11

预测动态占位符的占位符

14

ComboBox占位符没有随机显示

22

Confiforms没有显示占位符文本

110
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文