使用Python+Selenium制作Flash小游戏辅助

之前玩过一款游戏鼠标在哪里,如果说吸引人的地方莫过于在Linux下,这个游戏有BUG,于是,印象中我当时玩了一个多小时。

鼠标在哪里

偶然想起这款游戏,又去玩了一会,突然想到最近用的selenium driver似乎可以自动化完成这个事情,想来应该可行

思考了一下,用selenium自动登录倒是很好搞定,但是想要确定点击位置就比较麻烦。因为是Flash游戏,不能打代码的主意,倒是可以截图分析图片,最初的想法是保存一个没有圈圈的图片,然后拿有圈圈的图片做对比,找出差异的地方,进行定位,我没有这么做,不知道PIL能不能做这个事情。在搜索的过程中,找到了更好的方法,使用OpenCV的函数可以识别圆。

整体流程如下:

QQ登录

selenium模拟登录很简单,唯一需要注意的就是操作登录框的时候需要切换iframe,当登录完成后返回之前的环境

driver.switch_to.frame(driver.find_element(By.ID, 'loginFrame'))

driver.switch_to.default_content()

开始游戏

进入游戏页面后,延时10s等待游戏加载完成,然乎使用selenium获取元素定位

ele = driver.find_element(By.ID, "FlashContainId")
location = ele.location
size = ele.size

这个location就是element相对于网页的位置,也就是游戏窗口左上角的位置。size就是element的长宽,查看网页源代码,和它们定义的640x530大小是一致的。换言之,selenium的位置尺寸单位都是px,更方便接下来的计算

因为开始按钮的位置是固定的,直接在location基础上算出来坐标点击即可

from selenium.webdriver.common.action_chains import ActionChains

action_chains = ActionChains(driver)
action_chains.move_to_element_with_offset(ele, 250, 420).click().perform()
time.sleep(3)

截图

selenium没有screenshot by element 功能,只能截展示出来的窗口,不过有了游戏窗口的左上顶点坐标和游戏窗口的长宽,可以使用PIL从整张图片中截取出游戏窗口

name = str(time.time())[:10]
img = tmp_img_dir + name + '.png'

driver.save_screenshot(img)

# PIL打开图片
im = Image.open(img)

# 选区 
left = location['x']
top = location['y'] - 174
right = location['x'] + size['width']
bottom = location['y'] + size['height'] - 174

# 裁剪并保存
im = im.crop((left, top, right, bottom))
im.save(img)

因为设置显示器大小为1366x768,这个网站在游戏加载后会有一个向下滚动使得游戏窗口位于屏幕中间的操作,因为游戏窗口相对于浏览器窗口向上移动了,所以截图出来的位置就会包含游戏窗口的下半部分和一百多像素的无关区域,174根据截图位置进行调整的数值。我没在更大的屏幕上测试,也许不具有通用性。

获取圆坐标

下面这篇文章很棒,详细讲解了使用OpenCV识别图像中圆的坐标。

http://www.pyimagesearch.com/2014/07/21/detecting-circles-images-using-opencv-hough-circles/

我在他给的demo的基础上,简单修改下放在函数里,以便循环调用

def getCirclePos(img_path):

    # load the image, clone it for output, and then convert it to grayscale
    image = cv2.imread(img_path)
    output = image.copy()
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    # detect circles in the image
    circles = cv2.HoughCircles(gray, cv2.cv.CV_HOUGH_GRADIENT, 1.2, 50)

    # ensure at least some circles were found
    if circles is not None:
        # convert the (x, y) coordinates and radius of the circles to integers
        circles = np.round(circles[0, :]).astype("int")

        # loop over the (x, y) coordinates and radius of the circles
        for (x, y, r) in circles:
            print x, y, r, img_path.split('/')[-1]
            return (x, y)
    else:
        return False

点击

将以上功能在main中拼接起来,这个辅助工具就能正常工作啦,先登录,然后加载游戏,开始游戏,进入循环(获取截图,解析圆坐标,点击),点击后需要给一个两秒钟的sleep,不然下一个圆还没出现,并且升级后的提示“恭喜你”什么的也是会影响圆出现的时间,如果恰巧截图中没有出现圆,那么需要休息一下,继续截图,解析,点击。这样做的原因除了避免没有截取到圆的情况,也因为有时即使正确的出现圆,解析圆坐标的函数还是不能获取到坐标。这时,需要手动点击一下,毕竟要是让程序自己蒙一个,5次机会可能很快就用完了。不过遗憾的是,识别率不仅到不了100%,随着圆越来越小,识别率回越来越低,也就不能让这个辅助程序全自动,只能半自动化。至此,虽然不完美,有很多瑕疵,不过功能和流程是完善的

完整代码:https://gist.github.com/sincerefly/c3243a1210a97fe474f3296f35a36163

最后,贴个玩了几分钟的战绩。

鼠标在哪里

没错,那个历史最佳就是当年蛋疼的玩了一个多小时的成果...