基于selenium实现大麦网自动抢票脚本教程

发布于:2025-02-11 ⋅ 阅读:(16) ⋅ 点赞:(0)

闲来无事,打开大麦网发现现在大多数演唱票都需要手机端才能抢票,仅有很少一部分支持pc端用网页去抢票,但正所谓:道高一尺,魔高一丈,解决这个反爬问题,我们可以采用Airtest连接仿真机来模拟手机端操作,这次教程我们就先着手去解决利用selenium解决少部分可以用pc端抢票的问题。如果针对手机端抢票的呼声较高,后面我会出一篇关于Airtest抢票的blog。

前提声明:

1、本教程仅用于学习和研究使用,不得用于商业行为。

2、请确保在合法合规的前提前下使用本代码

3、本教程所涉及的操作均为正常模拟用户操作,不涉及任何数据入侵或数据窃取。

一、引言 

在热门演出和赛事门票一票难求的今天,利用自动化工具来提高抢票成功率成为很多人的需求,本文将详细介绍如何使用chromedriver及selenium利用python来编写一个简单的大麦网自动抢票脚本。

二、准备工作

在开始之前,确保开发环境中安装了我们所需要的库:

chromedriver版本: 131.0.6778.87

chrome版本:131.0.6778.140

selenium:4.27.1(用于网页自动化操作)

如果这些并未安装,可以看我之前写的blog,也可以在B站等平台搜索资源进行学习。这里我们就不详细说了。

三、代码分析

1、所需页面URL

首先我们需要定义大麦网首页、登陆页面以及我们想要抢的那张票的页面URL。

damai_url = "https://www.damai.cn/

login_url = ”https://passport.damai.cn/login?ru=https%3A%2F%2Fwww.damai.cn%2F

target_url ="https://detail.damai.cn/item.htm?spm=a2oeg.home.card_0.ditem_3.591b23e11Li5yj&id=862317821501“

 我们主要采取面向对象的思想来编写代码,我们首先定义一个类对象,然后进行初始化加载。

class Concert:
    def __init__(self):
        self.status = 0 # 状态,表示当前操作执行到哪一步
        self.login_method = 1 # 0:模拟登录 1:使用cookie登录
        self.driver = webdriver.Chrome()

然后执行登陆操作,我们需要判断是否需要进行模拟登陆操作,如果需要模拟登录就先打开登陆页面:

    """登录"""
    def login(self):
        # 如果为0,模拟登录
        if self.login_method == 0:
            self.driver.get(login_url)
        elif self.login_method == 1:
            # 如果当前目录下没有这个cookie.pkl文件
            if not os.path.exists('cookie.pkl'):
                # 登陆一下记录登录信息
                self.set_cookies()
            else:
                self.driver.get(target_url)
                # 登陆一下 通过selenium传入一些信息
                self.get_cookie()

我们来看定义的两个函数:set_cookies()和get_cookie():

    """cookies:登陆网站的时候出现的,记录用户信息"""
    def set_cookies(self):
        self.driver.get(login_url)
        print("###请扫码登陆###")
        time.sleep(10)
        print("###登陆成功###")
        pickle.dump(self.driver.get_cookies(), open('cookie.pkl','wb')) #获取登陆的信息,并保存下来
        print("###cookie保存成功###")
        # 登陆成功后就跳转到抢票页面
        self.driver.get(target_url)
        # time.sleep(2)
    # 如果文件中已经有了cookie.pkl文件
    def get_cookie(self):
        cookies = pickle.load(open('cookie.pkl','rb'))
        for cookie in cookies:
            cookie_dict = {
                "domain":".damai.cn",
                "name":cookie.get("name"),
                "value":cookie.get("value"),
            }
            self.driver.add_cookie(cookie_dict)
            print("###载入cookie成功###")

 打开浏览器,状态此时改为1

    def enter_concert(self):
        print("###打开浏览器,进入大麦网###")
        # 调用登录
        self.login()
        self.driver.refresh()
        self.status = 1
        print("###登陆成功###")

