Python自动化帮你抢票,你上你也行
off999 2024-09-27 13:46 30 浏览 0 评论
写在前面:这个程序不是一个人能在短时间内完成的,感谢达纳,王哥的支持帮助。也感谢小平老师,没有压迫,就没有项目。
简介:这是一篇很硬核的Blog, 有一定Python基础的童鞋方能看懂,本程序的主要内容是首先通过Python的Selenium爬虫对12306的图形验证码进行批量爬取,然后通过Pillow对图片的尺寸进行剪裁。将剪裁好的图片分为文字和图形部分并将其打上标签,分别构建卷积神经网络模型(CNN)对图片进行学习,最后通过一系列的整合,用于12306自动抢票,并将成功信息通过邮件方式发送给用户。
1、搭建12306爬虫程序
#利用Selenium批量爬取验证码
#建立存储目录
import time
import os
from lxml import etree
from urllib.request import urlopen
file_dir=r'E:\12306'#设定存储目录
if not os.path.exists(file_dir):
os.mkdir(file_dir)#判断文件夹是否已经存在
#导入selenium库
import selenium.webdriver as wb
#将谷歌游览器设置为自动化处理
br=wb.Chrome()
#进入到12306官网
br.get('https://kyfw.12306.cn/otn/resources/login.html')
#获取验证码按钮
time.sleep(5)#程序休眠,防止IP被封杀
button=br.find_element_by_xpath('/html/body/div[2]/div[2]/ul/li[2]/a')
time.sleep(3)
button.click()
#获取页面HTML源码病设定循环
i=0
while i<=1000:#设定爬取1000次
time.sleep(2)
page=br.page_source
#用xpath语言对目标图片地址进行提取
html=etree.HTML(page)
img=html.xpath('//*[@id="J-loginImg"]')[0]
img_url=img.attrib['src']
#用urllib库去请求网址得到图片的二进制数据
respond=urlopen(img_url)
print('done')
img_bytes=respond.file.read()
#讲读取的图片二进制写入文件并保存
with open(file_dir+'\\name_1_%d.jpg'%i,'wb') as f:#
f.write(img_bytes)
for m in range(20):
print('*',end='')
time.sleep(0.01)
print('完成第%d张图片下载'%(i+1))
fresh=br.find_element_by_xpath('/html/body/div[2]/div[2]/div[1]/div[2]/div[3]/div/div[3]')
time.sleep(5)#对验证码网页进行刷新,开启下一轮爬取
fresh.click()
i+=1到目前为止,验证码基本获取完毕,结果如下,我们当时为了更好的结果,爬取了3万多张,结果到后面识别准确率也只能达到90%左右,说明还是不够。
2、图片剪切,将图片剪切为指定大小,供于CNN模型学习
#将图片进行剪切分类
import os
from PIL import Image
import time
char_dir=r'E:\12306_cha'#构造文字部分存储目录
pic_dir=r'E:\12306_pic'#构造图形部分存储目录
ticket_dir=r'E:\12306'#爬虫图片被保存的位置
#判断文件夹是否已经存在,如果没有,新建
if not os.path.exists(char_dir):
os.mkdir(char_dir)
if not os.path.exists(pic_dir):
os.mkdir(pic_dir)
#获取文件列表
pic_list=os.listdir(ticket_dir)
for i,num in zip(pic_list,range(len(pic_list))):
try:
real=ticket_dir+'\\'+i
image=Image.open(real)
i=i.replace('.jpg','')
cp_ch=image.crop((117,0,230,26))#剪裁文字部分尺寸
cp_ch.save(char_dir+'\\'+'%s.jpg'%i)#保存文字部分
cp_pic_1_1=image.crop((3,39,72,109))#剪裁图片部分尺寸
cp_pic_1_1.save(pic_dir+'\\'+'%s_1_1.jpg'%i)#保存第一张图片,下面同理不在赘述
cp_pic_1_2=image.crop((75,39,144,109))
cp_pic_1_2.save(pic_dir+'\\'+'%s_1_2.jpg'%i)
cp_pic_1_3=image.crop((147,39,216,109))
cp_pic_1_3.save(pic_dir+'\\'+'%s_1_3.jpg'%i)
cp_pic_1_4=image.crop((219,39,288,109))
cp_pic_1_4.save(pic_dir+'\\'+'%s_1_4.jpg'%i)
cp_pic_2_1=image.crop((3,110,72,180))
cp_pic_2_1.save(pic_dir+'\\'+'%s_2_1.jpg'%i)
cp_pic_2_2=image.crop((75,110,144,180))
cp_pic_2_2.save(pic_dir+'\\'+'%s_2_2.jpg'%i)
cp_pic_2_3=image.crop((147,110,216,180))
cp_pic_2_3.save(pic_dir+'\\'+'%s_2_3.jpg'%i)
cp_pic_2_4=image.crop((219,110,288,180))
cp_pic_2_4.save(pic_dir+'\\'+'%s_2_4.jpg'%i)
for n in range(30):
print('*',end='')
time.sleep(0.02)
except:
print('image error')
continue
print('第%d张图片已经处理,还剩%d张'%(num+1,len(pic_list)-num-1))
print('Having done all the pictures')到这一步,一张验证码图片就被分为1张文字图片和8张图形图片分别保存在各自的文件夹中。
3、接下来一步,就是最让人自闭的打标签,将类别一样的整理到一个文件夹中,我人已经分傻了,能体会从3万张图片中找图片的痛苦吗?不过还是感谢达纳同学分好的小部分数据集,正是这部分数据集用于机器学习,才使得后面3万张被成功分类。
4、将图片做合适的处理,喂给CNN模型进行学习,模型搭建如下:
首先训练文字部分
#文字部分CNN网络
#原模型修改
# 导入所需模块
from keras.models import Sequential
from keras.layers.normalization import BatchNormalization
from keras.layers.convolutional import Conv2D
from keras.layers.convolutional import MaxPooling2D
from keras.initializers import TruncatedNormal
from keras.layers.core import Activation
from keras.layers.core import Flatten
from keras.layers.core import Dropout
from keras.layers.core import Dense
from keras import backend as K
class SimpleVGGNet:
@staticmethod
def build(width, height, depth, classes):
model = Sequential()
inputShape = (height, width, depth)
chanDim = -1
if K.image_data_format() == "channels_first":
inputShape = (depth, height, width)
chanDim = 1
# CONV => RELU => POOL
model.add(Conv2D(32, (3, 3), padding="same",
input_shape=inputShape,kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))
model.add(Activation("relu"))
model.add(BatchNormalization(axis=chanDim))
model.add(MaxPooling2D(pool_size=(3, 3)))#yk注:3*3和2*2(原文)的区别,池化层的尺寸会有影响吗?
#model.add(Dropout(0.25))#原文是备注掉的,增加的目的是为了增加模型的泛化能力
# (CONV => RELU) * 2 => POOL
model.add(Conv2D(64, (3, 3), padding="same",kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))
model.add(Activation("relu"))
model.add(BatchNormalization(axis=chanDim))
model.add(Conv2D(64, (3, 3), padding="same",kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))
model.add(Activation("relu"))
model.add(BatchNormalization(axis=chanDim))
model.add(MaxPooling2D(pool_size=(2, 2)))#yk将2*2修改为3*3
#model.add(Dropout(0.25))#yk增加了泛化能力
# (CONV => RELU) * 3 => POOL
model.add(Conv2D(128, (3, 3), padding="same",kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))
model.add(Activation("relu"))
model.add(BatchNormalization(axis=chanDim))
model.add(Conv2D(128, (3, 3), padding="same",kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))
model.add(Activation("relu"))
model.add(BatchNormalization(axis=chanDim))
model.add(Conv2D(128, (3, 3), padding="same",kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))
model.add(Activation("relu"))
model.add(BatchNormalization(axis=chanDim))
model.add(MaxPooling2D(pool_size=(2, 2)))
#model.add(Dropout(0.25))
# FC层
model.add(Flatten())
model.add(Dense(1024,kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))#yk将原文的512改为1024
model.add(Activation("relu"))
model.add(BatchNormalization())
#model.add(Dropout(0.25))#yk将0.6改为了0.25
# softmax 分类
model.add(Dense(classes,kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))
model.add(Activation("softmax"))
return model
```
>######CNN网络搭建完毕
```
# 导入所需工具包
from CNN_net import SimpleVGGNet
from sklearn.preprocessing import LabelBinarizer
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from keras.optimizers import SGD
from keras.preprocessing.image import ImageDataGenerator
import utils_paths
import matplotlib.pyplot as plt
import numpy as np
import argparse
import random
import pickle
import cv2
import os
import PIL.Image as Image
# 读取数据和标签
print("------开始读取数据------")
data = []
labels = []
# 拿到图像数据路径,方便后续读取
imagePaths = sorted(list(utils_paths.list_images(path_1)))#path_1时文字部分的文件路径
random.seed(42)
random.shuffle(imagePaths)
# 遍历读取数据
for imagePath in imagePaths:
# 读取图像数据
image = Image.open(imagePath)
image =np.array(image)
image = cv2.resize(image, (96, 96))#修改
data.append(image)
# 读取标签
label = imagePath.split(os.path.sep)[-2]
labels.append(label)
# 对图像数据做scale操作
data = np.array(data, dtype="float") / 255.0
labels = np.array(labels)
# 数据集切分
(trainX, testX, trainY, testY) = train_test_split(data,labels, test_size=0.25, random_state=42)
# 转换标签为one-hot encoding格式
lb = LabelBinarizer()
trainY = lb.fit_transform(trainY)
testY = lb.transform(testY)
# 数据增强处理
aug = ImageDataGenerator(rotation_range=30, width_shift_range=0.1,
height_shift_range=0.1, shear_range=0.2, zoom_range=0.2,
horizontal_flip=True, fill_mode="nearest")
# 建立卷积神经网络
model = SimpleVGGNet.build(width=96, height=96, depth=3,classes=len(lb.classes_))
# 设置初始化超参数
INIT_LR = 0.001
EPOCHS = 100#
BS = 32
# 损失函数,编译模型
print("------准备训练网络------")
opt = SGD(lr=INIT_LR, decay=INIT_LR / EPOCHS)
model.compile(loss="categorical_crossentropy", optimizer=opt,metrics=["accuracy"])
# 训练网络模型
H = model.fit_generator(aug.flow(trainX, trainY, batch_size=BS),
validation_data=(testX, testY), steps_per_epoch=len(trainX) // BS,
epochs=EPOCHS)
"""
H = model.fit(trainX, trainY, validation_data=(testX, testY),
epochs=EPOCHS, batch_size=32)
"""
# 测试
print("------测试网络------")
predictions = model.predict(testX, batch_size=32)
print(classification_report(testY.argmax(axis=1),
predictions.argmax(axis=1), target_names=lb.classes_))
# 绘制结果曲线
N = np.arange(0, EPOCHS)
plt.style.use("ggplot")
plt.figure()
plt.plot(N, H.history["loss"], label="train_loss")
plt.plot(N, H.history["val_loss"], label="val_loss")
plt.plot(N, H.history["accuracy"], label="train_acc")
plt.plot(N, H.history["val_accuracy"], label="val_acc")
plt.title("Training Loss and Accuracy")
plt.xlabel("Epoch #")
plt.ylabel("Loss/Accuracy")
plt.legend()
plt.savefig(r'path_2\cnn_plot.png')#path_2时学习曲线的保存路径
# 保存模型
print("------正在保存模型------")
model.save(r'path_3\cnn.model')#path_3是模型的保存路径
f = open(r'path_4\cnn_lb.pickle', "wb")#path_4是标签集的保存路径
f.write(pickle.dumps(lb))
f.close()
模型开始进行训练
可以看到最后的准确率很低,But由于我在其中将Dropout层去除,模型过拟合,导致我得到的结果虽然准确率低,但是识别效果很好(比我的图片识别效果还好)
图形部分CNN模型
#原模型修改
# 导入所需模块
from keras.models import Sequential
from keras.layers.normalization import BatchNormalization
from keras.layers.convolutional import Conv2D
from keras.layers.convolutional import MaxPooling2D
from keras.initializers import TruncatedNormal
from keras.layers.core import Activation
from keras.layers.core import Flatten
from keras.layers.core import Dropout
from keras.layers.core import Dense
from keras import backend as K
class SimpleVGGNet:
@staticmethod
def build(width, height, depth, classes):
model = Sequential()
inputShape = (height, width, depth)
chanDim = -1
if K.image_data_format() == "channels_first":
inputShape = (depth, height, width)
chanDim = 1
# CONV => RELU => POOL
model.add(Conv2D(32, (3, 3), padding="same",
input_shape=inputShape,kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))
model.add(Activation("relu"))
model.add(BatchNormalization(axis=chanDim))
model.add(MaxPooling2D(pool_size=(3, 3)))#余坤注:3*3和2*2(原文)的区别,池化层的尺寸会有影响吗?
model.add(Dropout(0.25))#原文是备注掉的,增加的目的是为了增加模型的泛化能力
# (CONV => RELU) * 2 => POOL
model.add(Conv2D(64, (3, 3), padding="same",kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))
model.add(Activation("relu"))
model.add(BatchNormalization(axis=chanDim))
model.add(Conv2D(64, (3, 3), padding="same",kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))
model.add(Activation("relu"))
model.add(BatchNormalization(axis=chanDim))
model.add(MaxPooling2D(pool_size=(3, 3)))#yk将2*2修改为3*3
model.add(Dropout(0.25))#yk增加了泛化能力
# (CONV => RELU) * 3 => POOL
model.add(Conv2D(128, (3, 3), padding="same",kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))
model.add(Activation("relu"))
model.add(BatchNormalization(axis=chanDim))
model.add(Conv2D(128, (3, 3), padding="same",kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))
model.add(Activation("relu"))
model.add(BatchNormalization(axis=chanDim))
model.add(Conv2D(128, (3, 3), padding="same",kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))
model.add(Activation("relu"))
model.add(BatchNormalization(axis=chanDim))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
# FC层
model.add(Flatten())
model.add(Dense(1024,kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))#yk将原文的512改为1024
model.add(Activation("relu"))
model.add(BatchNormalization())
model.add(Dropout(0.25))#yk将0.6改为了0.5
# softmax 分类
model.add(Dense(classes,kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.01)))
model.add(Activation("softmax"))
return modelCNN模型搭建完毕
开始对图形部分进行CNN网络训练
# 导入所需工具包
from CNN_net import SimpleVGGNet
from sklearn.preprocessing import LabelBinarizer
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from keras.optimizers import SGD
from keras.preprocessing.image import ImageDataGenerator
import utils_paths
import matplotlib.pyplot as plt
import numpy as np
import argparse
import random
import pickle
import cv2
import os
import PIL.Image as Image
# 读取数据和标签
print("------开始读取数据------")
data = []
labels = []
# 拿到图像数据路径,方便后续读取
imagePaths =
sorted(list(utils_paths.list_images(path_1)))#path_1是图形的路径
random.seed(42)
random.shuffle(imagePaths)
# 遍历读取数据
for imagePath in imagePaths:
# 读取图像数据
image = Image.open(imagePath)
image =np.array(image)
image = cv2.resize(image, (80, 80))#修改
data.append(image)
# 读取标签
label = imagePath.split(os.path.sep)[-2]
labels.append(label)
# 对图像数据做scale操作
data = np.array(data, dtype="float") / 255.0
labels = np.array(labels)
# 数据集切分
(trainX, testX, trainY, testY) = train_test_split(data,labels, test_size=0.25, random_state=42)
# 转换标签为one-hot encoding格式
lb = LabelBinarizer()
trainY = lb.fit_transform(trainY)
testY = lb.transform(testY)
# 数据增强处理
aug = ImageDataGenerator(rotation_range=30, width_shift_range=0.1,
height_shift_range=0.1, shear_range=0.2, zoom_range=0.2,
horizontal_flip=True, fill_mode="nearest")
# 建立卷积神经网络
model = SimpleVGGNet.build(width=80, height=80, depth=3,classes=len(lb.classes_))
# 设置初始化超参数
INIT_LR = 0.001
EPOCHS = 100#
BS = 32
# 损失函数,编译模型
print("------准备训练网络------")
opt = SGD(lr=INIT_LR, decay=INIT_LR / EPOCHS)
model.compile(loss="categorical_crossentropy", optimizer=opt,metrics=["accuracy"])
# 训练网络模型
H = model.fit_generator(aug.flow(trainX, trainY, batch_size=BS),
validation_data=(testX, testY), steps_per_epoch=len(trainX) // BS,
epochs=EPOCHS)
"""
H = model.fit(trainX, trainY, validation_data=(testX, testY),
epochs=EPOCHS, batch_size=32)
"""
# 测试
print("------测试网络------")
predictions = model.predict(testX, batch_size=32)
print(classification_report(testY.argmax(axis=1),
predictions.argmax(axis=1), target_names=lb.classes_))
# 绘制结果曲线
N = np.arange(0, EPOCHS)
plt.style.use("ggplot")
plt.figure()
plt.plot(N, H.history["loss"], label="train_loss")
plt.plot(N, H.history["val_loss"], label="val_loss")
plt.plot(N, H.history["accuracy"], label="train_acc")
plt.plot(N, H.history["val_accuracy"], label="val_acc")
plt.title("Training Loss and Accuracy")
plt.xlabel("Epoch #")
plt.ylabel("Loss/Accuracy")
plt.legend()
plt.savefig(r'path_2\cnn_plot.png')#path_2是学习曲线的存储路径
# 保存模型
print("------正在保存模型------")
model.save(r'path_3\cnn.model')#path_3是你自己的模型存储路径
f = open(r'path_4\cnn_lb.pickle', "wb")#path_4时你自己的标签集存储路径
f.write(pickle.dumps(lb))
f.close()可以看到最后的准确率很接近1,达到了92%,但我觉得效果还只是一般,还是会有点智障,比如把菠萝识别成啤酒,红豆识别成红枣,没办法,要想准确率更高,必须要有更大的数据集,其次对图片进行合适的处理(我还不是太会)
4.这里要提一下啊,我们的做法稍微巧了一点,我们先让机器对1500张左右的图片进行学习,准确率可以达到80%左右。然后我们让这种程度的机器对图片进行分类,最终分完了30000张图片(当然我们最后人工进行挑错了),如下是分类的代码:
# 导入所需工具包
from keras.models import load_model
import argparse
import pickle
import cv2
import PIL.Image as Image
import os
import shutil
import numpy as np
print("------读取模型和标签------")
model = load_model(path)#path是保存模型的路径
lb = pickle.loads(open(path_1, "rb").read())#path_1是保存标签集的路径
def get_piclist(path):
return os.listdir(path)
path=r'E:\12306_pic'
pic_list=get_piclist(path)#get picture list
for i in pic_list:
img_path=path+'\\'+i
image=np.array(Image.open(img_path))
image = cv2.resize(image, (80,80))
image = image.astype("float") / 255.0
image = image.reshape((1, image.shape[0], image.shape[1],image.shape[2]))
preds = model.predict(image)
j = preds.argmax(axis=1)[0]
label = lb.classes_[j]
accuracy=int(preds[0][j] * 100)
print(label+'===>'+str(accuracy))
if accuracy>10:
shutil.move(img_path,r'Profile'+'\\'+'%s'%label)#这一步是把图片转移到预测的标签文件中
else:
continue5、进行到这里,基本上绝大部分工作已经做完了,后面的就是把前面的结果和Selenium结合实现自动化抢票,代码如下:
#获取时钟的函数
import datetime
def get_time():
contem=datetime.datetime.now()
return contem
```
```
#发送邮件的函数,以163邮箱为端口
#获取你的邮箱
import smtplib
from email.mime.text import MIMEText
from email.utils import formataddr
my_sender='user' # 发件人邮箱账号
my_pass = 'password' # 发件人邮箱密码
def mail(address):
my_user=address
ret=True
try:
msg=MIMEText('小主,快来12306官网支付您的车票喽!!!','plain','utf-8')
msg['From']=formataddr(["抢票小助手",my_sender]) # 括号里的对应发件人邮箱昵称、发件人邮箱账号
msg['To']=formataddr(["FK",my_user]) # 括号里的对应收件人邮箱昵称、收件人邮箱账号
msg['Subject']="12306提醒" # 邮件的主题,也可以说是标题
server=smtplib.SMTP_SSL("smtp.163.com", 465) # 发件人邮箱中的SMTP服务器,端口是25,这里是163邮箱
server.login(my_sender, my_pass) # 括号中对应的是发件人邮箱账号、邮箱密码
server.sendmail(my_sender,[my_user,],msg.as_string()) # 括号中对应的是发件人邮箱账号、收件人邮箱账号、发送邮件
server.quit() # 关闭连接
except Exception: # 如果 try 中的语句没有执行,则会执行下面的 ret=False
ret=False
return
```
```
#获取用户的输入12306账号和密码,并存储为CSV文件以及自己的邮箱
import csv
import pandas as pd
import os
#设置读取或者写入CSV
def get_user():
path=r'..\User_information\user_information.csv'
if os.path.exists(path):
infm=pd.read_csv(path)
user_name=int(infm.columns[0])
user_secret=(infm.columns[1])
else:
user_name=int(input("Please input your User Name: "))
user_secret=input("please input your User Secret: ")
with open(path,'w',newline=None) as f:
cwriter=csv.writer(f)
cwriter.writerow([user_name,user_secret])
return (user_name,user_secret)
```
```
#图片识别部分函数
from keras.models import load_model
import argparse
import pickle
import cv2
import PIL.Image as Image
import os
import shutil
import numpy as np
print("------读取模型和标签------")
model_p = load_model(r'..\pic_train\cnn.model')
lb_p = pickle.loads(open(r'..\pic_train\cnn_lb.pickle', "rb").read())
model_c = load_model(r'..\char_train\cnn.model')
lb_c = pickle.loads(open(r'..\char_train\cnn_lb.pickle', "rb").read())
def pic_identify(image):
image = cv2.resize(image, (80,80))
image = image.astype("float") / 255.0
image = image.reshape((1, image.shape[0], image.shape[1],image.shape[2]))
preds = model_p.predict(image)
j = preds.argmax(axis=1)[0]
label = lb_p.classes_[j]
return label
def char_identify(charc):
image = cv2.resize(charc, (96, 96))
image = image.astype("float") / 255.0
image = image.reshape((1, image.shape[0], image.shape[1],image.shape[2]))
preds = model_c.predict(image)
i = preds.argmax(axis=1)[0]
label = lb_c.classes_[i]
return label
```
```
#登录12306官网函数
import selenium.webdriver as wb
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait as WDW
from selenium.webdriver.common.by import By
import time
from lxml import etree
from urllib.request import urlopen
import PIL.Image as Image
import numpy as np
from selenium.webdriver.common.action_chains import ActionChains
def login(user_name,user_secret,br):
url='https://kyfw.12306.cn/otn/resources/login.html'
wait=WDW(br,10)
br.get(url)
login_surface=wait.until(EC.presence_of_element_located((By.XPATH,'/html/body/div[2]/div[2]/ul/li[2]/a')))
time.sleep(5)
login_surface.click()
time.sleep(5)
user_name_button=br.find_element_by_id('J-userName').send_keys(user_name)
user_secret_button=br.find_element_by_id('J-password').send_keys(user_secret)
br.maximize_window()
def pic_get(br):
file_dir=r'..\download_pic'
page=br.page_source
html=etree.HTML(page)
img=html.xpath('//*[@id="J-loginImg"]')[0]
img_url=img.attrib['src']
respond=urlopen(img_url)
img_bytes=respond.file.read()
with open(file_dir+'\\12306.jpg','wb') as f:
f.write(img_bytes)
def pic_cut():
pic_path=r'..\download_pic\12306.jpg'
image=Image.open(pic_path)
charc=np.array(image.crop((117,0,230,26)))
pic_1=np.array(image.crop((3,39,72,109)))
l_1=(1050,350)
pic_2=np.array(image.crop((75,39,144,109)))
l_2=(1130,350)
pic_3=np.array(image.crop((147,39,216,109)))
l_3=(1200,350)
pic_4=np.array(image.crop((219,39,288,109)))
l_4=(1270,350)
pic_5=np.array(image.crop((3,110,72,180)))
l_5=(1050,420)
pic_6=np.array(image.crop((75,110,144,180)))
l_6=(1130,420)
pic_7=np.array(image.crop((147,110,216,180)))
l_7=(1200,420)
pic_8=np.array(image.crop((219,110,288,180)))
l_8=(1270,420)
return ([pic_1,pic_2,pic_3,pic_4,pic_5,pic_6,pic_7,pic_8],[l_1,l_2,l_3,l_4,l_5,l_6,l_7,l_8],charc)
def mouse_click(x,y,br):
ActionChains(br).move_by_offset(x,y).click().perform()
ActionChains(br).move_by_offset(-x,-y).perform()
```
```
#开始抢票,并将上面所有提到的函数集中到此模块中
from get_information import get_user as gu
from login_12306 import *
from identify import *
import time
import datetime
from clock import *
from tqdm import tqdm
from selenium.webdriver.common.keys import Keys
from email_notion import mail
def main():
#开始记录登录信息
username,password=gu()
address=input('请输入您的电子邮箱: ')
#启动游览器脚本
br=wb.Chrome()
#登录12306
login(username,password,br)
#等待5秒
time.sleep(5)
#下载图片
pic_get(br)
#图片剪切
piclist,location,chara=pic_cut()
#获取char文字内容
chara_content=char_identify(chara)
#识别文字,把机器不易识别的剔除
while chara_content=='卷尺' or chara_content=='锅铲' or chara_content=='海报' or chara_content=='珊瑚'or chara_content=='棉棒':
fresh=br.find_element_by_xpath('/html/body/div[2]/div[2]/div[1]/div[2]/div[3]/div/div[3]')
fresh.click()
pic_get(br)
time.sleep(10)
piclist,location,chara=pic_cut()
time.sleep(5)
chara_content=char_identify(chara)
#选取相对应的图片区
for i,j in zip(piclist,location):
if pic_identify(i)==chara_content:
print(pic_identify(i))
mouse_click(j[0],j[1],br)
login_button=br.find_element_by_id('J-login')
verify=input('How about the choice:')
if verify=='y':
login_button.click()
#选取买票,仅支持单程
time.sleep(10)
dan=br.find_element_by_xpath('//*[@id="J-chepiao"]/a')
dan_v=br.find_element_by_xpath('//*[@id="J-chepiao"]/div/div[1]/ul/li[1]/a')
dan.click()
dan_v.click()
#输入出发地,目的地和出发时间
fromstButton=br.find_element_by_id('fromStationText')
tostButton=br.find_element_by_id('toStationText')
departure=input("请输入出发地:")
destiney=input("请输入目的地:")
gotime=input('请输入出发时间(eg.2019-12-3):')
today=datetime.datetime.now()
expect=datetime.datetime.strptime(gotime,'%Y-%m-%d')
year=int(gotime.split('-')[0])
month=int(gotime.split('-')[1])
day=int(gotime.split('-')[2])
fromstButton.click()
fromstButton.send_keys(departure)
fromstButton.send_keys(Keys.ENTER)
time.sleep(1)
tostButton.click()
tostButton.send_keys(destiney)
tostButton.send_keys(Keys.ENTER)
date=br.find_element_by_id('date_icon_1').click()
tomonth=br.find_elements_by_xpath('/html/body/div[34]/div[1]/div[2]/div')
nextmonth=br.find_elements_by_xpath('/html/body/div[34]/div[2]/div[2]/div')
#date=br.find_element_by_id('date_icon_1')
#设定循环,如果不是提前一个月的节点,程序会进行休眠
while expect>today+datetime.timedelta(days=30):
for i in tqdm(range(3600)):
time.sleep(1)
today=get_time()
if time.localtime().tm_mon==month:
choice=tomonth[day-1].click()
else:
choice=nextmonth[day-1].click()
#开始查询
all_button=br.find_element_by_id('train_type_btn_all').click()
search_button=br.find_element_by_id('query_ticket').click()
#仅以一个例子为例,太多了设置比较麻烦
time.sleep(5)
train_info=etree.HTML(br.page_source)
#获取列车的始终地
place=train_info.xpath('//*[@id="train_num_0"]/div[2]/strong')
#获取列车的初末时间
getime=train_info.xpath('//*[@id="train_num_0"]/div[3]/strong')
trainId=train_info.xpath('//*[@id="queryLeftTable"]/tr[1]')[0].attrib['id']
print("已为您查询到可依靠的列车")
print('%s ===>> %s %s ===>> %s'%(place[0].text,place[1].text,getime[0].text,getime[1].text))
#预定开始
preorder=br.find_element_by_xpath('//*[@id="%s"]/td[13]/a'%trainId)
preorder.click()
time.sleep(2)
#选择乘客
passenger=br.find_element_by_id('normalPassenger_0')
passenger.click()
#提交订单
submit=br.find_element_by_id('submitOrder_id').click()
time.sleep(2)
#确认
sure=br.find_element_by_id('qr_submit_id').click()
mail(address)
return br
main()哎,由于我作业缠身,有一部分代码的注释还没有写完,等到闲下来会一一注释清楚,效果图额(主要是我忘了存,再加上调试代码时一天购票三次,已经没机会买票了,就看最终结果吧
这就是本篇Blog的主要内容,没办法时间太紧,作业太多,所以有不当之处还请指正,欢迎大家交流学习啊。所有文件程序都已保存到[百度网盘 lsbk](https://pan.baidu.com/s/1thlynCBF0yh58T_iFtaqew)
相关推荐
- u盘被写保护去除步骤(u盘的被写保护怎么解除)
-
U盘被写保护通常是因为存储器故障、病毒感染、文件系统损坏等原因导致的,但可以通过以下几种方法进行解除:1.检查U盘开关:一些U盘可能会有物理上的写保护开关,将其关闭即可解除写保护。2.使用命令行清...
- 电脑蓝屏怎么用u盘重装系统(电脑蓝屏重装系统教程u盘)
-
U盘装系统步骤:1.制作U盘启动盘。这里推荐U启动U盘启动盘制作工具,在网上一搜便是。2.U盘启动盘做好了,我们还需要一个GHOST文件,可以从网上下载一个ghost版的XP/WIN7/WIN8系统,...
-
- erp管理软件(erp管理软件免费版)
-
用友的ERP应该说从3万-3000都有ERP的价格随着企业的规模不同,价格也是不一样的。因为企业规模不同产生的效果也是不一样的,所以用友的产品分T1/T3/T6/U8/U9/NC3万小企业做个财务业务一体化还是勉强可以做的。只...
-
2025-11-11 09:03 off999
- 笔记本启动黑屏怎么回事(笔记本启动黑屏没反应)
-
笔记本开机后黑屏最常遇到的一种情况:因随便点击垃圾网站而导致电脑中脑或受到木马的侵袭,致使电脑系统瘫痪,解决的办法就是重装电脑系统,装好系统后安装查毒软件,定期对电脑进行杀毒全盘扫描,然后平时尽量不要...
- win7系统怎么开wifi热点(win7如何开wifi热点)
-
1、首先确认你的无线网卡开启。在开始菜单中依次找到“所有程序”--“附件”--“命令提示符”,右键“以管理员身份运行”; 2、在“命令提示符”里输入“netshwlans...
- 无线路由桥接设置方法(无线路由器无线桥接设置)
-
1、首先在电脑上输入并登录第一台路由器的IP地址。2、进入路由器管理界面之后,点击“无线设置”,然后点击基本设置中设置“SSID号”,接着点击“信道”,然后设置固定信号道。3、返回无线设置菜单栏,选择...
- win10企业版激活命令(win10企业版cmd激活命令)
-
关于这个问题,Windows10企业版可以通过以下方法进行激活:1.使用企业版密钥激活:如果你已经有了Windows10企业版的密钥,可以在“设置”中的“更新和安全”中选择“激活”来输入密钥进行...
-
- 如何恢复备份数据(备份的数据怎么恢复到手机上)
-
把备份删了的话,一键还原目前是用不了的。现在唯一的办法,是从网上下载一个数据恢复类的软件,只要的备份还没有被别的软件覆盖,是应该可以数据恢复回来的。不能保证百分之百得成功,但是恢复几率还很大的,你可以试试,操作方法首先点击手机“设置”。然后...
-
2025-11-11 06:51 off999
- 笔记本无线网卡怎么使用(笔记本无线网卡怎么使用教程)
-
笔记本无线网卡设置;第一:你要确定你的本本是否有无线上网功能,如果没有就得加个无线网卡;第二:有的话就打开无线网络接受开关;第三:程序设置主要就是在网上邻居的属性里,打开无线上网打开“网上邻居”的“属...
- 鲁大师电脑版官方下载(鲁大师电脑版官方下载安装)
-
因为鲁大师是跑分软件,它会拖慢电脑的运行速度,还会占据大量的内存,如果你的电脑配置不是太好的话,装了鲁大师只会雪上加霜,非但得不到任何优化作用,还会拖慢电脑的启动速度,造成不必要的损耗。玩游戏都会卡顿...
- win10怎么开机进入安全模式(win10开机怎么进安全模式怎么进)
-
进入Windows10安全模式有以下几种方法:方法一:使用开机高级选项1.在按下电源开机键后,持续按住F8键,直到你进入启动选项页面;2.从菜单中选择“安全模式”。方法二:使用系统配置1...
- 华硕电脑怎么重新安装系统(华硕电脑怎么重新安装系统教程)
-
第一步:备份重要数据重装系统前,务必先备份重要的个人数据。你可以将数据保存在外部存储设备上,或者使用云存储服务,确保数据安全可靠。第二步:下载系统镜像为了重装系统,你需要下载华硕笔记本电脑适用的操作系...
- 电脑显示此windows副本不是正版
-
1、第一步在电脑搜索框搜索命令提示符,鼠标右键以管理员的身份运行,2、第二步以管理员身份进去命令提示符之后输入"SLMGR-REARM",3、第三步按回车键可以看到命令已经成功重启一下...
- 电脑怎么复制粘贴按键(电脑复制粘贴按键是哪个)
-
电脑键盘上的粘贴键是:Ctrl+V按键。具体操作:1、以在excel表格中进行复制粘贴操作为例,首先选中需要复制粘贴操作的单元格。2、然后按下键盘上的“Ctrl+C”按键执行复制操作。3、然后将鼠标单...
- 笔记本黑屏但还在运行(笔记本电脑黑屏但运行)
-
具体修复方法:1、直接按下电脑机箱上的启动键让电脑重启,等待重新正常进入系统中。然后打开电脑系统盘,右键点击c盘进入属性设置面板中;2、在硬盘的属性设置中切换到工具标签;3、在查错选项中点击检查错误按...
欢迎 你 发表评论:
- 一周热门
-
-
抖音上好看的小姐姐,Python给你都下载了
-
全网最简单易懂!495页Python漫画教程,高清PDF版免费下载
-
Python 3.14 的 UUIDv6/v7/v8 上新,别再用 uuid4 () 啦!
-
python入门到脱坑 输入与输出—str()函数
-
飞牛NAS部署TVGate Docker项目,实现内网一键转发、代理、jx
-
宝塔面板如何添加免费waf防火墙?(宝塔面板开启https)
-
Python三目运算基础与进阶_python三目运算符判断三个变量
-
(新版)Python 分布式爬虫与 JS 逆向进阶实战吾爱分享
-
慕ke 前端工程师2024「完整」
-
失业程序员复习python笔记——条件与循环
-
- 最近发表
- 标签列表
-
- python计时 (73)
- python安装路径 (56)
- python类型转换 (93)
- python进度条 (67)
- python吧 (67)
- python的for循环 (65)
- python格式化字符串 (61)
- python静态方法 (57)
- python列表切片 (59)
- python面向对象编程 (60)
- python 代码加密 (65)
- python串口编程 (77)
- python封装 (57)
- python写入txt (66)
- python读取文件夹下所有文件 (59)
- python操作mysql数据库 (66)
- python获取列表的长度 (64)
- python接口 (63)
- python调用函数 (57)
- python多态 (60)
- python匿名函数 (59)
- python打印九九乘法表 (65)
- python赋值 (62)
- python异常 (69)
- python元祖 (57)
