Python数据分析在投标人串标审计中的运用
为了改善工程项目招投标领域的营商环境,维护社会主义市场经济秩序,查处工程项目招投标违法违规行为,结合数据分析技术在审计领域的运用,谈谈审计人员在电子招投标中如何运用Python分析软件查处工程项目中投标人串标行为。
一、投标人串标审计的法律法规依据
《中华人民共和国招标投标法实施条例》第40条规定,有下列情形之一的,视为投标人相互串通投标:
1.不同投标人的投标文件由同一单位或者个人编制;
2.不同投标人委托同一单位或者个人办理投标事宜;
3.不同投标人的投标文件载明的项目管理成员为同一人;
4.不同投标人的投标文件异常一致或者投标报价呈规律性差异;
5.不同投标人的投标文件相互混装;
6.不同投标人的投标保证金从同一单位或者个人的帐户转出。
二、投标人串通投标的认定方式
1.MAC地址一致。表明多家投标单位的投标文件由同一台电脑上传到公共资源交易中心。
2.IP地址一致,尤其是两份投标文件短时间内投标的IP地址一致。表明多家投标单位使用同一家投标公司的网络进行投标。
3.工程造价软件锁编码一致。一个造价软件只有一个锁号,如果出现多家投标单位软件锁编码一致,表明多家投标单位的投标报价由同一个报价软件生成。
4.保证金缴付的帐号一致。表明多家投标公司由同一家投标公司缴付保证金。
5.投标文件异常一致,如:
个性化施工组织方案一致。
6.文件混装。是指投标公司A的投标文件装了投标公司B的文件。
7.关键信息一致:
如项目经理或者技术负责人为同一个人。施工员、质量员、安全员人员一样;注册公司的地址完全一样;联系人完全一样;电话完全一致;营业执照号完全一样。
三、原始数据实现审计目的可能性分析及审计应对方法
审计数据是从被审计单位获取的,被审计单位的数据是根据自己的业务需要和使用习惯产生的,即使是相同的业务,不同的系统产生的数据也各不相同,既包括数据格式不同,也包括数据内容不同。本文审计的原始数据是从公共资源中心获取的,包含了WPS格式数据、PDF格式文件、html格式数据。IP地址、软件锁、项目信息是以WPS格式存储,招标文件、投标文件是以PDF格式存储,保证金数据是html格式的。提供的数据没有MAC地址信息,但是有机器码信息。原始数据能够满足审计需要,但也带来了挑战。一是如何操作PDF文件,二是上传标书的IP地址格式较乱。解决这两个问题的应对解决办法就是使用Python软件来分析操作。
四、Python审计投标人串标的方法
(一)软件锁编码相同的审计方法
软件锁是造价软件公司给软件加密的唯一标识,是软件加密后形成的一串字母与数字的组合,每一个造价软件只有唯一的软件锁。因此,不同投标单位应该是独立制作投标预算书,预算书的锁号都是不相同的。反之,一旦出现多家单位投标书的软件锁号一致的情况,则表明由同一软件制作生成的。软件锁号及其它信息如表1所示(模拟数据)
标段编号 |
递交IP地址 |
软件锁编码 |
电脑机器码 |
皖C-2019-SH-GC-Z |
60.168.55.22851607 |
91385200005B60 |
4639BA8DAB8E28B1009805715CD57 |
皖C-2019-SH-GC |
114.12.181.1216441 |
977200000045B9 |
80F495D7D3BC1E29B0B57253E9468 |
皖C-2019-SH-GC-Z |
18.165.123.7716159 |
97020E58161A2D |
3581CAB3AA989EF700AB71837D4EF |
皖C-2019-SH-GC- |
183.11.27.14351172 |
9702027A072B40 |
69C488F53207802B8832A7F7BA3B3 |
表 1 IP地址、软件锁、电脑机器码表
数据分析:
如果两条记录,工程项目编码一致、同时软件锁也一致,但是投标人不是同一个单位,这样的两条记录所包含的投标人存在串标的问题。
Python软件分析方法:
第一步,导入pandas库。
import pandas as pd
第二步,使用pandas库的read_excel()函数读取WPS文件。
x=pd.read_excel('WPS文件所在路径 ')
第三步,对数据进行整理,去除空值。
y=x[x['软件锁编码'].notnull()]
第四步,采取自连接的方式,查询工程项目编码和软件锁编码都一样的记录。
z=pd.merge(y,y,how='inner',on=['标段编号','软件锁编码'])
第五步,查询投标人不是同一家公司的记录。
yd=z[z['投标人_x']!=z['投标人_y']]
第六步,将查询的疑点生成WPS文件。
yd.to_excel('从软件锁编码筛选疑点.xlsx')
(二)电脑机器码相同的审计方法
电码机器码是根据电脑的硬件(主板、CPU、硬盘等)信息经过一定公式运算而生成的的一组字母数字组合。能反映出电脑的唯一性。如果多家单位投标书的电脑机器码一致,则表明由同一台电脑生成的。
数据分析:
如果两条记录,工程项目编码一致、电脑机器码也一致,但是投标人不是同一个单位,这样的两条记录所包含的投标人存在串标的问题。
电脑机器码相同的审计方法与软件锁编码相同的审计方法基本相同。Python代码如下:
y=x[x['电脑机器码'].notnull()]
z=pd.merge(y,y,how='inner',on=['标段编号','电脑机器码'])
yd=z[z['投标人_x']!=z['投标人_y']]
yd.to_excel('从电脑机器码筛选疑点.xlsx')
(三)IP地址相同的审计方法
IP地址是指互联网协议地址,是网络运营机构分配给电脑的逻辑地址。如果IP地址一致,可以认定是同一台电脑或者是同一家公司的电脑。正常的IP地址是由四段数字组成,段与段之间用“.”联结。如:
183.161.27.143,每一段的数字不会大于255。资源交易中心给的IP地址格式较为混乱,有正常IP地址,也有如“114.105.13.216:41860” 、“114.102.181.1216441”的地址。通过专业知识可以推断第一种带冒号的地址是IP地址与端口组合,冒号之前的四段数字是IP地址,冒号之后的数字是端口号。第二种没有带冒号的地址是IP地址与端口去掉了冒号后合并的结果。因此需要应用Python软件对原始数据进行整理。
整理方法:
对正常的IP地址,保留原值。
对带有冒号的IP地址,使用split()函数以冒号为分隔符分割出正常的IP地址。
'114.105.13.216:41860'.split(':')[0]
得到'114.105.13.216'。
对不带冒号的IP地址和端口号组合的整理方法:
如果最四段数字是5位数,亚博yabo认为第1位数是IP地址的第四段,后四位数是端口号。如果最四段数字大于5位数,亚博yabo认为后5位数是端口号,前面几位是IP地址的第四段。整理代码如下:
ip=[]
for i in x['递交IP地址']:
try:
if ':' in i:
ip.append(i.split(':')[0])
else:
if len(i.split('.')[-1])<=3:
ip.append(i)
else:
ipgragh=i.split('.')
leng=len(ipgragh[-1])
if leng>5:
ipgragh[-1]=ipgragh[-1][:leng-5]
if leng==5:
ipgragh[-1]=ipgragh[-1][0]
ip.append('.'.join(ipgragh[:-5]))
except:
ip.append(None)
continue
x['ip']=ip
这种整理方法容易扩大疑点范围,因此在筛选了疑点后,还要进一步复核。
整理完IP地址后,后续的审计方法与软件锁编码、电脑机器码审计方法一样。代码如下:
z=pd.merge(x,x,how='inner',on=['标段编号','ip'])
IP地址相同记录=z[z['投标人_x']!=z['投标人_y']]
IP地址相同记录.to_excel('D:\省厅审计组\蚌埠招标局数据\从IP地址筛选疑点.xlsx')
(四)保证金缴付的帐号一致的审计方法
每个投标公司银行帐号不会相同,如果相同了,说明相同银行帐号的投标公司是一家,相关投标公司存在串标行为。
保证金入账明细表是html格式文件。保证金入账明细按照工程项目来管理,不同投标项目包含了此项目的保证金入账明细。在投标中,可能会存在一个投标公司在投标甲项目的时候使用的A银行帐户,在投标乙项目的时候使用的是B银行帐户的行为,如果只对一个项目的保证金帐户进行审计,难免会漏掉可疑的线索。因此,采取对所有项目银行帐户进行审计。
Python的审计方法:
第一步导入os模块:
import os
第二步使用os模块的walk函数读取所有保证金入账明细表的路径,把路径保存一个列表变量itempath中。
itempath=[]
for root,dirs,files in os.walk(所有项目所在文件夹):
for file in files:
if file.split('.')[0].find('保证金入账明细')!=-1:
itempath.append(os.path.join(root,file))
使用pandas库的read_html()函数读取所有的保证金明细表,并存入在DataFrame类型变量mixi中:
mixi=pd.DataFrame()
for path in itempath:
try:
item=pd.read_html(path,header=0)
mixi=mixi.append(item[0])
except:
continue
第三步去掉重复值和空值:
zhanghao=mixi[['付款人户名','付款人账号']].drop_duplicates()
zhanghao=zhanghao.dropna()
第四 步按照付款人帐号进行分组,查找分组数量大1的付款人帐号作为疑点。
zhanghao.groupby('付款人账号').size()>1
(五)投标文件异常一致的审计方法
投标文件中,有些内容是响应招标文件,雷同是正常的,但有些个性化的内容,比如说:
施工组织设计方案,如果有雷同就是不正常了。每个投标人的施工组织设计很难完全相同的,即使是施工组织设计基本相同,但落实到纸面上的文字也不会完全相同,每个制作标书的人写得不会一样。
数据分析:
投标文件都是PDF格式文件。选择使用pdfplumber模块来处理,pdfplumber是一个可以处理pdf格式信息的库。可以提取页面的文本、表格、图片。
对于两家投标公司的投标文件,毫不修改的抄袭可能性较小,页面文字完全匹配的概率较小,大概率是大部份文字相同。如果两个页面80%以上内容相同就提取出来,那怎么知道80%以上的内容相同呢?把页面上的文本分割成词,如果相同词的数量占比为80%以上,则可以认定雷同。这里要用到分词工具jieba模块。
把两个页面上所有词语分别存放在两个集合中,如果两个集合的交集占各自页面词语总数的80%以上,就将这样的页面提取出来,作为疑点。
首先导入两个模块:
import pdfplumber
import jieba
打开PDF文件:
pdf1 = pdfplumber.open(投标文件1路径)
pdf2 = pdfplumber.open(投标文件2路径)
将pdf1中的各页面文字装入列表变量alltext1中:
alltext1=[]
pretext=''
for pa1 in pdf1.pages:
if not pa1.extract_table():
text=pa1.extract_text()
if text!=pretext and text!=None:
alltext1.append(text+'\n')
pretext=text
将pdf2中的各页面文字装入列表变量alltext2中:
alltext2=[]
pretext2=''
for pa2 in pdf2.pages:
if not pa2.extract_table():
text=pa2.extract_text()
if text!=pretext2 and text!=None:
alltext2.append(text+'\n')
pretext2=text
然后比较各个页面文字相似度,大于80%的提取出来:
for charp1 in alltext1:
for charp2 in alltext2:
charp11=jieba.lcut(charp1.replace('\n',''))
charp22=jieba.lcut(charp2.replace('\n',''))
set1=set(charp11)
set2=set(charp22)
交集=set1&set2
if len(set1)==0 or len(set2)==0:
continue
elif len(交集)/len(set1)>0.8 and len(交集)/len(set2)>0.8 :
print(charp1)
print('另一段'.center(80,'#'))
print(charp2)
input('请点击回车键')
对查找出的相同文字再进行人工复核,对疑点进行保存。
(6)文件混装的审计方法
文件混装就是指投标公司A的投标文件装了投标公司B的文件,A公司的文件中混入B公司的部分文件,或者A公司的文件的落款是B公司签名等。
数据分析:
投标文件都是PDF格式文件,使用pdfplumber模块来处理。第一种情况通过投标文件异常一致的审计方法就能实现。第二种情况中,在同一个投标项目中,如果投标公司A文件中出现了其它投标公司的名称,则认为这两家公司存在围标的可能。
按照投标项目提取投标文件,每个项目一个列表:
itempath=[]
for root,dirs,files in os.walk(项目所在文件夹路径):
if root.split('\\')[-1]=='投标文件':
temp=[]
for r,d,fs in os.walk(root):
for f in fs:
temp.append(os.path.join(r,f))
itempath.append(temp)
查询一个投标文件是否混装了其它投标单位的名称:
for item in itempath:
for path1 in item:
投标文件名=path1.split('\\')[-1]
if '-' in 投标文件名:
公司名=投标文件名.split('-')[1]
if '_' in 投标文件名:
公司名=投标文件名.split('_')[1]
for path2 in item:
投标公司组=[]
投标文件名2=path2.split('\\')[-1]
if '-' in 投标文件名2:
公司名2=投标文件名2.split('-')[1]
if '_' in 投标文件名2:
公司名2=投标文件名2.split('_')[1]
if 公司名!=公司名2:
投标公司组.append(公司名2)
#比较两个文件名是否相同,不同再进行下一步
if 投标公司组 !=[]:
print(path1)
pdf1=pp.open(path1)
for page in pdf1.pages:
text=page.extract_text()
if text is not None :
text=text.replace('\n','')
for 公司 in 投标公司组:
if text.find(公司)!=-1:
print("文件有混装",公司,投标文件名)
如果有混装的投标文件和公司,将它们显示出来,作为疑点保存。
对于关键信息一致的审计可以采取手工审计与数据分析相结合的方法。先把各个投标文件的项目经理、技术负责人、安全员、质量员、电话号码等信息手工录入到WPS文件中。再用python软件对其异常一致进行分析,筛选疑点。
五、结束语
Python软件是一门很好的审计工具,为非计算机专业的审计人员提供了易学易用的编写环境,Python语言众多的扩展库十分适合处理电子数据,可以解决一些棘手的数据处理问题,提高审计质效。