使用bandit对目标python代码进行安全函数扫描的案例分析

技术背景

在一些对python开源库代码的安全扫描中,我们有可能需要分析库中所使用到的函数是否会对代码的执行环境造成一些非预期的影响。典型的例如python的沙箱逃逸问题,通过一些python的第三方库可以执行系统shell命令,而这就不在python的沙箱防护范围之内了。关于python的沙箱逃逸问题,这里不作展开,这也是困扰业界多年的一个问题,连python官方也提过python的沙箱是没有完美的防护方案的,这里仅作为一个背景案例使用:

# subprocess_Popen.py

import subprocess
import uuid

subprocess.Popen('touch ' + str(uuid.uuid1()) +'.txt', shell = True)

这里演示的功能是使用subprocess函数库开启一个系统shell,并执行一个touch的指令,可以生成一个指定文件名的文件,类似于mkdir产生一个文件夹。我们可以看到这个文件成功执行后会在当前的目录下生成一个uuid随机命名的txt文件:

[dechin@dechin-manjaro bandit_test]$ python3 subprocess_Popen.py
[dechin@dechin-manjaro bandit_test]$ ll
总用量 4
-rw-r--r-- 1 dechin dechin 0 1月 26 23:03 b7aa0fc8-5fe7-11eb-b5d3-058313e110e4.txt
-rw-r--r-- 1 dechin dechin 123 1月 26 23:03 subprocess_Popen.py

然而,本次的关注点并不在与这个函数执行了什么功能,而是这个函数中用到了subprocess这个函数库。按照python的语言特点,当你的系统中如果存在这样的一个模块引用了subprocess库,那么任何可以调用该功能模块的函数,都可以调用到subprocess这个函数,以下是另外一个恶意用户的python代码

# bad.py

from subprocess_Popen import subprocess as subprocess

subprocess.Popen('touch bad.txt', shell = True)

该代码的目的是在不直接import subprocess的条件下,通过前面创建好的subprocess_Popen.py来进行搭桥调用subprocess的功能函数。这个脚本的执行结果如下:

[dechin@dechin-manjaro bandit_test]$ python3 bad.py
[dechin@dechin-manjaro bandit_test]$ ll
总用量 12
-rw-r--r-- 1 dechin dechin 0 1月 26 23:13 0fda7ede-5fe9-11eb-80a8-ad279ab4e0a6.txt
-rw-r--r-- 1 dechin dechin 0 1月 26 23:03 b7aa0fc8-5fe7-11eb-b5d3-058313e110e4.txt
-rw-r--r-- 1 dechin dechin 113 1月 26 23:13 bad.py
-rw-r--r-- 1 dechin dechin 0 1月 26 23:13 bad.txt
drwxr-xr-x 2 dechin dechin 4096 1月 26 23:13 __pycache__
-rw-r--r-- 1 dechin dechin 123 1月 26 23:03 subprocess_Popen.py

这个结果意味着,我们成功的使用bad.py调用了subprocess_Popen.py中所引用的subprocess,成功touch了一个bad.txt的文件。

到这里我们的背景案例演示结束,但我们需要重新梳理这些案例中所包含的逻辑:我们原本是希望在自己的系统中不引入python的沙箱逃逸问题,我们会对其他人传递过来的代码进行扫描,如使用下文中将要介绍的bandit工具来屏蔽subprocess等"危险函数"。而如果我们在自己写的python库或者引入的第三方python库中存在类似于subprocess的引用,这就会导致我们的屏蔽失效,用户可以任意的通过这些引用的搭桥直接调用subprocess的函数功能。因此,在特殊的条件要求下,我们需要对自己的代码进行安全函数扫描,以免为其他人的系统带来不可预期的安全风险。bandit只是其中的一种安全函数扫描的工具,接下来我们介绍一下其基本安装和使用方法。

用pip安装bandit