购票具体逻辑地实现:如果我们还停留在我们要买的这张票的页面,门票的信息可能是缺货,这个时候需要我们不断的刷新,因此我们可以写一个while循环来实现它,直到点击进入页面:订单确认页为止。给不同的情况,赋予不同的状态,并采用不同的逻辑去判断,比如需要我们手动选座购买的逻辑代码等等。然后就是进入订单确认页的具体逻辑执行,我们可以采取xpath的方式去定位元素。把具体的代码封装到方法中。

    # 抢票并下单:首先判断是否能够购买,如果不能就一直刷新网页,知道能够购买为止
    def choose_ticket(self):
        if self.status == 1:
            print("="*30)
            print("###请选择日期以及票价###")
            while self.driver.title.find("订单确认页") == -1: # driver.title.find寻找索引页,如果找不到返回-1,找到返回索引页
                # 下单按钮
                button = self.driver.find_element(By.XPATH,'/html/body/div[2]/div/div[1]/div[1]/div/div[2]/div[3]/div[9]/div/div[3]/div[3]').text
                if button == "提交缺货登记": # 这里也就是说不一定会是"不,立即购票"这几个字,也有可能会是提交缺货登记等
                    self.driver.refresh()
                elif button == "不,立即购票":
                    self.driver.find_element(By.XPATH,'/html/body/div[2]/div/div[1]/div[1]/div/div[2]/div[3]/div[9]/div/div[3]/div[3]').click()
                    time.sleep(10)
                elif button == "不,选座购票":
                    self.driver.find_element(By.XPATH,'/html/body/div[2]/div/div[1]/div[1]/div/div[2]/div[3]/div[9]/div/div[3]/div[3]').click()
                    self.status = 2
                else:
                    self.status = 3
                tittle = self.driver.title
                if tittle == "选择座位":
                    print("###请选择座位###")
                    time.sleep(10)
                    self.driver.find_element('//*[@id="root"]/div/div[4]/div[2]/button').click()
                elif tittle == "订单确认页":
                    while True:
                        print("###正在加载中###")
                        self.order_check()
                        break

其实这里如果不是企业级的项目,而是自用的话,可以直接通过selenium语句去写观影人等信息。

 # self.driver.find_element(By.XPATH,'//*[@id="dmViewerBlock_DmViewerBlock"]/div/div/div[3]').click()
 # name = self.driver.find_element(By.XPATH,'//*[@id="addholder-model"]/div/div[1]/div/div[1]/input').clear().send_keys("your_name")
 # testify = self.driver.find_element(By.XPATH,'//*[@id="addholder-model"]/div/div[1]/div/div[5]/input').clear().send_keys("your_password")
# self.driver.find_element(By.XPATH,'//*[@id="addholder-model"]/div/div[1]/div/div[8]/div').click()
# time.sleep(2)
# self.driver.find_element(By.XPATH,'//*[@id="dmViewerBlock_DmViewerBlock"]/div[2]/div/div/div[2]/i').click()

我们来看order_check()这个方法的具体实现,其实就是勾选观影人(通过selenium去定位接口)然后提交订单即可。

    def order_check(self):
        print("###开始确认订单###")
        try:
            self.driver.find_element(By.XPATH, '//*[@id="dmViewerBlock_DmViewerBlock"]/div[2]/div/div/div[2]/i').click()
        except Exception as e:
            print("###购票人信息选择失败,请重新选择元素###")
            print(e)
        time.sleep(0.5)
        shoujihao = self.driver.find_element(By.XPATH,'//*[@id="dmContactBlock_DmContactBlock"]/div[2]/div/div[2]/input').clear()
        time.sleep(2)
        self.driver.find_element(By.XPATH,'//*[@id="dmContactBlock_DmContactBlock"]/div[2]/div/div[2]/input').send_keys("your_iphone_number")
        time.sleep(2)
        self.driver.find_element(By.XPATH, '//*[@id="dmOrderSubmitBlock_DmOrderSubmitBlock"]/div[2]/div/div[2]/div[2]/div[2]').click()
        time.sleep(10)

