Python爬虫学习之selenium项目1---12306模拟登录和验证码识别

Selenium是一款基于浏览器自动化的工具,使用它可以模拟浏览器进行网页访问,对于爬取一些动态加载数据的网站算是一个非常好用的工具了。

今天练习的项目就是基于Selenium对12306进行模拟登陆,并对其中的登陆验证码通过调用第三方平台超级鹰来进行自动识别点击,完成自动登陆。

一、首先,先对要爬取的网站12306进行一个分析。官方地址:https://www.12306.cn/index

  1. 来到首页,我们可以看到在左上角有个登陆,点击之后就会来到登陆页面。(之前尝试过直接用Selenium访问登陆页面的URL地址,但是返回的数据会出现错误的网页源代码)。所以,为了保证稳定,还是通过点击首页的按钮来到登陆页面再进行模拟登陆的操作。
  2. 我们要进行的是账号密码登录,所以在来到登陆页面后,还要进行一次点击“账号登陆”。在账号登陆页面我们可以看到,在登陆前需要完成验证码识别才能进行登陆
  3. Python爬虫学习之selenium项目1---12306模拟登录和验证码识别_第1张图片Python爬虫学习之selenium项目1---12306模拟登录和验证码识别_第2张图片Python爬虫学习之selenium项目1---12306模拟登录和验证码识别_第3张图片

二、分析完网站结构后,开始编写代码.总体可分为3个步骤。(1.跳转至登陆界面。2.使用第三方对验证码进行识别点击。3.输入参数进行模拟登陆)

  1. 先使用Selenium对12306网页进行点击到登陆界面

from selenium import webdriver
from selenium.webdriver import ActionChains #用于浏览器执行图片点击等操作通过动作链调用
import time                    #对于一些网页元素需要加载时间,以及正常速度访问这里通过简单的time.sleep()进行等待
from PIL import Image          #对于后续对验证码图片需要进行保存读取的操作
from chaojiying import chaojiying    #第三方工具超级鹰的调用代码
browser = webdriver.Chrome(executable_path='./chromedriver')

#起始方法
def start_get():
    url = 'https://www.12306.cn/index/'
    browser.get(url)
    #1、从首页点击登陆按钮--->登陆界面
    account_btn = browser.find_element_by_css_selector('#J-header-login > a:nth-child(1)')
    time.sleep(2)

    #点击进入到登陆界面
    account_btn.click()
    time.sleep(3)
    #点击到账户密码登陆界面
    browser.find_element_by_css_selector(
        'body > div.login-panel > div.login-box > ul > li.login-hd-account > a').click()
    time.sleep(2)
    #2、先做验证码识别的操作
    code_distinguish()
    #3、在登陆界面点击账户密码登陆,然后输入账户密码
    login_page()

2.首先可先分为以下3个大步骤,以及每个步骤之中的细节分析

  • 验证码图片的截取
    1. 拿到网页中验证码图片的节点定位
    2. 通过location/size方法获取到验证码图片的像素位置和尺寸大小。通过计算得出左上角和右下角2个点的坐标位置,后将其封装到一个元祖中保存。
    3. 进行验证码图片截图操作(后续上传第三方平台只需要上传验证码图片即可)
      1. 整张网页页面截图
      2. 调用crop函数通过前面得到的左上角和右下角2个坐标位置,在整张网页中进行定位截图(就可以得到单独的验证码图片了)
  • 使用第三方工具进行识别,解析参数
    1. 第三方工具的使用可以通过去官网下载示例代码学习即可。这里这是简单的将截图下来的验证码图片通过给定的方法上传给第三方平台即可
    2. 对于第三方平台返回的数据是需要我们自己手动来解析和存储的。这里将返回出来的坐标位置取出后,还需要对其进行整理,封装到列表中,方便调用。
  • 浏览器动作链点击图片
    1. 因为这里超级鹰返回给我们的图片数据坐标是以验证码这张图片作为参照物,并不是整张网页截图。
    2. 所以Selenium中要调用动作链要先进行区域选择,定位到网页中这张验证码图片的节点。再从超级鹰返回的数据中解析符合要去的图片的位置坐标,然后进行点击
      ActionChains(browser).move_to_element_with_offset(code_img,x,y).click().perform()
      