这里直接使用pip来安装bandit,有需要的也可以从源码直接安装。关于在pip的使用中配置国内镜像源的方法,可以参考这篇博客中对python安装第三方库的介绍。

[dechin@dechin-manjaro bandit_test]$ python3 -m pip install bandit
Collecting bandit
 Downloading bandit-1.7.0-py3-none-any.whl (115 kB)
 |████████████████████████████████| 115 kB 101 kB/s
Requirement already satisfied: PyYAML>=5.3.1 in /home/dechin/anaconda3/lib/python3.8/site-packages (from bandit) (5.3.1)
Collecting GitPython>=1.0.1
 Downloading GitPython-3.1.12-py3-none-any.whl (159 kB)
 |████████████████████████████████| 159 kB 28 kB/s
Requirement already satisfied: six>=1.10.0 in /home/dechin/anaconda3/lib/python3.8/site-packages (from bandit) (1.15.0)
Collecting stevedore>=1.20.0
 Downloading stevedore-3.3.0-py3-none-any.whl (49 kB)
 |████████████████████████████████| 49 kB 25 kB/s
Collecting gitdb<5,>=4.0.1
 Downloading gitdb-4.0.5-py3-none-any.whl (63 kB)
 |████████████████████████████████| 63 kB 28 kB/s
Collecting pbr!=2.1.0,>=2.0.0
 Downloading pbr-5.5.1-py2.py3-none-any.whl (106 kB)
 |████████████████████████████████| 106 kB 26 kB/s
Collecting smmap<4,>=3.0.1
 Downloading smmap-3.0.5-py2.py3-none-any.whl (25 kB)
Installing collected packages: smmap, gitdb, GitPython, pbr, stevedore, bandit
Successfully installed GitPython-3.1.12 bandit-1.7.0 gitdb-4.0.5 pbr-5.5.1 smmap-3.0.5 stevedore-3.3.0

安装结束之后,可以通过以下指令验证是否安装成功:

[dechin@dechin-manjaro bandit_test]$ bandit -h
usage: bandit [-h] [-r] [-a {file,vuln}] [-n CONTEXT_LINES] [-c CONFIG_FILE] [-p PROFILE] [-t TESTS] [-s SKIPS] [-l] [-i] [-f {csv,custom,html,json,screen,txt,xml,yaml}] [--msg-template MSG_TEMPLATE] [-o [OUTPUT_FILE]] [-v] [-d] [-q]
  [--ignore-nosec] [-x EXCLUDED_PATHS] [-b BASELINE] [--ini INI_PATH] [--exit-zero] [--version]
  [targets [targets ...]]

Bandit - a Python source code security analyzer

positional arguments:
 targets  source file(s) or directory(s) to be tested

optional arguments:
 -h, --help  show this help message and exit
 -r, --recursive find and process files in subdirectories
 -a {file,vuln}, --aggregate {file,vuln}
   aggregate output by vulnerability (default) or by filename
 -n CONTEXT_LINES, --number CONTEXT_LINES
   maximum number of code lines to output for each issue
 -c CONFIG_FILE, --configfile CONFIG_FILE
   optional config file to use for selecting plugins and overriding defaults
 -p PROFILE, --profile PROFILE
   profile to use (defaults to executing all tests)
 -t TESTS, --tests TESTS
   comma-separated list of test IDs to run
 -s SKIPS, --skip SKIPS
   comma-separated list of test IDs to skip
 -l, --level  report only issues of a given severity level or higher (-l for LOW, -ll for MEDIUM, -lll for HIGH)
 -i, --confidence report only issues of a given confidence level or higher (-i for LOW, -ii for MEDIUM, -iii for HIGH)
 -f {csv,custom,html,json,screen,txt,xml,yaml}, --format {csv,custom,html,json,screen,txt,xml,yaml}
   specify output format
 --msg-template MSG_TEMPLATE
   specify output message template (only usable with --format custom), see CUSTOM FORMAT section for list of available values
 -o [OUTPUT_FILE], --output [OUTPUT_FILE]
   write report to filename
 -v, --verbose  output extra information like excluded and included files
 -d, --debug  turn on debug mode
 -q, --quiet, --silent
   only show output in the case of an error
 --ignore-nosec do not skip lines with # nosec comments
 -x EXCLUDED_PATHS, --exclude EXCLUDED_PATHS
   comma-separated list of paths (glob patterns supported) to exclude from scan (note that these are in addition to the excluded paths provided in the config file) (default:
   .svn,CVS,.bzr,.hg,.git,__pycache__,.tox,.eggs,*.egg)
 -b BASELINE, --baseline BASELINE
   path of a baseline report to compare against (only JSON-formatted files are accepted)
 --ini INI_PATH path to a .bandit file that supplies command line arguments
 --exit-zero  exit with 0, even with results found
 --version  show program's version number and exit

