單位的環(huán)境監(jiān)控系統(tǒng)數(shù)據(jù)庫(kù)上移到省公司摊欠,省公司又不肯開(kāi)放權(quán)限去讀取數(shù)據(jù)。在同事的提醒下柱宦,使用爬蟲來(lái)抓取數(shù)據(jù)凄硼。為此我又得重新開(kāi)始學(xué)習(xí)新東西。這次的坑是python,基礎(chǔ)學(xué)習(xí)、建站税肪、抓蟲阅悍。折騰兩個(gè)月,終于在今天完成了對(duì)系統(tǒng)數(shù)據(jù)的讀取。
(注:讀取的是公開(kāi)的數(shù)據(jù),不涉及保密內(nèi)容,目的是為了將人工轉(zhuǎn)為電腦自動(dòng)讀取
)
思路大概如下:
- 通過(guò)瀏覽器的開(kāi)發(fā)者工具分析系統(tǒng)的請(qǐng)求數(shù)據(jù)尼斧,發(fā)現(xiàn)需要cookie,這個(gè)cookie登錄后才可用试吁。所以使用selenium來(lái)打開(kāi)網(wǎng)站棺棵,并使用帳號(hào)密碼自動(dòng)登錄。
- 自動(dòng)登錄時(shí)需要驗(yàn)證碼熄捍,百度了一個(gè)比較簡(jiǎn)單的識(shí)別方法烛恤,成功率不高,但設(shè)置為自動(dòng)重復(fù)登錄余耽,可以掛著去喝茶缚柏,也不麻煩。使用的工具是tesseract碟贾,和pytesseract币喧、pillow。
- 判斷是否登錄成功袱耽,若失敗則再次登錄杀餐,成功則構(gòu)造、發(fā)送請(qǐng)求包朱巨、獲取數(shù)據(jù)史翘。
說(shuō)明:
- 所需軟件如下:
- tesseract識(shí)別軟件:需要在系統(tǒng)中安裝,方法很簡(jiǎn)單,就下載安裝恶座。
- 使用selenium時(shí),需下載webdriver沥阳,地址:https://docs.seleniumhq.org/download下載解壓至/usr/local/bin中(我使用的是Mac跨琳,windows和lunix好像是解壓至某路徑,并設(shè)置系統(tǒng)path桐罕。具體請(qǐng)百度)
- python所需包的安裝如下(我好像安裝完pytesseract就帶有pillow):
pip install selenium pip install pytesseract
- 構(gòu)造headers和data脉让,我建議是通過(guò)瀏覽器的開(kāi)發(fā)者工具將請(qǐng)求包拷貝為curl格式,這里面就包含了所有的信息功炮。
- curl包含了請(qǐng)求包的所有信息溅潜,但是目前未找到方便的方法來(lái)提取并構(gòu)造為requests的headers和data格式,只好用手工構(gòu)造薪伏。
- 在構(gòu)造headers時(shí)滚澜,因?yàn)椴恢绾稳ビ?jì)算'Content-Length',所以去掉了這一項(xiàng)嫁怀,對(duì)數(shù)據(jù)抓取沒(méi)有影響设捐,也就暫且不管。
- 識(shí)別驗(yàn)證碼那一段塘淑,我基本上照抄這篇博客的:https://blog.csdn.net/weixin_38374974/article/details/80152899
- 數(shù)據(jù)處理部分比較簡(jiǎn)單萝招,只是顯示抓到的數(shù)據(jù)包(json格式)和讀取其中某一項(xiàng)。
當(dāng)前只實(shí)現(xiàn)功能存捺,代碼尚未優(yōu)化槐沼。如下:
# -*- coding:utf-8 -*-
from selenium import webdriver
from PIL import Image, ImageEnhance
import pytesseract, time, re, requests, json
def getvalicode(driver):
"""
獲取驗(yàn)證碼
:param driver:
:return: 返回驗(yàn)證碼,類型:str
"""
driver.save_screenshot('./code/1.png')
# 裁剪驗(yàn)證碼圖片捌治,并保存到文件中
ran = Image.open("./code/1.png")
box = (1490, 795, 1630, 850) # 獲取驗(yàn)證碼位置,自動(dòng)定位不是很明白岗钩,就使用了手動(dòng)定位,代表(左肖油,上凹嘲,右,下)
ran.crop(box).save('./code/2_1.png')
# 處理驗(yàn)證碼圖片
imagecode = Image.open('./code/2_1.png')
sharp_img = ImageEnhance.Contrast(imagecode).enhance(2.0) # 圖像增強(qiáng)构韵,二值化
sharp_img.save("./code/2_2.png")
sharp_img.load() # 對(duì)比度增強(qiáng)
# 識(shí)別驗(yàn)證碼
# time.sleep(2)
vcode = pytesseract.image_to_string(sharp_img)
vcode = re.sub('[^0-9a-zA-Z]', '', vcode)
print('驗(yàn)證碼:', vcode, end='\t')
return vcode
def login(driver, url):
"""
登陸系統(tǒng)
:param driver:
:param url:
:return:True:登錄成功;False:登錄失敗
"""
driver.get(url)
# driver.implicitly_wait(15)
driver.maximize_window()
time.sleep(5) # 等待瀏覽器打開(kāi)頁(yè)面
vcode = getvalicode(driver)
while len(vcode) != 4:
driver.refresh()
driver.maximize_window()
time.sleep(5) # 等待瀏覽器打開(kāi)頁(yè)面
vcode = getvalicode(driver)
# 獲取用戶名周蹭、密碼、驗(yàn)證碼疲恢、登錄按鍵
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')
# 設(shè)置用戶帳號(hào)密碼
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 == '*****' # 此處通過(guò)頁(yè)面的title是否為某個(gè)字段來(lái)判斷是否登錄成功
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):
"""
構(gòu)建Headers 可通過(guò)開(kāi)發(fā)者工具中的請(qǐng)求包獲刃桌省(建議使用將該請(qǐng)求包拷貝為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():
"""
構(gòu)建請(qǐng)求包中的data
:return:
"""
urldata = json.dumps({'44-F0-FF-78-F4-25': {'2120001': ['221200190010', '231200020010']}}) # {'局站編碼':{'采集器編碼':['信號(hào)編碼','']}}
return urldata
def getdata(headers, urldata):
"""
獲取數(shù)據(jù)
:param headers: 請(qǐng)求包頭
:param urldata: 請(qǐng)求包的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()
# 重復(fù)嘗試登錄
islogined = login(driver, url)
while not islogined:
islogined = login(driver, url)
print('\n', driver.title, '成功登錄!')
# 讀取數(shù)據(jù)
headers = makeheaders(driver)
urldata = makeurldata()
getdata(headers, urldata)
finally:
driver.close()
if __name__ == '__main__':
main()
如果本文對(duì)您有幫助,請(qǐng)給我留個(gè)言宛畦。謝謝瘸洛!