以上就是实现大麦网自动抢票的全部代码实现,其实逻辑来讲并不复杂,主要就是selenium的一些应用,最重要的是我们需要学习当中的面向对象的思想以及逻辑的复现,这是我们需要我们掌握并且要攻克的难点。

四、代码优化与注意事项

1、元素定位:要准确的使用xpath或者其他定位方式(如CSS选择器)来定位页面元素,因为大麦网也买你可能会更新,元素的xpath语法可能会改变,所以需要定期检查和调整代码。

2、等待时间:合理设置等待的时间,避免因为也页面加载缓慢导致操作失败,但也不能设置过长的时间影响抢票效率。可以使用selenium中的显示等待和隐式等待来优化。

3、多线程:可以考虑使用多线程技术,同时监控多个场次或者多个门票的抢购情况,提高抢票成功的概率,但要注意大麦网的相关规则,避免被判定为异常操作。

五、全部代码

from selenium import webdriver
from selenium.webdriver.common.by import By
import time
import os
import pickle
# 自动登录
# 大麦网首页
damai_url = "https://www.damai.cn/"
# 登录页面网址
login_url = "https://passport.damai.cn/login?ru=https%3A%2F%2Fwww.damai.cn%2F"
# 要抢票的网址
target_url = "https://detail.damai.cn/item.htm?spm=a2oeg.home.card_0.ditem_3.591b23e11Li5yj&id=862317821501"

