今天学习如何自动登录12306,但是有点难,很多东西不是很知道原理,视频里的老师也有很多是直接粘贴过来的代码。
先来看看效果叭!速度有加快,实际上没那么快识别:
首先,打开12306的网页,看一下该如何自动登录。
这个就是刚进入时的页面,默认是二维码扫码的界面,我们要将他切换成账号密码登录。
切换完以后,会发现下面有一个验证码,这个时候就又要用到我们的超级鹰了,但是我们会发现得到的爬取到的图片的src是空值,这是因为验证码是动态加载的,每一次打开都是不一样的,此时我们的思路就是将整个页面截取下来,然后再截取出验证码部分的图片,放在文件中,然后用超级鹰去破解验证码。
首先,我们找到超级鹰的源码,将他粘贴到我们的文件中方便直接使用。
#!/usr/bin/env python
# coding:utf-8
import requests
from hashlib import md5
class Chaojiying_Client(object):
def __init__(self, username, password, soft_id):
self.username = username
password = password.encode('utf8')
self.password = md5(password).hexdigest()
self.soft_id = soft_id
self.base_params = {
'user': self.username,
'pass2': self.password,
'softid': self.soft_id,
}
self.headers = {
'Connection': 'Keep-Alive',
'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
}
def PostPic(self, im, codetype):
"""
im: 图片字节
codetype: 题目类型 参考 http://www.chaojiying.com/price.html
"""
params = {
'codetype': codetype,
}
params.update(self.base_params)
files = {'userfile': ('ccc.jpg', im)}
r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files,
headers=self.headers)
return r.json()
def ReportError(self, im_id):
"""
im_id:报错题目的图片ID
"""
params = {
'id': im_id,
}
params.update(self.base_params)
r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
return r.json()
chaojiying = Chaojiying_Client('4415232000', '4415232000', '915722') # 用户中心>>软件ID 生成一个替换 96001
im = open('png.jpg', 'rb').read() # 本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
print(chaojiying.PostPic(im, 9004)['pic_str']) # 1902 验证码类型 官方网站>>价格体系 3.4+版 print 后要加())
在下面开始打我们的代码。
下面是这次要用到的包
from selenium import webdriver
from selenium.webdriver import ActionChains
from time import sleep
from PIL import Image # 截取图片用的
接下来访问12306网页
# 创建一个浏览器实例
bro = webdriver.Chrome(executable_path="chromedriver.exe")
# 浏览器窗口最大化
bro.maximize_window()
bro.get("https://kyfw.12306.cn/otn/resources/login.html")
sleep(1)
再找到“账号登录”的标签,点击切换到账号密码登录。
login = bro.find_element_by_css_selector("body > div.login-panel > div.login-box > ul > li.login-hd-account > a")
login.click()
sleep(2)
此时的界面是这样的
现在要就要截取整个屏幕了,这里用到的方法是save_screenshot(“图片保存路径”)
bro.save_screenshot("./aa.png")
运行后看看结果:
成功截取到了整个页面,但是要记得先将浏览器窗口最大化,使用maximize_window()来实现(在上面的代码中已经用到)。
现在对验证码的位置做个定位,在这里,图片的location应该是图片左上角的坐标,所有它的右下角的x坐标就是location中的x加上图片大小中的宽度,图片右下角的y坐标应该是location中的y坐标加上图片的高度
code_img = bro.find_element_by_id("J-loginImg")
location = code_img.location # 验证码左上角的x,y坐标
size = code_img.size # 宽度
c = (int(location["x"]),int(location["y"]),int(location["x"]+int(size["width"])),
int(location["y"])+int(size["height"]))
那么接下来就要在我们保存的这张图片的基础上裁剪出验证码的图片,需要用到的是PIL中的Image库。
i = Image.open("aa.png")
code_img_name = "code.png"
frame = i.crop(rangle)
frame.save(code_img_name)
现在就把验证码给裁剪了出来
接下来只要让超级鹰去识别我们保存的验证码图片就可以了.
我们会发现,超级鹰返回的是要点击的图片的坐标,而且是用"|"符号隔开的,比如 112,98|36,94,所以我们要用split方法将两个坐标分别取出,然后再将他们的x,y也分别取出,放进一个列表中,最后再将这些列表放入all_list这个大的列表中中,遍历这个总的列表,就可以得到每个坐标了(这里有点复杂,还需要多理解下)
all_list = []
if "|" in result:
list1 = result.split("|")
print(list1)
count1 = len(list1)
for i in range(count1):
xy_list = []
x = int(list1[i].split(",")[0])
y = int(list1[i].split(",")[1])
xy_list.append(x)
xy_list.append(y)
all_list.append(xy_list)
else:
x = int(result.split(",")[0])
y = int(result.split(",")[1])
xy_list = []
xy_list.append(x)
xy_list.append(y)
all_list.append(xy_list)
for l in all_list:
x = l[0]
y = l[1]
ActionChains(bro).move_to_element_with_offset(code_img, x, y).click().perform()
sleep(1)
我们还要输入账号和密码.
username = bro.find_element_by_id("J-userName")
username.send_keys("166****2464")
pwd = bro.find_element_by_id("J-password")
pwd.send_keys("*********")
差不多就这样,最后贴一个完整的代码,靠这个方法也成功的登录了学校的教务系统.
#!/usr/bin/env python
# coding:utf-8
import requests
from hashlib import md5
class Chaojiying_Client(object):
def __init__(self, username, password, soft_id):
self.username = username
password = password.encode('utf8')
self.password = md5(password).hexdigest()
self.soft_id = soft_id
self.base_params = {
'user': self.username,
'pass2': self.password,
'softid': self.soft_id,
}
self.headers = {
'Connection': 'Keep-Alive',
'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
}
def PostPic(self, im, codetype):
"""
im: 图片字节
codetype: 题目类型 参考 http://www.chaojiying.com/price.html
"""
params = {
'codetype': codetype,
}
params.update(self.base_params)
files = {'userfile': ('ccc.jpg', im)}
r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files,
headers=self.headers)
return r.json()
def ReportError(self, im_id):
"""
im_id:报错题目的图片ID
"""
params = {
'id': im_id,
}
params.update(self.base_params)
r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
return r.json()
from selenium import webdriver
from selenium.webdriver import ActionChains
from time import sleep
from PIL import Image
# 创建一个浏览器实例
bro = webdriver.Chrome(executable_path="chromedriver.exe")
# 浏览器窗口最大化
bro.maximize_window()
bro.get("https://kyfw.12306.cn/otn/resources/login.html")
sleep(0.2)
login = bro.find_element_by_css_selector("body > div.login-panel > div.login-box > ul > li.login-hd-account > a")
login.click()
sleep(2)
# 截取屏幕
bro.save_screenshot("./aa.png")
code_img = bro.find_element_by_id("J-loginImg")
location = code_img.location # 验证码左上角的x,y坐标
size = code_img.size # 宽度
rangle = (int(location["x"]), int(location["y"]), int(location["x"] + int(size["width"])),
int(location["y"]) + int(size["height"]))
# 局部区域裁剪
i = Image.open("./aa.png")
code_img_name = "./code.png"
frame = i.crop(rangle)
frame.save(code_img_name)
username = bro.find_element_by_id("J-userName")
username.send_keys("166****2464")
pwd = bro.find_element_by_id("J-password")
pwd.send_keys("************")
chaojiying = Chaojiying_Client('4415232000', '4415232000', '915722') # 用户中心>>软件ID 生成一个替换 96001
im = open('code.png', 'rb').read() # 本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
result = chaojiying.PostPic(im, 9004)['pic_str'] # 1902 验证码类型 官方网站>>价格体系 3.4+版 print 后要加())
all_list = []
if "|" in result:
list1 = result.split("|")
print(list1)
count1 = len(list1)
for i in range(count1):
xy_list = []
x = int(list1[i].split(",")[0])
y = int(list1[i].split(",")[1])
xy_list.append(x)
xy_list.append(y)
all_list.append(xy_list)
else:
x = int(result.split(",")[0])
y = int(result.split(",")[1])
xy_list = []
xy_list.append(x)
xy_list.append(y)
all_list.append(xy_list)
for l in all_list:
x = l[0]
y = l[1]
ActionChains(bro).move_to_element_with_offset(code_img, x, y).click().perform()
sleep(1)
login_button = bro.find_element_by_id("J-login")
login_button.click()
sleep(1)
block = bro.find_element_by_id("nc_1_n1z")
action = ActionChains(bro)
action.click_and_hold(block)
action.move_by_offset(300, 0).perform()
sleep(0.5)
sleep(5)
bro.quit()
教务系统登录的原理也差不多,而且更加简单,因为不是点击图片,知识简单的识别数字验证码.