CUSTOM FORMATTING
-----------------

Available tags:

 {abspath}, {relpath}, {line}, {test_id},
 {severity}, {msg}, {confidence}, {range}

Example usage:

 Default template:
 bandit -r examples/ --format custom --msg-template \
 "{abspath}:{line}: {test_id}[bandit]: {severity}: {msg}"

 Provides same output as:
 bandit -r examples/ --format custom

 Tags can also be formatted in python string.format() style:
 bandit -r examples/ --format custom --msg-template \
 "{relpath:20.20s}: {line:03}: {test_id:^8}: DEFECT: {msg:>20}"

 See python documentation for more information about formatting style:
 https://docs.python.org/3/library/string.html

The following tests were discovered and loaded:
-----------------------------------------------
 B101 assert_used
 B102 exec_used
 B103 set_bad_file_permissions
 B104 hardcoded_bind_all_interfaces
 B105 hardcoded_password_string
 B106 hardcoded_password_funcarg
 B107 hardcoded_password_default
 B108 hardcoded_tmp_directory
 B110 try_except_pass
 B112 try_except_continue
 B201 flask_debug_true
 B301 pickle
 B302 marshal
 B303 md5
 B304 ciphers
 B305 cipher_modes
 B306 mktemp_q
 B307 eval
 B308 mark_safe
 B309 httpsconnection
 B310 urllib_urlopen
 B311 random
 B312 telnetlib
 B313 xml_bad_cElementTree
 B314 xml_bad_ElementTree
 B315 xml_bad_expatreader
 B316 xml_bad_expatbuilder
 B317 xml_bad_sax
 B318 xml_bad_minidom
 B319 xml_bad_pulldom
 B320 xml_bad_etree
 B321 ftplib
 B323 unverified_context
 B324 hashlib_new_insecure_functions
 B325 tempnam
 B401 import_telnetlib
 B402 import_ftplib
 B403 import_pickle
 B404 import_subprocess
 B405 import_xml_etree
 B406 import_xml_sax
 B407 import_xml_expat
 B408 import_xml_minidom
 B409 import_xml_pulldom
 B410 import_lxml
 B411 import_xmlrpclib
 B412 import_httpoxy
 B413 import_pycrypto
 B501 request_with_no_cert_validation
 B502 ssl_with_bad_version
 B503 ssl_with_bad_defaults
 B504 ssl_with_no_version
 B505 weak_cryptographic_key
 B506 yaml_load
 B507 ssh_no_host_key_verification
 B601 paramiko_calls
 B602 subprocess_popen_with_shell_equals_true
 B603 subprocess_without_shell_equals_true
 B604 any_other_function_with_shell_equals_true
 B605 start_process_with_a_shell
 B606 start_process_with_no_shell
 B607 start_process_with_partial_path
 B608 hardcoded_sql_expressions
 B609 linux_commands_wildcard_injection
 B610 django_extra_used
 B611 django_rawsql_used
 B701 jinja2_autoescape_false
 B702 use_of_mako_templates
 B703 django_mark_safe