class Concert:
    def __init__(self):
        self.status = 0 # 状态,表示当前操作执行到哪一步
        self.login_method = 1 # 0:模拟登录 1:使用cookie登录
        self.driver = webdriver.Chrome()
    """cookies:登陆网站的时候出现的,记录用户信息"""
    def set_cookies(self):
        self.driver.get(login_url)
        print("###请扫码登陆###")
        time.sleep(10)
        print("###登陆成功###")
        pickle.dump(self.driver.get_cookies(), open('cookie.pkl','wb')) #获取登陆的信息,并保存下来
        print("###cookie保存成功###")
        # 登陆成功后就跳转到抢票页面
        self.driver.get(target_url)
        # self.zhanghao = self.driver.find_element(By.XPATH, '//*[@id="fm-login-id"]').send_keys("17719114890")
        # self.mima = self.driver.find_element(By.XPATH, '//*[@id="fm-login-password"]').send_keys("dwq0219423")
        # self.button = self.driver.find_element(By.XPATH, '//*[@id="fm-login-submit"]').click()
        # time.sleep(2)
    # 如果文件中已经有了cookie.pkl文件
    def get_cookie(self):
        cookies = pickle.load(open('cookie.pkl','rb'))
        for cookie in cookies:
            cookie_dict = {
                "domain":".damai.cn",
                "name":cookie.get("name"),
                "value":cookie.get("value"),
            }
            self.driver.add_cookie(cookie_dict)
        time.sleep(10) # 在这里记得手动刷新一下,用户信息才会显示出来
        print("###载入cookie成功###")
    """登录"""
    def login(self):
        # 如果为0,模拟登录
        if self.login_method == 0:
            self.driver.get(login_url)
        elif self.login_method == 1:
            # 如果当前目录下没有这个cookie.pkl文件
            if not os.path.exists('cookie.pkl'):
                # 登陆一下记录登录信息
                self.set_cookies()
            else:
                self.driver.get(target_url)
                # 登陆一下 通过selenium传入一些信息
                self.get_cookie()
    """打开浏览器"""
    def enter_concert(self):
        print("###打开浏览器,进入大麦网###")
        # 调用登录
        self.login()
        self.driver.refresh()
        self.status = 1
        print("###登陆成功###")
    # 抢票并下单:首先判断是否能够购买,如果不能就一直刷新网页,知道能够购买为止
    def choose_ticket(self):
        if self.status == 1:
            print("="*30)
            print("###请选择日期以及票价###")
            while self.driver.title.find("订单确认页") == -1: # driver.title.find寻找索引页,如果找不到返回-1,找到返回索引页
                # 下单按钮
                button = self.driver.find_element(By.XPATH,'/html/body/div[2]/div/div[1]/div[1]/div/div[2]/div[3]/div[9]/div/div[3]/div[3]').text
                if button == "提交缺货登记": # 这里也就是说不一定会是"不,立即购票"这几个字,也有可能会是提交缺货登记等
                    self.driver.refresh()
                elif button == "不,立即购票":
                    self.driver.find_element(By.XPATH,'/html/body/div[2]/div/div[1]/div[1]/div/div[2]/div[3]/div[9]/div/div[3]/div[3]').click()
                    time.sleep(10)
                elif button == "不,选座购票":
                    self.driver.find_element(By.XPATH,'/html/body/div[2]/div/div[1]/div[1]/div/div[2]/div[3]/div[9]/div/div[3]/div[3]').click()
                    self.status = 2
                else:
                    self.status = 3
                tittle = self.driver.title
                if tittle == "选择座位":
                    print("###请选择座位###")
                    time.sleep(10)
                    self.driver.find_element('//*[@id="root"]/div/div[4]/div[2]/button').click()
                elif tittle == "订单确认页":
                    while True:
                        print("###正在加载中###")
                        self.order_check()
                        break
                    # 实现下单的逻辑
                    # self.driver.find_element(By.XPATH,'//*[@id="dmViewerBlock_DmViewerBlock"]/div/div/div[3]').click()
                    # name = self.driver.find_element(By.XPATH,'//*[@id="addholder-model"]/div/div[1]/div/div[1]/input').clear().send_keys("your_name")
                    # testify = self.driver.find_element(By.XPATH,'//*[@id="addholder-model"]/div/div[1]/div/div[5]/input').clear().send_keys("your_password")
                    # self.driver.find_element(By.XPATH,'//*[@id="addholder-model"]/div/div[1]/div/div[8]/div').click()
                    # time.sleep(2)
                    # self.driver.find_element(By.XPATH,'//*[@id="dmViewerBlock_DmViewerBlock"]/div[2]/div/div/div[2]/i').click()
    def order_check(self):
        print("###开始确认订单###")
        try:
            self.driver.find_element(By.XPATH, '//*[@id="dmViewerBlock_DmViewerBlock"]/div[2]/div/div/div[2]/i').click()
        except Exception as e:
            print("###购票人信息选择失败,请重新选择元素###")
            print(e)
        time.sleep(0.5)
        shoujihao = self.driver.find_element(By.XPATH,'//*[@id="dmContactBlock_DmContactBlock"]/div[2]/div/div[2]/input').clear()
        time.sleep(2)
        self.driver.find_element(By.XPATH,'//*[@id="dmContactBlock_DmContactBlock"]/div[2]/div/div[2]/input').send_keys("your_iphone_number")
        time.sleep(2)
        self.driver.find_element(By.XPATH, '//*[@id="dmOrderSubmitBlock_DmOrderSubmitBlock"]/div[2]/div/div[2]/div[2]/div[2]').click()
        time.sleep(10)

if __name__ == '__main__':
    concert = Concert()
    concert.enter_concert()
    concert.choose_ticket()

六、总结

通过使用chromedriver和selenium库,我们可以编写一个简单的大麦网自动抢票脚本。但要注意,自动抢票可能存在违反平台规则以及法律风险等情况,在使用时需要谨慎并确保自己的行为合法合规。同时,也希望票务平台能够不断优化售票机制,让更多真正有需求的用户能够公平的购买到门票。