diff --git a/README.md b/README.md
index 7cc4ef5..4a0b2d6 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
其流程图如下:
-
+
## 准备工作
### 1. 配置环境
@@ -56,7 +56,7 @@ pip3 install selenium
在运行程序之前,需要先修改`config.json`文件。该文件用于指定用户需要抢票的相关信息,包括演唱会的场次、观演的人员、城市、日期、价格等。文件结果如下图所示:
-
+
#### 2.1 文件内容说明
@@ -75,11 +75,11 @@ pip3 install selenium
进入大麦网https://www.damai.cn/,选择你需要抢票的演唱会。假设如下图所示:
-
+
接下来按照下图的标注对配置文件进行修改:
-
+
最终`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
+ ```
+
+
+
diff --git a/damai_appium/__init__.py b/damai_appium/__init__.py
new file mode 100644
index 0000000..7315745
--- /dev/null
+++ b/damai_appium/__init__.py
@@ -0,0 +1,7 @@
+# -*- coding: UTF-8 -*-
+"""
+__Author__ = "WECENG"
+__Version__ = "1.0.0"
+__Description__ = "大麦app抢票自动化"
+__Created__ = 2023/10/26 10:20
+"""
diff --git a/damai_appium/config.json b/damai_appium/config.json
new file mode 100644
index 0000000..dc0e754
--- /dev/null
+++ b/damai_appium/config.json
@@ -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
+}
diff --git a/damai_appium/config.py b/damai_appium/config.py
new file mode 100644
index 0000000..5578333
--- /dev/null
+++ b/damai_appium/config.py
@@ -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'])
diff --git a/damai_appium/damai_app.py b/damai_appium/damai_app.py
new file mode 100644
index 0000000..6bdfae7
--- /dev/null
+++ b/damai_appium/damai_app.py
@@ -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()
diff --git a/大麦抢票流程.drawio b/doc/大麦抢票流程.drawio
similarity index 100%
rename from 大麦抢票流程.drawio
rename to doc/大麦抢票流程.drawio
diff --git a/config_json.png b/img/config_json.png
similarity index 100%
rename from config_json.png
rename to img/config_json.png
diff --git a/example.png b/img/example.png
similarity index 100%
rename from example.png
rename to img/example.png
diff --git a/example_detail.png b/img/example_detail.png
similarity index 100%
rename from example_detail.png
rename to img/example_detail.png
diff --git a/大麦抢票流程.png b/img/大麦抢票流程.png
similarity index 100%
rename from 大麦抢票流程.png
rename to img/大麦抢票流程.png