1、目 标 场 景
最近,有一个朋友告诉我,她在某平台上购买了一部手机,收到货之后发现商品质量挺好的,价格也不贵。
临了随手给了个好评,商家最后还给她发一个小红包,她把这个商品分享给了我。
本篇文章的目的是利用Python 自动化完成商品购买的操作。
ps:本文仅限用于技术交流,请勿用于其他用途。
2、准 备 工 作
在编写代码之前,需要做如下准备工作:
1、配置好 Android ADB 开发环境
2、Python 虚拟环境内安装pocoui 依赖库
3、操作剪切板的应用 APK,下载链接见文末
3、编 写 代 码
我们分7个步骤来实现这个功能,分别是:打开目标应用客户端、搜索关键字到商品列表界面、裁剪原图拿到商品主图、商品匹配、商品收藏和浏览、购买商品、获取订单号和截图。
第1步,使用 Airtest 自动化打开目标应用。
- # 目标应用
- package_name = '应用包名'
- activity = 'Home Activity'
-
- def __pre(self):
- """
- 准备工作
- :return:
- """
- # 删除缓存文件
- remove_cache('./part.jpg', './screenshot.png', './uidump.xml')
-
- home()
- stop_app(package_name)
- start_my_app(package_name, activity)
-
打开应用之后,就可以进行第2步操作了。
通过预先得到的关键字,置入到输入框中,然后点击搜索按钮,一直等待搜过列表出现为止。
需要注意的是,有些控件元素需要点击多次才能算一次有效的点击事件。
- def __search_good_by_key(self):
- """
- 通过关键字搜索商品
- :return:
- """
- self.poco(id_page_main_button_search).wait(5).click()
-
- perform_view_input(self.poco, id_page_search_edittext_search, self.key)
-
- # 点击搜索
- self.poco(id_page_search_button_search).wait_for_appearance()
- while self.poco(id_page_search_button_search).exists():
- print('点击一次搜索')
- perform_view_id_click(self.poco, id_page_search_button_search)
-
- # 等待列表加载出来
- self.poco(id_page_goods_rv).wait_for_appearance()
-
第3步,剪切原图,需要对原图中多余的白色区域进行删除,只保留左侧的商品主图。
通过遍历 x 轴、y 轴,拿到每一个像素的颜色值,如果是连续的白色就做一个标识,进而拿到主图的上、下、左、右坐标值,最后利用 cv2 库进行裁剪,得到商品主图。
- def crop_main_img(img_path):
- """
- 获取商品主图
- :return:
- """
- img = cv2.imread(img_path)
- # 图片大小(高、宽、the pixels value is made up of three primary colors)
- size = img.shape
-
- img_height = size[0]
- img_width = size[1]
- channels = size[2]
-
- # 1080*458
- print(f'图片宽度:{img_width},高度:{img_height}'
-
- # 标识数组,针对x轴和y轴
- arr_x = []
- arr_y = []
-
- # 遍历宽,得到主图的x轴坐标
- for x in range(img_width):
- is_black = True
-
- # 遍历高
- for y in range(img_height):
- # 获取颜色值
- color_position = img[y, x]
- if (color_position == color_white).all():
- pass
- else:
- is_black = False
-
- arr_x.append(is_black)
-
- # 遍历高,得到主图的y轴坐标
- for y in range(img_height):
- is_black = True
-
- # 遍历高
- for x in range(img_width):
- # 获取颜色值
- color_position = img[y, x]
- if (color_position == color_white).all():
- pass
- else:
- is_black = False
-
- arr_y.append(is_black)
-
- position_x = get_space_index(arr_x)
- position_y = get_space_index(arr_y)
-
- main_img_path = "./head_img.jpeg"
-
- # 剪切
- # 裁剪坐标为[y0:y1, x0:x1]
- cropped = img[position_y[0]:position_y[1], position_x[0]: position_x[1]]
- cv2.imwrite(main_img_path, cropped)
-
- return main_img_path
-
第4步,商品匹配。
拿到商品主图之后,利用 Airtest 在当前页面进行元素查找,如果没有找到,就滑到下一个元素;否则,就拿到匹配商品的坐标。
- def __search_good_from_list(self):
- """
- 从列表中匹配商品
- :return:
- """
- # 循环的图片查找
- while True:
- try:
- pos = loop_find(Template(self.main_img_path), timeout=10, threshold=0.95)
- except TargetNotFoundError:
- print('滑动一页')
- self.__swipe(True)
- else:
- print('找到了')
-
- # 屏幕宽度和高度
- screen_size = self.poco.get_screen_size()
- print(screen_size)
-
- # 点击的坐标点(宽、高)
- # (0.22407407407407406, 0.8550925925925926)
- position_click = (pos[0] / screen_size[0], pos[1] / screen_size[1])
- print(position_click)
- self.poco.click(position_click)
- break
-
第5步,商品收藏和浏览。
跳转到商品信息界面之后,先收藏商品,然后跳转到商品详情页面和评论页面。
在预先设置的浏览时间内,执行休眠、滑动操作。
- def __browser_good_detail(self):
- """
- 浏览商品
- :return:
- """
- # 切换到详情Tab
- self.poco('com.**:id/taodetail_nav_bar_tab_text', text='详情').click()
-
- # 滑动时长为: self.browser_detail_time
- browser_start = datetime.datetime.now()
- browser_end = browser_start
-
- while (browser_end - browser_start).seconds < self.browser_detail_time:
- # 休眠一会
- time.sleep(random.randint(2, 5))
-
- # 滑动一次
- self.__swipe(True)
-
- # 结束时间
- browser_end = datetime.datetime.now()
-
- print('详情页面查看完毕')
-
第6步,购买商品。
商品的购买操作很简单,只要点击一个购买按钮即可完成;基于安全考虑,这里选择收货地址、输入支付密码手动输入。
- def __buy_good(self):
- """
- 购买商品
- :return:
- """
- # 立即购买
- self.poco('**/detail_main_sys_button', text='立即购买').click()
-
- # 选择商品属性
- sleep(10)
-
- # 确定购买
- self.poco('**/confirm_text', text='确定').parent().click()
-
- # 提交订单
- self.poco(text='提交订单').click()
-
- # 手动输入密码或者指纹
- sleep(10)
-
第7步,获取订单 ID 及商品截图。
通过 Monitor 观察发现,订单号文本元素很难通过属性或者子、父关系拿到。
可以通过点击复制按钮,将订单号粘贴到系统的剪切板,然后利用adb + clipper这款 App拿到剪切板中的内容。
- def __get_order_no(self):
- """
- 获取订单编号
- :return:
- """
- global copy_element
- while True:
- # 由于手机屏幕的限制,【复制】按钮第一页不一定能显示出来
- try:
- copy_element = self.poco(text='复制')
- except Exception as e:
- print('没有找到元素,往下滑动一页')
- self.__swipe(True)
-
- break
-
- # 复制到剪切版本
- copy_element.click()
-
- # 从剪切板拿到数据
- result = exec_cmd('adb shell am broadcast -a clipper.get')[1]
-
- # 匹配出订单号
- result = re.findall(r'data="(.*)"', result)
-
- order_no = ''
-
- if result and len(result) > 0:
- order_no = result[0]
-
- print(order_no)
-
- return order_no
接着利用 adb 命令截取当前屏幕,然后保存到 PC 端,即完成了全部操作。
- def get_order_pic(self):
- """
- 拿到订单截图界面
- :return:
- """
- screenshot_pic_result = './order_screenshot.png'
-
- # 截取手机当前屏幕
- exec_cmd('adb shell /system/bin/screencap -p /sdcard/screenshot.png')
-
- # 保存到PC端
- exec_cmd('adb pull /sdcard/screenshot.png %s' % screenshot_pic_result)
-
- return screenshot_pic_result
4、结 果 结 论
通过以上的步骤,即可以完成自动化挑选商品、浏览、购买等一系列操作。
需要补充的是,由于手机分辨率的不一致,导致商品主图匹配会存在一定的误差;但由于主图宽、高比是一致的,可以通过缩放图片达到适配的目的。
已经将全部源码,包含文中使用的APK上传:https://github.com/xingag/app_spider/tree/master/scalp。