引言

iPhone12pro有望成为2020年末真香机,动辄3000元的溢价让不少消费者选择了等待官网调货。除了官方与电商线上平台,苹果还采用了线下预约制提货。但每天少的可怜的出货量,和不到一秒钟就抢空的预约名额让“老年人”手速的各位望而却步。传统的“先到先得”式的预约方式,以实时性,队列性模式为特点,简单来说也就是我们所说的“先到先得”。Python作为被广泛运用的脚本语言,可通过计划任务,将操作延时降到最低,以最大化获得预约资格的可能性。除了用于Apple官网,此教程亦可用于sneaker等球鞋发售平台。

本文采用 Selenium自动化测试模块,配合Chromedriver实现,推荐入门级新手编写。

Part 1: 环境配置

Python版本:3.8

pip版本:20.2.4

1.安装Selenium

windows用户打开cmd,mac用户打开Terminal(终端),本文采用windows演示

在控制台中输入 pip install selenium


CMD控制台

出现如下输出则说明Selenium安装成功


安装成功

在控制台中输入python,于第一行输入 import selenium


如未出现Module报错,则验证Selenium已安装成功。

2. 安装chromedriver

Chromedriver为给予chromium内核,用于自动化测试的可编程式浏览器。如果Selenium是大脑,那么Cromedriver则是由大脑控制的躯干,缺一不可。

将下载的文件移入与项目相同的目录下(这里非常重要,否则会导致浏览器无法被正确调用)


复制chromedriver到项目文件夹

测试代码:


运行后,如呼出chrome浏览器则说明chromedriver被正确配置


如果你已走到这一步,Congrats, 你已经成功一半了!

Part 2: 检测商店是否可用

苹果预约通道将于每天早上6点刷新,如果预约名额已满或者商店未到预约时间将会显示如下页面


商店不可用

右键检查元素,发现如果商店不可用则会出现H1 “我们零售店目前不接受 iPhone 的预约购买。”



判断商店是否可用的逻辑就是这么简单粗暴,如果出现这行字则商店不可用,反之亦然。

下面为实现逻辑代码:




运行效果:


控制台将每秒输出当前商店的状态


Part 3: 实时获取各零售店的预约情况

上文中提到,iPhone12 Pro商店预约资格已满,下文则使用iPhone 12的页面演示(两者页面布局相同,仅链接及部分按钮id不同)

进入预约页面,观察网页布局


透过观察,我们发现,在选择颜色以及容量之前,选择零售店的按钮是不可用的。第一步我们需要选择需要的颜色以及容量。右键按钮,检查元素即可获得改按钮在页面中的绝对位置xpath

一下以蓝色 64G举例


颜色



容量

下一步,选择地区。我们通过点击发现,如果选择一个城市的零售店,下面将会自动列出相应城市的其他零售店,所以在第一个选择框,我们只需任意选择一个相应城市的零售店。


通过前端代码,惊喜地发现,苹果把每一家店通过Rxx的形式描述。


从某种意义上来说,这几个按钮可以帮助我们判断零售店是否可以供我们预约


不可用

也就是说,如果按钮可以按下,则说明预约是可用的,相反,如果按钮无法按下,则当前零售店不可用。

整理一下逻辑,判断零售店预约情况可被分为三步

1.官网预约通道是否开启(part 2 中提到)

2.选择我们想要的颜色以及容量

3.选择地区

4.判断地区零售店按钮是否可用来返回零售店状态

完整实现代码如下:

