写在前面:这个程序不是一个人能在短时间内完成的,感谢达纳,王哥的支持帮助。也感谢小平老师,没有压迫,就没有项目。

简介:这是一篇很硬核的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+'\\'+iimage=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')continueprint('第%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 Kclass SimpleVGGNet:@staticmethoddef build(width, height, depth, classes):model = Sequential()inputShape = (height, width, depth)chanDim = -1if K.image_data_format() == "channels_first":inputShape = (depth, height, width)chanDim = 1# CONV => RELU => POOLmodel.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 => POOLmodel.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 => POOLmodel.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改为1024model.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 Kclass SimpleVGGNet:@staticmethoddef build(width, height, depth, classes):model = Sequential()inputShape = (height, width, depth)chanDim = -1if K.image_data_format() == "channels_first":inputShape = (depth, height, width)chanDim = 1# CONV => RELU => POOLmodel.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 => POOLmodel.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*3model.add(Dropout(0.25))#yk增加了泛化能力# (CONV => RELU) * 3 => POOLmodel.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改为1024model.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 model
CNN模型搭建完毕
开始对图形部分进行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.5、这里要提一下啊,我们的做法稍微巧了一点,我们先让机器对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+'\\'+iimage=np.array(Image.open(img_path))image = cv2.resize(image, (80,80))image = image.astype("float") / 255.0image = 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:continue
5、进行到这里,基本上绝大部分工作已经做完了,后面的就是把前面的结果和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 formataddrmy_sender='user'    # 发件人邮箱账号
my_pass = 'password'              # 发件人邮箱密码
def mail(address):my_user=addressret=Truetry: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=Falseret=Falsereturn
#获取用户的输入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.0image = 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.0image = 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_sourcehtml=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()#登录12306login(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,妈妈再也不用担心我买不到票了~

从爬虫构建数据集到CNN模型的验证码识别,一步一步搭建基于Python的PC个人端12306抢票程序相关推荐

  1. python爬虫抢火车票_如何用python写一个简单的12306抢票软件|python 爬火车票 教程...

    python 如果抓取验证码图片 类似12306的登录验证码图片 这个以前做次.最大的麻烦是码的识别算法的识别率太低.12306那种网站登陆错3次就限制你20分钟.所以除非你有33%以上的识别率否则不 ...

  2. 从零开始,手把手教你使用Keras和TensorFlow构建自己的CNN模型

    最近学习CNN,搭建CNN模型时看网上鱼龙混杂的博客走了不少歪路,决定自己来总结一下. 注意本教程未必对所有版本有效,请根据需要的版本适当调整.文章中配置的环境是Python 3.8.12 ,Tens ...

  3. Python:基于Python爬虫技术的抢票程序及其实现

    临近放假,相信我们每天都在群聊里或者朋友圈看到一些帮忙抢火车票的信息.看到朋友们抢回家的车票这么辛(bei)苦(can),结合圈里一些前辈的指点,抱着学习的心态用Python做了一个简单的自动化抢票程 ...

  4. 爬虫实战篇---12306抢票爬虫

    12306抢票爬虫 先直接上一下效果图吧: 图片上信息是抢票成功后的界面 1.技术路线 selenium + chromedriver 2.思路分析 (1).模拟浏览器登录抢票界面,手动进行登录 (2 ...

  5. Python爬虫实战之12306抢票

    12306抢票 前言 一.爬虫是什么? 二.使用步骤 1.引入库 2.爬虫代码 3.城市编码 4.主程序 总结 前言 提示:用python实现简单的12306余票查询 提示:以下是本篇文章正文内容,下 ...

  6. 深度学习初学者使用Keras构建和部署CNN模型

    https://www.toutiao.com/a6666072496283845134/ 如果你在黑框中画一个图形.您应该从模型中得到一个预测. 恭喜!您已构建并部署了第一个CNN模型.继续尝试写出 ...

  7. 基于深度学习的轴承故障识别-构建基础的CNN模型

    上回书说到,处理序列的基本深度学习算法分别是循环神经网络(recurrent neural network)和一维卷积神经网络(1D convnet).上篇构建了基础的LSTM模型,这一篇自然轮到CN ...

  8. Python爬虫实战之12306抢票开源

    今天就和大家一起来讨论一下python实现12306余票查询(pycharm+python3.7),一起来感受一下python爬虫的简单实践 我们说先在浏览器中打开开发者工具(F12),尝试一次余票的 ...

  9. python 12306抢票_Python爬虫实战:12306抢票开源!

    今天就和大家一起来讨论一下python实现12306余票查询(pycharm+python3.7),一起来感受一下python爬虫的简单实践 我们说先在浏览器中打开开发者工具(F12),尝试一次余票的 ...

最新文章

  1. 2019 阿里云峰会·北京站正式启动,互联网出海分论坛报名开启...
  2. Linux 开机显示:welcome to emergency mode
  3. 如何理解Linux shell中的“2>1”(将文件描述2(标准错误输出)的内容重定向到文件描述符1(标准输出))(尼玛>符号竟然不支持搜索,害我搜搜不到,只能搜)
  4. 备考信息系统项目管理师5点必过经验
  5. 如何设置使eclipse修改代码不重启tomcat
  6. 10.14 socket 网络编程
  7. 自定义控件学习,优秀网站推荐
  8. mysql 数据迁移_CentOS7迁移Mysql数据库文件
  9. windows mysql导入sql文件
  10. html5小游戏源码_[源码和报告分享]基于HTML5实现的贪吃蛇小游戏
  11. maven项目jdk版本配置及常见错误Error:java: 无效的目标发行版: 8解决
  12. java图片像素90翻转_java后台解决上传图片翻转90的问题,有demo,经过测试可用...
  13. LOJ2420「NOIP2015」神奇的幻方
  14. Codeforces 1437 F. Emotional Fishermen —— dp
  15. 【评测】照胶的仪器选购
  16. 计算机经典书籍大全(内含下载方式)
  17. PHP GD库 生成图片水印
  18. 【个人记录 | 研二预答辩】
  19. 用于图像降噪的卷积自编码器
  20. 【ybt高效进阶1-5-2】【luogu P3456】山峰和山谷 / GRZ-Ridges and Valleys

热门文章

  1. 傻瓜式文章一键伪原创工具
  2. android立体3D效果_3D立体画,让你身临其境
  3. MUI-grid(栅格),超小屏xs和小屏幕sm
  4. python和java哪个好就业-计算机专业选Java和Python哪个前景好点?
  5. 唐山校友会会长苏伟与徐飞校长的一次短信交流
  6. Flutter 左右菜单联动
  7. 蓝桥杯真题——猜年龄python讲解
  8. 在线电子书阅读微信小程序 毕业设计(4)图书详细页-图书目录
  9. 2022年全球及中国工程机械租赁行业头部企业市场占有率及排名调研报告
  10. GitHub 9K Star!Apollo作者手把手教你微服务配置中心之道