单位的环境监控系统数据库上移到省公司,省公司又不肯开放权限去读取数据。在同事的提醒下,使用爬虫来抓取数据。为此我又得重新开始学习新东西。这次的坑是python,基础学习、建站、抓虫。折腾两个月,终于在今天完成了对系统数据的读取。
(注:读取的是公开的数据,不涉及保密内容,目的是为了将人工转为电脑自动读取
)
思路大概如下:
- 通过浏览器的开发者工具分析系统的请求数据,发现需要cookie,这个cookie登录后才可用。所以使用selenium来打开网站,并使用帐号密码自动登录。
- 自动登录时需要验证码,百度了一个比较简单的识别方法,成功率不高,但设置为自动重复登录,可以挂着去喝茶,也不麻烦。使用的工具是tesseract,和pytesseract、pillow。
- 判断是否登录成功,若失败则再次登录,成功则构造、发送请求包、获取数据。
说明:
- 所需软件如下:
- tesseract识别软件:需要在系统中安装,方法很简单,就下载安装。
- 使用selenium时,需下载webdriver,地址:https://docs.seleniumhq.org/download下载解压至/usr/local/bin中(我使用的是Mac,windows和lunix好像是解压至某路径,并设置系统path。具体请百度)
- 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()
如果本文对您有帮助,请给我留个言。谢谢!