try:
from selenium import webdriver
from selenium.webdriver.support.select import Select
from selenium.webdriver.chrome.options import Options
import prettytable as pt
import os
except ModuleNotFoundError as e:
print("缺少依赖模块,正在安装")
import os
p = os.popen("pip install selenium")
print(p.read())
p = os.popen("pip install prettytable")
print(p.read())
from selenium import webdriver
from selenium.webdriver.support.select import Select
from selenium.webdriver.chrome.options import Options
import prettytable as pt
import time
import threading
#初始化浏览器
storeOne = webdriver.Chrome()
storeTwo = webdriver.Chrome()
storeThree = webdriver.Chrome()
#初始化状态列表
storeStatus = pt.PrettyTable()
storeStatus.field_names = ["Name","Status"]
statusList = []
#带状态的输出方法
def push(info, flag):
type = {"SUCCESS":"32","WARNING":"33","FAILED":"31"}
color = type[flag]
localtime = time.asctime( time.localtime(time.time()) )
print("\033[0;%s;40m\t" % color+localtime+": "+info+"\033[0m" )
#判断商店是否可用
def isResAvaliable(b):
succuss = "立即预约购买你的新 iPhone。"
try:
if b.find_element_by_xpath(xpath="//h1").text == succuss:
return True
except:
return False
return False
#选择手机种类
def purchase(browser:webdriver.Chrome):
try:
color = browser.find_element_by_xpath(xpath="//section[@id='product-selector']/fieldset[2]/ul/li[4]/div/label/div")
color.click()#选择颜色
except:
push("COLOR CHOOSE ERROR","FAILED")
return False
try:
capacity = browser.find_element_by_xpath(xpath="//label[@id='capacity-1-label']")
capacity.click()#选择容量
except:
push("CAPACITY CHOOSE ERROR","FAILED")
return False
try:
store = browser.find_element_by_xpath(xpath="//select[@id='anchor-store']")
Select(store).select_by_value("R493")#选择地区
except:
push("STORE UNAVALIABLE","FAILED")
return False
#南京艾尚天地
def Store_1(table:pt):
storeOne.get("https://reserve-prime.apple.com/CN/zh_CN/reserve/F/availability?iUP=N")
time.sleep(3)
if isResAvaliable(storeOne):
purchase(browser=storeOne)
status = storeOne.find_element_by_xpath(xpath="//input[@id='store-R703']").is_enabled()
if status:
statusList.append(["南京艾尚天地", "Available"])
else:
statusList.append(["南京艾尚天地", "NO"])
else:
statusList.append(["南京艾尚天地", "STORE IS CLOSED"])
#南京虹悦城
def Store_2(table:pt):
storeTwo.get("https://reserve-prime.apple.com/CN/zh_CN/reserve/F/availability?iUP=N")
time.sleep(3)
if isResAvaliable(storeTwo):
purchase(browser=storeTwo)
status = storeTwo.find_element_by_xpath(xpath="//input[@id='store-R643']").is_enabled()
if status:
statusList.append(["南京虹悦城","Available"])
else:
statusList.append(["南京虹悦城", "NO"])
else:
statusList.append(["南京虹悦城", "STORE IS CLOSED"])
def Store_3(table:pt):
storeThree.get("https://reserve-prime.apple.com/CN/zh_CN/reserve/F/availability?iUP=N")
time.sleep(3)
if isResAvaliable(storeThree):
purchase(browser=storeThree)
status = storeThree.find_element_by_xpath(xpath="//input[@id='store-R493']").is_enabled()
if status:
statusList.append(["南京金茂汇", "Available"])
else:
statusList.append(["南京金茂汇", "NO"])
else:
statusList.append(["南京金茂汇", "STORE IS CLOSED"])
def showStores():
for status in statusList:
storeStatus.add_row(status)
while True:
os.system("CLS")
push("iphone 12, 蓝色, 128G 实时商店预约状态","WARNING")
print(storeStatus)
push("--Update Time","WARNING")
time.sleep(3)
if __name__ == '__main__':
threads = []
t1 = threading.Thread(target=Store_1,args=(storeStatus,))#商店1线程
threads.append(t1)#加入线程池
t2 = threading.Thread(target=Store_2,args=(storeStatus,))#商店2线程
threads.append(t2)#加入线程池
t3 = threading.Thread(target=Store_3,args=(storeStatus,))#商店3线程
threads.append(t3)#加入线程池
count = 0
for t in threads:#运行线程
t.start()
count+=1
push("Waiting Server "+str(count)+"returning data","SUCCESS")
time.sleep(1)
for t in threads:#在浏览器获得信息之前不刷新表格
t.join()
showStores()#输出状态

简单的加一点细节,一个简单的判断是否可以预约的程序就完成啦!

效果运行如下:


到此为止,你已经基本知道该如何使用selenium了,也对网页分析以及判断的逻辑有了基本的认识,下一章我们将实现购买和完成预约,

点赞过20下周更新

点赞过50后天更新

点赞过100磕着护肝药立马更新T_T

声明!!!:

本文仅用作个人学习使用,禁止用于任何违法活动,所产生的后果与代码作者无关