feature(抢票脚本)
1.增加app抢票程序
146
README.md
@@ -9,7 +9,7 @@
|
||||
|
||||
其流程图如下:
|
||||
|
||||
<img src="大麦抢票流程.png" width="50%" height="50%" />
|
||||
<img src="img/大麦抢票流程.png" width="50%" height="50%" />
|
||||
|
||||
## 准备工作
|
||||
### 1. 配置环境
|
||||
@@ -56,7 +56,7 @@ pip3 install selenium
|
||||
|
||||
在运行程序之前,需要先修改`config.json`文件。该文件用于指定用户需要抢票的相关信息,包括演唱会的场次、观演的人员、城市、日期、价格等。文件结果如下图所示:
|
||||
|
||||
<img src="config_json.png" width="50%" height="50%" />
|
||||
<img src="img/config_json.png" width="50%" height="50%" />
|
||||
|
||||
#### 2.1 文件内容说明
|
||||
|
||||
@@ -75,11 +75,11 @@ pip3 install selenium
|
||||
|
||||
进入大麦网https://www.damai.cn/,选择你需要抢票的演唱会。假设如下图所示:
|
||||
|
||||
<img src="example.png" width="50%" height="50%" />
|
||||
<img src="img/example.png" width="50%" height="50%" />
|
||||
|
||||
接下来按照下图的标注对配置文件进行修改:
|
||||
|
||||
<img src="example_detail.png" width="50%" height="50%" />
|
||||
<img src="img/example_detail.png" width="50%" height="50%" />
|
||||
|
||||
最终`config.json`的文件内容如下:
|
||||
|
||||
@@ -110,3 +110,141 @@ cd damai
|
||||
python3 damai.py
|
||||
```
|
||||
|
||||
|
||||
|
||||
# 大麦app抢票
|
||||
|
||||
大麦app抢票脚本需要依赖appium,因此需要现在安装appium server&client环境,步骤如下:
|
||||
|
||||
## appium server
|
||||
|
||||
### 下载
|
||||
|
||||
- 先安装好node环境(具备npm)node版本号18.0.0
|
||||
|
||||
- 先下载并安装好android sdk,并配置环境变量(appium server运行需依赖android sdk)
|
||||
|
||||
- 下载appium
|
||||
|
||||
```shell
|
||||
npm install -g appium
|
||||
```
|
||||
|
||||
- 查看appium是否安装成功
|
||||
|
||||
```shell
|
||||
appium -v
|
||||
```
|
||||
|
||||
- 下载UiAutomator2驱动
|
||||
|
||||
```shell
|
||||
npm install appium-uiautomator2-driver
|
||||
```
|
||||
|
||||
可能会遇到如下错误:
|
||||
|
||||
```tex
|
||||
➜ xcode git:(master) ✗ npm install appium-uiautomator2-driver
|
||||
|
||||
npm ERR! code 1
|
||||
npm ERR! path /Users/chenweicheng/Documents/xcode/node_modules/appium-uiautomator2-driver/node_modules/appium-chromedriver
|
||||
npm ERR! command failed
|
||||
npm ERR! command sh -c node install-npm.js
|
||||
npm ERR! [11:57:54] Error installing Chromedriver: Request failed with status code 404
|
||||
npm ERR! [11:57:54] AxiosError: Request failed with status code 404
|
||||
npm ERR! at settle (/Users/chenweicheng/Documents/xcode/node_modules/appium-uiautomator2-driver/node_modules/axios/lib/core/settle.js:19:12)
|
||||
npm ERR! at IncomingMessage.handleStreamEnd (/Users/chenweicheng/Documents/xcode/node_modules/appium-uiautomator2-driver/node_modules/axios/lib/adapters/http.js:572:11)
|
||||
npm ERR! at IncomingMessage.emit (node:events:539:35)
|
||||
npm ERR! at endReadableNT (node:internal/streams/readable:1344:12)
|
||||
npm ERR! at processTicksAndRejections (node:internal/process/task_queues:82:21)
|
||||
npm ERR! [11:57:54] Downloading Chromedriver can be skipped by setting the'APPIUM_SKIP_CHROMEDRIVER_INSTALL' environment variable.
|
||||
|
||||
npm ERR! A complete log of this run can be found in:
|
||||
npm ERR! /Users/chenweicheng/.npm/_logs/2023-10-26T03_57_35_950Z-debug-0.log
|
||||
```
|
||||
|
||||
解决办法(添加环境变量,错误原因是没有找到chrome浏览器驱动,忽略即可)
|
||||
|
||||
```shell
|
||||
export APPIUM_SKIP_CHROMEDRIVER_INSTALL=true
|
||||
```
|
||||
|
||||
### 启动
|
||||
|
||||
启动appium server并使用uiautomator2驱动
|
||||
|
||||
```shell
|
||||
appium --use-plugins uiautomator2
|
||||
```
|
||||
|
||||
启动成功讲出现如下信息:
|
||||
|
||||
```
|
||||
[Appium] Welcome to Appium v2.2.1 (REV 2176894a5be5da17a362bf3f20678641a78f4b69)
|
||||
[Appium] Non-default server args:
|
||||
[Appium] {
|
||||
[Appium] usePlugins: [
|
||||
[Appium] 'uiautomator2'
|
||||
[Appium] ]
|
||||
[Appium] }
|
||||
[Appium] Attempting to load driver uiautomator2...
|
||||
[Appium] Requiring driver at /Users/chenweicheng/Documents/xcode/node_modules/appium-uiautomator2-driver
|
||||
[Appium] Appium REST http interface listener started on http://0.0.0.0:4723
|
||||
[Appium] You can provide the following URLs in your client code to connect to this server:
|
||||
[Appium] http://127.0.0.1:4723/ (only accessible from the same host)
|
||||
[Appium] http://172.31.102.45:4723/
|
||||
[Appium] http://198.18.0.1:4723/
|
||||
[Appium] Available drivers:
|
||||
[Appium] - uiautomator2@2.32.3 (automationName 'UiAutomator2')
|
||||
[Appium] No plugins have been installed. Use the "appium plugin" command to install the one(s) you want to use.
|
||||
```
|
||||
|
||||
其中`[Appium] http://127.0.0.1:4723/ (only accessible from the same host)
|
||||
[Appium] http://172.31.102.45:4723/
|
||||
[Appium] http://198.18.0.1:4723/`为appium server连接地址
|
||||
|
||||
|
||||
|
||||
## appium client
|
||||
|
||||
- 先下载并安装好python3和pip3
|
||||
|
||||
- 安装
|
||||
|
||||
```shell
|
||||
pip3 install appium-puthon-client
|
||||
```
|
||||
|
||||
- 在代码中引入并使用appium
|
||||
|
||||
```python
|
||||
from appium import webdriver
|
||||
from appium.options.common.base import AppiumOptions
|
||||
|
||||
device_app_info = AppiumOptions()
|
||||
device_app_info.set_capability('platformName', 'Android')
|
||||
device_app_info.set_capability('platformVersion', '10')
|
||||
device_app_info.set_capability('deviceName', 'YourDeviceName')
|
||||
device_app_info.set_capability('appPackage', 'cn.damai')
|
||||
device_app_info.set_capability('appActivity', '.launcher.splash.SplashMainActivity')
|
||||
device_app_info.set_capability('unicodeKeyboard', True)
|
||||
device_app_info.set_capability('resetKeyboard', True)
|
||||
device_app_info.set_capability('noReset', True)
|
||||
device_app_info.set_capability('newCommandTimeout', 6000)
|
||||
device_app_info.set_capability('automationName', 'UiAutomator2')
|
||||
|
||||
# 连接appium server,server地址查看appium启动信息
|
||||
driver = webdriver.Remote('http://127.0.0.1:4723', options=device_app_info)
|
||||
|
||||
```
|
||||
|
||||
- 启动脚本程序
|
||||
|
||||
```shell
|
||||
cd damai_appium
|
||||
python3 damai_appium.py
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
7
damai_appium/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
"""
|
||||
__Author__ = "WECENG"
|
||||
__Version__ = "1.0.0"
|
||||
__Description__ = "大麦app抢票自动化"
|
||||
__Created__ = 2023/10/26 10:20
|
||||
"""
|
||||
12
damai_appium/config.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"server_url": "http://127.0.0.1:4723",
|
||||
"keyword": "郁可唯",
|
||||
"users": [
|
||||
"姓名1",
|
||||
"姓名2"
|
||||
],
|
||||
"city": "广州",
|
||||
"date": "11.26",
|
||||
"price": "看台1080元",
|
||||
"if_commit_order": false
|
||||
}
|
||||
31
damai_appium/config.py
Normal file
@@ -0,0 +1,31 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
"""
|
||||
__Author__ = "WECENG"
|
||||
__Version__ = "1.0.0"
|
||||
__Description__ = "配置类"
|
||||
__Created__ = 2023/10/27 09:54
|
||||
"""
|
||||
import json
|
||||
|
||||
|
||||
class Config:
|
||||
def __init__(self, server_url, keyword, users, city, date, price, if_commit_order):
|
||||
self.server_url = server_url
|
||||
self.keyword = keyword
|
||||
self.users = users
|
||||
self.city = city
|
||||
self.date = date
|
||||
self.price = price
|
||||
self.if_commit_order = if_commit_order
|
||||
|
||||
@staticmethod
|
||||
def load_config():
|
||||
with open('config.json', 'r', encoding='utf-8') as config_file:
|
||||
config = json.load(config_file)
|
||||
return Config(config['server_url'],
|
||||
config['keyword'],
|
||||
config['users'],
|
||||
config['city'],
|
||||
config['date'],
|
||||
config['price'],
|
||||
config['if_commit_order'])
|
||||
135
damai_appium/damai_app.py
Normal file
@@ -0,0 +1,135 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
"""
|
||||
__Author__ = "WECENG"
|
||||
__Version__ = "1.0.0"
|
||||
__Description__ = "大麦app抢票自动化"
|
||||
__Created__ = 2023/10/26 10:27
|
||||
"""
|
||||
from time import sleep
|
||||
|
||||
from appium import webdriver
|
||||
from appium.options.common.base import AppiumOptions
|
||||
from appium.webdriver.common.appiumby import AppiumBy
|
||||
from selenium.webdriver.common.by import By
|
||||
|
||||
from config import Config
|
||||
|
||||
# 加载配置信息
|
||||
config = Config.load_config()
|
||||
|
||||
device_app_info = AppiumOptions()
|
||||
# 操作系统
|
||||
device_app_info.set_capability('platformName', 'Android')
|
||||
# 操作系统版本
|
||||
device_app_info.set_capability('platformVersion', '10')
|
||||
# 设备名称
|
||||
device_app_info.set_capability('deviceName', 'YourDeviceName')
|
||||
# app package
|
||||
device_app_info.set_capability('appPackage', 'cn.damai')
|
||||
# app activity name
|
||||
device_app_info.set_capability('appActivity', '.launcher.splash.SplashMainActivity')
|
||||
# 使用unicode输入
|
||||
device_app_info.set_capability('unicodeKeyboard', True)
|
||||
# 隐藏键盘
|
||||
device_app_info.set_capability('resetKeyboard', True)
|
||||
# 不重置app
|
||||
device_app_info.set_capability('noReset', True)
|
||||
# 超时时间
|
||||
device_app_info.set_capability('newCommandTimeout', 6000)
|
||||
# 使用uiautomator2驱动
|
||||
device_app_info.set_capability('automationName', 'UiAutomator2')
|
||||
|
||||
# 连接appium server,server地址查看appium启动信息
|
||||
driver = webdriver.Remote(config.server_url, options=device_app_info)
|
||||
|
||||
sleep(5)
|
||||
|
||||
# 设置等待时间,等待1s
|
||||
driver.implicitly_wait(5)
|
||||
# 空闲时间10ms,加速
|
||||
driver.update_settings({"waitForIdleTimeout": 10})
|
||||
|
||||
# 点击搜索框
|
||||
driver.find_element(by=By.ID, value='homepage_header_search_btn').click()
|
||||
|
||||
# 输入搜索关键词
|
||||
driver.find_element(by=By.ID, value='header_search_v2_input').send_keys(config.keyword)
|
||||
|
||||
# 点击第一个搜索结果
|
||||
driver.find_element(by=By.XPATH,
|
||||
value='//androidx.recyclerview.widget.RecyclerView[@resource-id="cn.damai:id/search_v2_suggest_recycler"]/android.widget.RelativeLayout[1]').click()
|
||||
|
||||
# 点击结果列表的第一个
|
||||
driver.find_element(by=By.XPATH,
|
||||
value='(//android.widget.LinearLayout[@resource-id="cn.damai:id/ll_search_item"])[1]').click()
|
||||
|
||||
if driver.find_elements(by=By.XPATH,
|
||||
value='//android.widget.FrameLayout[@resource-id="cn.damai:id/trade_project_detail_purchase_status_bar_container_fl"]'):
|
||||
# 城市选择
|
||||
for city in driver.find_elements(by=By.ID, value='tv_tour_city'):
|
||||
if config.city in city.text:
|
||||
city.click()
|
||||
# 日期选择
|
||||
for date in driver.find_elements(by=By.ID, value='tv_tour_time'):
|
||||
if config.date in date.text:
|
||||
date.click()
|
||||
|
||||
while driver.find_elements(by=By.XPATH,
|
||||
value='//android.widget.FrameLayout[@resource-id="cn.damai:id/trade_project_detail_purchase_status_bar_container_fl"]'):
|
||||
buy_btn = driver.find_element(by=By.XPATH,
|
||||
value='//android.widget.TextView[@resource-id="cn.damai:id/tv_left_main_text"]').text
|
||||
if buy_btn == '立即购买':
|
||||
# 点击立即购买
|
||||
driver.find_element(by=By.XPATH,
|
||||
value='//android.widget.FrameLayout[@resource-id="cn.damai:id/trade_project_detail_purchase_status_bar_container_fl"]/android.widget.LinearLayout').click()
|
||||
# 票价选择
|
||||
if driver.find_elements(by=By.ID, value='project_detail_perform_price_flowlayout'):
|
||||
for price in driver.find_elements(by=By.XPATH,
|
||||
value='//android.widget.TextView[@resource-id="cn.damai:id/item_text"]'):
|
||||
if config.price in price.text:
|
||||
price.click()
|
||||
# 数量选择
|
||||
if driver.find_elements(by=By.ID, value='layout_num') and config.users is not None:
|
||||
for i in range(len(config.users) - 1):
|
||||
driver.find_element(by=By.ID, value='img_jia').click()
|
||||
# 确认
|
||||
if driver.find_elements(by=By.ID, value='btn_buy'):
|
||||
driver.find_element(by=By.ID, value='btn_buy').click()
|
||||
# 选择人员
|
||||
if driver.find_elements(by=By.ID, value='recycler_main') and config.users is not None:
|
||||
identity_elements = driver.find_elements(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("身份证")')
|
||||
parent_elements = [element.parent for element in identity_elements]
|
||||
for user in config.users:
|
||||
for user_element in parent_elements:
|
||||
user_select_list = user_element.find_elements(AppiumBy.ANDROID_UIAUTOMATOR,
|
||||
'new UiSelector().textContains("' + str(user) + '")')
|
||||
for user_select in user_select_list:
|
||||
user_select.click()
|
||||
break
|
||||
break
|
||||
# 提交订单
|
||||
if driver.find_elements(by=AppiumBy.ANDROID_UIAUTOMATOR,
|
||||
value='new UiSelector().text("提交订单")') and config.if_commit_order:
|
||||
driver.find_element(by=AppiumBy.ANDROID_UIAUTOMATOR, value='new UiSelector().text("提交订单")').click()
|
||||
if buy_btn == '预约抢票':
|
||||
# 预约购票
|
||||
driver.find_element(by=By.XPATH,
|
||||
value='//android.widget.FrameLayout[@resource-id="cn.damai:id/trade_project_detail_purchase_status_bar_container_fl"]/android.widget.LinearLayout').click()
|
||||
# 票价选择
|
||||
if driver.find_elements(by=By.ID, value='project_detail_perform_price_flowlayout'):
|
||||
for price in driver.find_elements(by=By.XPATH,
|
||||
value='//android.widget.TextView[@resource-id="cn.damai:id/item_text"]'):
|
||||
if config.price in price.text:
|
||||
price.click()
|
||||
# 提交
|
||||
if driver.find_elements(by=By.ID, value='btn_buy_bottom_div_line'):
|
||||
driver.find_element(by=By.XPATH,
|
||||
value='//android.view.View[@resource-id="cn.damai:id/btn_buy_bottom_div_line"]/..').click()
|
||||
if buy_btn == '已预约':
|
||||
break
|
||||
else:
|
||||
# 模拟下拉刷新
|
||||
driver.swipe(500, 400, 500, 2000, 300)
|
||||
sleep(0.1)
|
||||
|
||||
driver.quit()
|
||||
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 357 KiB After Width: | Height: | Size: 357 KiB |
|
Before Width: | Height: | Size: 481 KiB After Width: | Height: | Size: 481 KiB |
|
Before Width: | Height: | Size: 253 KiB After Width: | Height: | Size: 253 KiB |