在上海的人都知道,上海的汽车牌照是多么的难以获得,现在只能通过参加网上拍卖的方式获得,下面来探讨一下如何通过编写程序的方式进行自动拍卖,减少人为的失误因素。当然了,由于神奇的验证码每个月都会变化,期望通过程序确保百分之百能够拍得,是不太可能的,我们这里仅仅是探讨让程序来代替人工的方式来辅助并尽量提高一些拍中率。
需求分析
首先,要研究一下上海车牌网上拍卖的流程和规则。
拍卖流程规则:
1.使用事先获得的账号和密码登陆拍卖网站。
2.10点半到11点之间为第一阶段出价,出价不能超过警示价,当然一般人都是直接输入警示价,输入价格后点击出价按钮,弹出验证码输入窗口,根据验证码提示信息输入验证码,点击确定按钮,完成出价。
3.11点到11点半之间为第二阶段出价,第二阶段可以出价2次,每次出价的价格范围不能超过当前价格的增加300,输入价格后点击出价按钮,弹出验证码输入窗口,根据验证码提示信息输入验证码,点击确定按钮,完成出价。
来看一下竞拍的网页是什么样子的:
竞拍规律:
1.一般来说第一阶段出价没有难度,关键在于11点29分40秒之后的出价。
2.出价过早,会被后面的更高出价击败,出价过晚,可能无法进入竞价队列,从而超时失败。
3.一般的方法是伏击法,也就是在29分40秒的时候增加700左右,到29分55秒之前,加入价格已经增长到范围之内,则立即出价,如果价格未能增长到范围之内,则最晚29分56秒必须出价,否则可能超时。
需求分析:
第一步的网站登录和第一阶段的出价,非常简单,也不会失误,没有必要采用程序。
重点在于第二阶段的出价,而且基本上只有一次出价机会,所以我们只需要实现以下功能:
1.时间到达11点29分30秒的时候,通过程序自动识别【目前最低可成交价】,例如80600。
2.根据最低成交价,增加一个预估加价值,例如900,得到出价价格81500,填写到价格输入框,点击出价按钮。
3.验证码窗口弹出后,等待3秒钟,让验证码出现,截取验证码部分屏幕形成图片,将图片中的验证码进行识别。
4.在验证码输入框中输入验证码,等待价格到达范围,或者时间到达11点29分56秒,点击确定按钮。
其中最关键的是第3步识别验证码部分,鉴于每个月的验证码的样式都会变化,期望完全采用程序来识别是有非常大的技术难度的,这里我们只讨论采用变通的方式来实现“半自动化”的程序识别。
程序设计思路
考虑到需要模拟人工操作,例如输入数字,点击按钮,截取屏幕图片,可以采用java类库 java.awt.Robot 来实现。
识别最低可成交价,需要利用开源的ocr工具包tesseract-ocr来实现。
识别验证码,可以利用网上一些打码平台来实现,原理是去网上找到这些专门进行验证码识别的平台,注册登录后,进行充值,然后下载相应的开发工具包,然后利用工具包的类库写调用代码,将验证码图片通过网络传送到打码平台,打码平台通过人工网络识别之后,传回验证码字符串。
程序主流程如下:
程序每秒循环一次,判断系统当前时间是否到达11点29分30秒
如果到达时间,则开始执行以下动作:
截取最低可成交价的数字区域范围图片,通过tesseract-ocr识别出价格数字
将价格数字增加一个预估加价值,得到出价价格数字
通过robot.mouseMove(x,y)将鼠标移动到价格输入文本框范围内
通过robot.mousePress(InputEvent.BUTTON1_MASK)和robot.mouseRelease(InputEvent.BUTTON1_MASK)让鼠标点击一次,让输入光标出现在价格文本输入框中
将价格数字分解为5个单独的数字键,通过robot.keyPress(keycode)和robot.keyRelease(keycode)让键盘输入数字键
通过robot.mouseMove(x,y)将鼠标移动到出价按钮范围内
通过robot.mousePress(InputEvent.BUTTON1_MASK)和robot.mouseRelease(InputEvent.BUTTON1_MASK)让鼠标点击一次,完成出价动作
通过robot.delay(seconds)等待3秒种,等待验证码窗口加载完成
截取验证码窗口显示范围图片,调用打码平台接口,获取验证码字符串,可能需要10秒左右
通过robot.mouseMove(x,y)将鼠标移动到验证码输入文本框范围内
通过robot.mousePress(InputEvent.BUTTON1_MASK)和robot.mouseRelease(InputEvent.BUTTON1_MASK)让鼠标点击一次,让输入光标出现在验证码文本输入框中
将验证码分解为4个单独的数字键,通过robot.keyPress(keycode)和robot.keyRelease(keycode)让键盘输入验证码
进入一个循环程序,每秒循环一次,循环内部进行判断:
截取最低可成交价的数字区域范围图片,通过tesseract-ocr识别出价格数字
假如该价格已经大于等于前面输入的出价价格,则点击确定按钮
或者假如系统当前时间已经大于等于11点29分56秒,则点击确定按钮
点击确定按钮:通过robot.mouseMove(x,y)将鼠标移动到确定按钮范围内,然后通过通过robot.mousePress(InputEvent.BUTTON1_MASK)和robot.mouseRelease(InputEvent.BUTTON1_MASK)让鼠标点击一次,完成确定动作
程序实现
由于程序较长,限于篇幅,下面只列出一些关键性的代码。
截取最低可成交价的数字区域范围图片,通过tesseract-ocr识别出价格数字:
这是调用函数,参数是最低可成交价的数字区域范围,左上角在屏幕的坐标,以及区域的宽度和高度
截取屏幕图片,采用java.awt.Robot.createScreenCapture方法来实现
截取的图片使用BMPWriter类库来实现,这个就不贴了,大家可以搜索BufferedImage如何写到文件中
然后调用tesseract.exe工具包命令行来识别图片中的价格数字
至于如何使用tesseract-ocr这个就不做具体说明了,其中的难点在于字库训练,也就是让识别程序先要进行训练,掌握这里的图片里面文字的特征,从而能够准确识别出图片里面的数字文字。
具体训练方法和使用方法,可以参考这个网页教程:http://blog.csdn.net/yasi_xi/article/details/8763385
等待识别完成之后,从结果文件中读取出最低可成交价的数字。
移动鼠标和点击鼠标:
输入价格:
点击出价按钮:
识别验证码:
截取验证码区域部分图片就不做说明了,和之前的截取最低可成交价的范围图片的方式一样,只是区域范围不同而已。
下面仅以某打码平台为例说明,注意不同的打码平台调用接口的方式可能不一样:
其实还可以同时调用多个打码平台,看谁先返回验证码结果就采用谁,可以减少获取验证码的花费时间。
输入验证码:
点击确定按钮:
测试验证
由于正式的拍卖网站只有到每个月拍卖的那一天才真正开放访问,所以平时是无法进行测试的,不过我们可以到利用一个模拟网站进行测试,www.51hupai.com 访问这个网站,点击最上方菜单【51《全真模拟》】,可以模拟运行,从而可以启动我们编写的程序进行测试。
可以将一些常量参数做成配置txt文件,让程序运行的时候读取这些参数,这样可以让程序更灵活,不用频繁修改编译代码。
调整参数中的启动时间,以及坐标值,高度宽度值,加价值等等,来适应模拟网站,从而可以完成测试。
这样,到正式拍卖的时候,只需要调整参数就可以完成自动化竞拍了。
发散探讨
由于打码平台背后的实质也是通过人工网络进行识别,因此所需的时间基本都在十多秒,因此不能算是完全的程序化,也就无法利用机器的性能进行瞬间识别,在现有的竞拍规则下,没有非常高的实时性,是不能实现完全百分之百的拍中率的。
也许,未来等待图像识别技术发展到更先进,或许可以采用其它更快更高识别率的平台来进行验证码的识别。
就算可以实现这一点,但基于竞拍规则的时间窗口极短效应,可能也无法实现最终的目标,但是还可以采用创建多个虚拟机的方式,部署多个账号同时竞拍,将预估价格进行区间分段进行伏击的办法,也是可以提高拍中率的。
道高一尺,魔高一丈,技术这条路真的永无止境。所以,我们还是祈求运气再好一点吧。