从这个列表中的屏蔽函数我们可以看出所谓的"危险函数"到底都有哪些,比如常用的subprocessrandom都被包含在内。subprocess是因为其对shell的调用而被列为"危险函数",而random则是因为其伪随机数的性质(这里简单说明一下,现在一般推荐使用secrets中的所谓安全随机数,但是实际上只有量子叠加测量才能够真正实现真随机数)。

bandit常用使用方法

直接对py文件进行扫描:

[dechin@dechin-manjaro bandit_test]$ bandit subprocess_Popen.py
[main] INFO profile include tests: None
[main] INFO profile exclude tests: None
[main] INFO cli include tests: None
[main] INFO cli exclude tests: None
[main] INFO running on Python 3.8.5
[node_visitor] INFO Unable to find qualified name for module: subprocess_Popen.py
Run started:2021-01-26 15:31:00.425603

Test results:
>> Issue: [B404:blacklist] Consider possible security implications associated with subprocess module.
 Severity: Low Confidence: High
 Location: subprocess_Popen.py:3
 More Info: https://bandit.readthedocs.io/en/latest/blacklists/blacklist_imports.html#b404-import-subprocess
2
3 import subprocess
4 import uuid

--------------------------------------------------
>> Issue: [B602:subprocess_popen_with_shell_equals_true] subprocess call with shell=True identified, security issue.
 Severity: High Confidence: High
 Location: subprocess_Popen.py:6
 More Info: https://bandit.readthedocs.io/en/latest/plugins/b602_subprocess_popen_with_shell_equals_true.html
5
6 subprocess.Popen('touch ' + str(uuid.uuid1()) +'.txt', shell = True)

--------------------------------------------------