#2、先做验证码识别的操作
def code_distinguish():
    #拿到验证码图片的节点定位
    code_img = browser.find_element_by_css_selector('#J-loginImg')
    #2.1这里的location是获取这张图片的左上角的左边,返回的是字典封装的x,y的值.基于整个网页返回给你的值
    loca = code_img.location#x,y{'x': 856, 'y': 284}
        #1.所以,一般还需要拿到这张图片的尺寸,即长和宽
    size = code_img.size    #{'height': 188, 'width': 300}
    #2.2接下来,就是需要做截屏。
        #1.需要定位左上角和右下角2个点的左边,即共4个.保存在元祖中
    code_position = (int(loca['x']), int(loca['y']), int(loca['x']+size['width']),int(loca['y']+size['height']))
        #2.调用截图功能,将图片截取下去。这个功能在无头浏览器中应用的比较多,比如你想验证无头浏览器是否访问到了你想要的页面,就可以使用截图来测试观看
        #3.截取当前浏览器打开的这张页面的整个图像(记住,是整个浏览器打开的页面的图像)
    browser.save_screenshot('./12306/aa.png')
        #4.用Image包打开这张截图
    i = Image.open('./12306/aa.png')
        #5.给要截取的验证码图片取名
    code_img_name = './12306/code.png'
        #6.调用crop函数,在这大的图片里面通过2个点对其内部的内容进行截图
    frame = i.crop(code_position)
        #7.将截取下来的图片存到目录中,并取名
    frame.save(code_img_name)

    #2.3调用超级鹰进行识别,拿到返回的结果
    im = open('./12306/code.png', 'rb').read()
        #{'err_no': 0, 'err_str': 'OK', 'pic_id': '3095313233818200001', 'pic_str': '25,143|260,134', 'md5': '29c583678bdf3eb09ece5726fcfaa285'}
    result = chaojiying.PostPic(im, 9004)['pic_str']
        #1.解析超级鹰返回的数据
            #先声明一个数组用来存储所有的图片坐标,想要的是这种结果[[25,143],[260,134]]
    all_list = []
    if '|' in result:
        list1 = result.split('|')   #-->['25,143', '260,134']
        count_list = len(list1)
        for i in range(count_list):
            xy = []
            x = int(list1[i].split(',')[0])
            y = int(list1[i].split(',')[0])
            xy.append(x)
            xy.append(y)
            all_list.append(xy)
        #针对的是只有一组图片符合的数据['25,143']
    else:
        xy = []
        x = int(result.split(',')[0])
        y = int(result.split(',')[1])
        xy.append(x)
        xy.append(y)
        all_list.append(xy)
    #2.4拿到返回结果后,需要调用动作链,以此点击选中的图片坐标.
    action = ActionChains(browser)
    for i in all_list:
        x = i[0]
        y = i[1]
    #2.4.1:这样使用move_to_element_with_offset。表示选中一个区域再进行移动。因为返回的坐标数值是以发送的图片作为参照,而我们需要进行点击是要在整张页面中。为了解决这个问题,需要把范围先切换到页面中的图片上
        ActionChains(browser).move_to_element_with_offset(code_img,x,y).click().perform()
    time.sleep(5)

3.输入用户名和密码进行登陆提交

  1. 拿到网页中用户名和密码的输入文本框的节点,调用send.keys( )方法进行传参
  2. 点击登陆按钮即可完成
#3、在登陆界面点击账户密码登陆,然后输入账户密码
def login_page():

    #2.拿到用户名和密码输入框
    username_input = browser.find_element_by_css_selector('#J-userName')
    username_input.send_keys('')
    time.sleep(3)

    password_input = browser.find_element_by_css_selector('#J-password')
    password_input.send_keys('')

    time.sleep(3)

    #4.点击登陆按钮
    login_btn = browser.find_element_by_css_selector('#J-login').click()

if __name__ == '__main__':
    start_get()

超级鹰调用代码如下:

#!/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('user_name', 'pass_word', 'ID')	#用户中心>>软件ID 生成一个替换 96001


此篇文章是通过学习b站2019老男孩全栈18期 爬虫+数据分析参考所总结。在此表示感谢。感兴趣的同学也自行观看,讲的还是比较细,对初学者也是比较友好的。b站传送门:https://www.bilibili.com/video/av53878299?p=1

你可能感兴趣的:(selenium,python)