自动读取单位某系统数据

单位的环境监控系统数据库上移到省公司,省公司又不肯开放权限去读取数据。在同事的提醒下,使用爬虫来抓取数据。为此我又得重新开始学习新东西。这次的坑是python,基础学习、建站、抓虫。折腾两个月,终于在今天完成了对系统数据的读取。

注:读取的是公开的数据,不涉及保密内容,目的是为了将人工转为电脑自动读取

思路大概如下:

  • 通过浏览器的开发者工具分析系统的请求数据,发现需要cookie,这个cookie登录后才可用。所以使用selenium来打开网站,并使用帐号密码自动登录。
  • 自动登录时需要验证码,百度了一个比较简单的识别方法,成功率不高,但设置为自动重复登录,可以挂着去喝茶,也不麻烦。使用的工具是tesseract,和pytesseract、pillow。
  • 判断是否登录成功,若失败则再次登录,成功则构造、发送请求包、获取数据。

说明:

  • 所需软件如下:
  1. tesseract识别软件:需要在系统中安装,方法很简单,就下载安装。
  2. 使用selenium时,需下载webdriver,地址:https://docs.seleniumhq.org/download下载解压至/usr/local/bin中(我使用的是Mac,windows和lunix好像是解压至某路径,并设置系统path。具体请百度)
  3. python所需包的安装如下(我好像安装完pytesseract就带有pillow):
pip install selenium
pip install pytesseract
  • 构造headers和data,我建议是通过浏览器的开发者工具将请求包拷贝为curl格式,这里面就包含了所有的信息。
  • curl包含了请求包的所有信息,但是目前未找到方便的方法来提取并构造为requests的headers和data格式,只好用手工构造。
  • 在构造headers时,因为不知如何去计算'Content-Length',所以去掉了这一项,对数据抓取没有影响,也就暂且不管。
  • 识别验证码那一段,我基本上照抄这篇博客的:https://blog.csdn.net/weixin_38374974/article/details/80152899
  • 数据处理部分比较简单,只是显示抓到的数据包(json格式)和读取其中某一项。

当前只实现功能,代码尚未优化。如下:

# -*- coding:utf-8 -*-
from selenium import webdriver
from PIL import Image, ImageEnhance
import pytesseract, time, re, requests, json


def getvalicode(driver):
    """
    获取验证码
    :param driver:
    :return: 返回验证码,类型:str
    """
    driver.save_screenshot('./code/1.png')

    # 裁剪验证码图片,并保存到文件中
    ran = Image.open("./code/1.png")
    box = (1490, 795, 1630, 850)    # 获取验证码位置,自动定位不是很明白,就使用了手动定位,代表(左,上,右,下)
    ran.crop(box).save('./code/2_1.png')

    # 处理验证码图片
    imagecode = Image.open('./code/2_1.png')
    sharp_img = ImageEnhance.Contrast(imagecode).enhance(2.0)  # 图像增强,二值化
    sharp_img.save("./code/2_2.png")
    sharp_img.load()  # 对比度增强

    # 识别验证码
    # time.sleep(2)
    vcode = pytesseract.image_to_string(sharp_img)
    vcode = re.sub('[^0-9a-zA-Z]', '', vcode)
    print('验证码:', vcode, end='\t')
    return vcode


def login(driver, url):
    """
    登陆系统
    :param driver:
    :param url:
    :return:True:登录成功;False:登录失败
    """
    driver.get(url)
    # driver.implicitly_wait(15)
    driver.maximize_window()
    time.sleep(5)  # 等待浏览器打开页面

    vcode = getvalicode(driver)
    while len(vcode) != 4:
        driver.refresh()
        driver.maximize_window()
        time.sleep(5)  # 等待浏览器打开页面
        vcode = getvalicode(driver)

    # 获取用户名、密码、验证码、登录按键
    username = driver.find_element_by_id('sAccount')
    userpwd = driver.find_element_by_id('sPasswd')
    valicode = driver.find_element_by_id('sValidataCode')
    login_button = driver.find_element_by_id('LoginButton')

    # 设置用户帐号密码
    username.clear()
    username.send_keys('****')
    userpwd.clear()
    userpwd.send_keys('*******')
    valicode.clear()
    valicode.send_keys(vcode)
    time.sleep(1)
    login_button.click()
    time.sleep(10)

    return driver.title == '*****'  # 此处通过页面的title是否为某个字段来判断是否登录成功


def getcookie(driver):
    """
    获取cookies
    :param driver:
    :return: 返回cookie 类型是str
    """
    cookies = driver.get_cookies()
    cookie = cookies[0]['name'] + '=' + cookies[0]['value']
    return cookie


def makeheaders(driver):
    """
    构建Headers 可通过开发者工具中的请求包获取(建议使用将该请求包拷贝为curl格式,curl格式包含了header、param、data)
    :param driver:
    :return: headers
    """
    cookie = getcookie(driver)
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
        'Origin': 'http://******',
        'Host': '*****',
        'Accept': 'text/plain, */*; q=0.01',
        'Connection': 'keep-alive',
        'Accept-Language': 'zh-cn',
        'Accept-Encoding': 'gzip, deflate',
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.1.2 Safari/605.1.15',
        'Referer': 'http://********/index.jsp?urlPKIGuid=1535609520181',
        # 'Content-Length':287,
        'X-Requested-With': 'XMLHttpRequest',
        'Cookie': cookie
    }
    return headers


def makeurldata():
    """
    构建请求包中的data
    :return:
    """
    urldata = json.dumps({'44-F0-FF-78-F4-25': {'2120001': ['221200190010', '231200020010']}})  # {'局站编码':{'采集器编码':['信号编码','']}}
    return urldata


def getdata(headers, urldata):
    """
    获取数据
    :param headers: 请求包头
    :param urldata: 请求包的data
    :return: 
    """
    url = 'http://*********!getRuntimeData.action'
    session = requests.Session()
    r = session.post(url, headers=headers, data={'SU': urldata})
    a = r.json()
    print(a, '\n*************')
    for i in a:
        for j in i:
            print(i[j]['MeasuredVal'])


def main():
    url = 'http://*****/login.jsp'
    try:
        driver = webdriver.Firefox()
        # 重复尝试登录
        islogined = login(driver, url)
        while not islogined:
            islogined = login(driver, url)
        print('\n', driver.title, '成功登录!')
        # 读取数据
        headers = makeheaders(driver)
        urldata = makeurldata()
        getdata(headers, urldata)
    finally:
        driver.close()


if __name__ == '__main__':
    main()

如果本文对您有帮助,请给我留个言。谢谢!

你可能感兴趣的