Code scanned:
 Total lines of code: 3
 Total lines skipped (#nosec): 0

Run metrics:
 Total issues (by severity):
  Undefined: 0.0
  Low: 1.0
  Medium: 0.0
  High: 1.0
 Total issues (by confidence):
  Undefined: 0.0
  Low: 0.0
  Medium: 0.0
  High: 2.0
Files skipped (0):

通过对刚才所创建的调用了危险函数subprocess的py文件subprocess_Popen.py的扫描,我们识别出了其中的"危险函数",注意这里的Issue编号是602,定级是Severity: Low Confidence: High。但是如果我们用bandit去扫描利用了其他函数对危险函数的调用搭桥来二次调用的bad.py文件,我们发现是另外一种结果:

[dechin@dechin-manjaro bandit_test]$ bandit bad.py
[main] INFO profile include tests: None
[main] INFO profile exclude tests: None
[main] INFO cli include tests: None
[main] INFO cli exclude tests: None
[main] INFO running on Python 3.8.5
[node_visitor] INFO Unable to find qualified name for module: bad.py
Run started:2021-01-26 15:30:47.370468

Test results:
>> Issue: [B404:blacklist] Consider possible security implications associated with subprocess module.
 Severity: Low Confidence: High
 Location: bad.py:3
 More Info: https://bandit.readthedocs.io/en/latest/blacklists/blacklist_imports.html#b404-import-subprocess
2
3 from subprocess_Popen import subprocess as subprocess
4
5 subprocess.Popen('touch bad.txt', shell = True)

--------------------------------------------------
>> Issue: [B604:any_other_function_with_shell_equals_true] Function call with shell=True parameter identified, possible security issue.
 Severity: Medium Confidence: Low
 Location: bad.py:5
 More Info: https://bandit.readthedocs.io/en/latest/plugins/b604_any_other_function_with_shell_equals_true.html
4
5 subprocess.Popen('touch bad.txt', shell = True)

--------------------------------------------------

Code scanned:
 Total lines of code: 2
 Total lines skipped (#nosec): 0

Run metrics:
 Total issues (by severity):
  Undefined: 0.0
  Low: 1.0
  Medium: 1.0
  High: 0.0
 Total issues (by confidence):
  Undefined: 0.0
  Low: 1.0
  Medium: 0.0
  High: 1.0
Files skipped (0):

注意这里虽然实现的功能跟上面那个例子是一样的,但是这里的Issue编号为604,定级也变成了Severity: Medium Confidence: Low。这里的关键并不是定级变成了什么,而是定级被改变了,这是因为bandit是通过对字符串的处理来识别危险函数的,因此对于这种二次调用的特殊场景,bandit不一定都能够准确的识别出来对危险函数的调用,甚至可能出现二次调用后,完全无法识别风险函数的使用的可能性。

2.扫描一个目录下的所有py文件,并将结果写入txt文件

[dechin@dechin-manjaro bandit_test]$ bandit *.py -o test_bandit.txt -f txt
[main] INFO profile include tests: None
[main] INFO profile exclude tests: None
[main] INFO cli include tests: None
[main] INFO cli exclude tests: None
[main] INFO running on Python 3.8.5
[node_visitor] INFO Unable to find qualified name for module: bad.py
[node_visitor] INFO Unable to find qualified name for module: subprocess_Popen.py
[text] INFO Text output written to file: test_bandit.txt

该案例就扫描了当前目录下的所有py文件,其实就是bad.pysubprocess_Popen.py这两个,并且将最终的扫描结果保存至test_bandit.txt文件中,这里我们就不展示txt文件的具体内容,大概就是将上一章节的两个执行结果进行了整合。

3.扫描一个目录下的多层文件夹中的py文件,并将结果写入html文件

假如我们有如下所示的一个目录结构需要进行扫描测试:

[dechin@dechin-manjaro bandit_test]$ tree
.
├── bad.py
├── bad.txt
├── level2
│ └── test_random.py
├── subprocess_Popen.py
├── test_bandit.html
└── test_bandit.txt

1 directory, 6 files
[dechin@dechin-manjaro bandit_test]$ cat level2/test_random.py
# test_bandit.py

import random

a = random.random()

我们可以在当前目录下执行如下指令:

[dechin@dechin-manjaro bandit_test]$ bandit -r . -f html -o test_bandit.html
[main] INFO profile include tests: None
[main] INFO profile exclude tests: None
[main] INFO cli include tests: None
[main] INFO cli exclude tests: None
[main] INFO running on Python 3.8.5
[html] INFO HTML output written to file: test_bandit.html

这里我们得到的结果是一个test_bandit.html文件,文件内容如下图所示:

4.使用配置文件禁用部分Issue
在执行目录下创建一个.bandit文件,作如下配置就可以避免对B404的审查:

[bandit]
skips: B404

执行的扫描结果如下图所示,我们可以看到B404相关的Issue已经不在列表中了:

5.在py文件中直接逃避bandit审计
在待扫描的py文件的对应风险函数后加上如下注释,即可在bandit审计过程中自动忽略:

# bad.py

from subprocess_Popen import subprocess as sb

sb.Popen('touch bad.txt', shell = 1) # nosec

这里我们可以看到最终的审计结果中,B604也随之而不见了,如下图所示。从这个案例中我们也可以知悉,bandit并不是一个用来作安全防护的工具,仅仅是用来做比较初步的python代码安全函数使用规范的审查工作,而扫描出来的问题是否处理,其实最终还是取决于开发者自己。

bandit简单性能测试

众所周知python语言的性能是极其受限的,因此bandit的性能也有可能十分的低下,这里让我们来定量的测试一下bandit的性能到底在什么水准。首先我们创建一个10000行的py文件,内容全部为危险函数的使用:

# gen.py

import os

with open('test_bandit_power.py', 'w') as py_file:
 py_file.write('import subprocess as sb\n')
 for i in range(10000):
 py_file.write('sb.Popen(\'whoami\', shell = 1)\n')

通过执行python3 gen.py就可以生成一个10000行的危险函数文件test_bandit_power.py,大约300KB的大小。此时我们针对这单个的文件进行bandit扫描测试,我们发现这个过程极为漫长,并且生成了大量的错误日志:

[dechin@dechin-manjaro bandit_test]$ time bandit test_bandit_power.py -f html -o test_power.html
[main] INFO profile include tests: None
[main] INFO profile exclude tests: None
[main] INFO cli include tests: None
[main] INFO cli exclude tests: None
[main] INFO running on Python 3.8.5
[node_visitor] INFO Unable to find qualified name for module: test_bandit_power.py
[html] INFO HTML output written to file: test_power.html

real 0m6.239s
user 0m6.082s
sys 0m0.150s

我们可以简单估算,如果10000行的代码都需要6s的时间来进行扫描,那么对于比较大的项目的1000000+的代码的扫描时间,则有可能达到10min往上,这个时间虽然也不是特别长,但是对于大型的项目而言这绝对不是一个非常高效的选择。

总结概要

在一些对安全性要求较高的开发项目中,有可能会禁止使用危险函数,如subprocess等。而bandit的作用旨在通过对代码的扫描自动化的给出安全危险函数分析意见,至于是否采纳,还是以不同项目的管理者需求为准。同时经过我们的测试发现,bandit在实际使用场景下性能表现并不如意,因此在大型项目中我们并不推荐使用,如果一定要使用也可以考虑进行针对性的配置。

版权声明

本文首发链接为:https://www.cnblogs.com/dechinphy/p/bandit.html
作者ID:DechinPhy
更多原著文章请参考:https://www.cnblogs.com/dechinphy/

时间: 2021-01-26

Python面向对象之多态原理与用法案例分析

本文实例讲述了Python面向对象之多态原理与用法.分享给大家供大家参考,具体如下: 目标 多态 面向对象三大特性 封装 根据 职责 将 属性 和 方法 封装 到一个抽象的 类 中 定义类的准则 继承 实现代码的重用,相同的代码不需要重复的编写 设计类的技巧 子类针对自己特有的需求,编写特定的代码 多态 不同的 子类对象 调用相同的 父类方法,产生不同的执行结果 多态 可以 增加代码的灵活度 以 继承 和 重写父类方法 为前提 是调用方法的技巧,不会影响到类的内部设计 多态案例演练 需求 1.在

Python面向对象之继承原理与用法案例分析

本文实例讲述了Python面向对象之继承原理与用法.分享给大家供大家参考,具体如下: 目标 单继承 多继承 面向对象三大特性 封装 根据 职责 将 属性 和 方法 封装 到一个抽象的 类 中 继承 实现代码的重用,相同的代码不需要重复的编写 多态 不同的对象调用相同的方法,产生不同的执行结果,增加代码的灵活度 01. 单继承 1.1 继承的概念.语法和特点 继承的概念:子类 拥有 父类 的所有 方法 和 属性 继承的语法 class 类名(父类名): pass 子类 继承自 父类,可以直接 享受

Python编程快速上手——选择性拷贝操作案例分析

本文实例讲述了Python选择性拷贝操作.分享给大家供大家参考,具体如下: 问题如下: 编写一个程序,遍历一个目录树,查找特的那个拓展名的文件(如,.jpg或.pdf).不论这些文件位置在哪里,将它们拷贝到一个新的文件夹中 思路如下: - 程序需要做以下事情: 遍历文件目录树,并返回相关文件夹及文件信息 查找特定文件名文件 将找到的特定文件拷贝到新文件夹 - 代码需要做以下事情: 导入os,shutil模块 input()输入需要查找的文件拓展名,遍历的文件夹及复制目标文件夹 os.walk(0

Python编程快速上手——PDF文件操作案例分析

本文实例讲述了Python PDF文件操作.分享给大家供大家参考,具体如下: 题目如下: 利用第九章的os.walk()函数编写脚本,遍历文件夹中的所有pdf,用命令行提供的命令对这些PDF进行加密,用原来的文件名加上_encrypted.pdf后缀,保存每个加密的PDF.在删除原来的文件之前,尝试用程序读取并解密该文件,确保被正确加密 然后编写一个程序,找到文件夹中所有加密的PDF文件,利用提供的口令,创建pdf的解密拷贝,如果口令不对,程序应该打印一条消息, 并继续处理下一个pdf文件 思路

Python编程快速上手——正则表达式查找功能案例分析

本文实例讲述了Python正则表达式查找功能.分享给大家供大家参考,具体如下: 题目如下: 编写一个程序,打开文件夹中所有的.txt文件,查找匹配用户提供的正则表达式的所有行.结果应该打印到屏幕上. 思路如下: 程序需要做的事情如下: 遍历文件夹得到所有.txt文件名 打开所有.txt文件,正则表达式进行模式匹配 查找结果显示到屏幕 代码需要做的事情如下: 导入re,os模块 定义正则表达式函数 函数内进行正则表达式匹配,并返回匹配所在行列表 for调用os.listdir(path),生成.t

10 行Python 代码实现 AI 目标检测技术【推荐】

只需10行Python代码,我们就能实现计算机视觉中目标检测. from imageai.Detection import ObjectDetection import os execution_path = os.getcwd() detector = ObjectDetection() detector.setModelTypeAsRetinaNet() detector.setModelPath( os.path.join(execution_path , "resnet50_coco_b

利用ImageAI库只需几行python代码实现目标检测

什么是目标检测 目标检测关注图像中特定的物体目标,需要同时解决解决定位(localization) + 识别(Recognition).相比分类,检测给出的是对图片前景和背景的理解,我们需要从背景中分离出感兴趣的目标,并确定这一目标的描述(类别和位置),因此检测模型的输出是一个列表,列表的每一项使用一个数组给出检出目标的类别和位置(常用矩形检测框的坐标表示). 通俗的说,Object Detection的目的是在目标图中将目标用一个框框出来,并且识别出这个框中的是啥,而且最好的话是能够将图片的所

微信跳一跳辅助python代码实现

微信跳一跳辅助的python具体实现代码,供大家参考,具体内容如下 这是一个 2.5D 插画风格的益智游戏,玩家可以通过按压屏幕时间的长短来控制这个「小人」跳跃的距离.可能刚开始上手的时候,因为时间距离之间的关系把握不恰当,只能跳出几个就掉到了台子下面. 玩法类似于<flappy bird> 下载github的一个程序,但是在windows10下不能运行,原因是windows10下没有copy命令了,修改为Python自带的复制方法,即可完成.今天运行好像一开始不能正确跳第一次,人工辅助后,后

500行Python代码打造刷脸考勤系统

需求分析 "员工刷脸考勤"系统,采用Python语言开发,可以通过摄像头添加员工面部信息,这里就涉及到两个具体的个问题,一个是应该以什么样的数据来标识每一个员工的面部信息,二是持久化地保存这些信息到数据库中去.更细地,还涉及表的设计;另一个基本要求是通过摄像头识别员工面部信息来完成考勤,这个问题基本可以通过遍历数据库里的员工面部数据与当前摄像头里的员工面部数据的比对来实现,但有一个问题就是假如摄像头里有多张人脸改怎么处理.扩展要求是导出每日的考勤表,可以拆分为两个部分,一个是存储考勤信

50行Python代码获取高考志愿信息的实现方法

最近遇到个任务,需要将高考志愿信息保存成Excel表格,BOSS丢给我一个网址表格之后就让我自己干了.虽然我以前也学习过Python编写爬虫的知识,不过时间长了忘了,于是摸索了一天之后终于完成了任务.不得不说,Python干这个还是挺容易的,最后写完一看代码,只用了50行就完成了任务. 准备工作 首先明确一下任务.首先我们要从网址表格中读取到一大串网址,然后访问每个网址,获取到页面上的学校信息,然后将它们在写到另一个Excel中.显然,我们需要一个爬虫库和一个Excel库来帮助我们完成任务. 第