單位的環(huán)境監(jiān)控系統(tǒng)數(shù)據(jù)庫上移到省公司,省公司又不肯開放權(quán)限去讀取數(shù)據(jù)。在同事的提醒下,使用爬蟲來抓取數(shù)據(jù)。為此我又得重新開始學(xué)習(xí)新東西。這次的坑是python,基礎(chǔ)學(xué)習(xí)、建站、抓蟲。折騰兩個月,終于在今天完成了對系統(tǒng)數(shù)據(jù)的讀取。
(注:讀取的是公開的數(shù)據(jù),不涉及保密內(nèi)容,目的是為了將人工轉(zhuǎn)為電腦自動讀取)
思路大概如下:
- 通過瀏覽器的開發(fā)者工具分析系統(tǒng)的請求數(shù)據(jù),發(fā)現(xiàn)需要cookie,這個cookie登錄后才可用。所以使用selenium來打開網(wǎng)站,并使用帳號密碼自動登錄。
- 自動登錄時需要驗(yàn)證碼,百度了一個比較簡單的識別方法,成功率不高,但設(shè)置為自動重復(fù)登錄,可以掛著去喝茶,也不麻煩。使用的工具是tesseract,和pytesseract、pillow。
- 判斷是否登錄成功,若失敗則再次登錄,成功則構(gòu)造、發(fā)送請求包、獲取數(shù)據(jù)。
說明:
- 所需軟件如下:
- tesseract識別軟件:需要在系統(tǒng)中安裝,方法很簡單,就下載安裝。
- 使用selenium時,需下載webdriver,地址:https://docs.seleniumhq.org/download下載解壓至/usr/local/bin中(我使用的是Mac,windows和lunix好像是解壓至某路徑,并設(shè)置系統(tǒng)path。具體請百度)
- python所需包的安裝如下(我好像安裝完pytesseract就帶有pillow):
pip install selenium pip install pytesseract
- 構(gòu)造headers和data,我建議是通過瀏覽器的開發(fā)者工具將請求包拷貝為curl格式,這里面就包含了所有的信息。
- curl包含了請求包的所有信息,但是目前未找到方便的方法來提取并構(gòu)造為requests的headers和data格式,只好用手工構(gòu)造。
- 在構(gòu)造headers時,因?yàn)椴恢绾稳ビ?jì)算'Content-Length',所以去掉了這一項(xiàng),對數(shù)據(jù)抓取沒有影響,也就暫且不管。
- 識別驗(yàn)證碼那一段,我基本上照抄這篇博客的:https://blog.csdn.net/weixin_38374974/article/details/80152899
- 數(shù)據(jù)處理部分比較簡單,只是顯示抓到的數(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)證碼位置,自動定位不是很明白,就使用了手動定位,代表(左,上,右,下)
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() # 對比度增強(qiáng)
# 識別驗(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) # 等待瀏覽器打開頁面
vcode = getvalicode(driver)
while len(vcode) != 4:
driver.refresh()
driver.maximize_window()
time.sleep(5) # 等待瀏覽器打開頁面
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è)置用戶帳號密碼
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):
"""
構(gòu)建Headers 可通過開發(fā)者工具中的請求包獲?。ńㄗh使用將該請求包拷貝為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)建請求包中的data
:return:
"""
urldata = json.dumps({'44-F0-FF-78-F4-25': {'2120001': ['221200190010', '231200020010']}}) # {'局站編碼':{'采集器編碼':['信號編碼','']}}
return urldata
def getdata(headers, urldata):
"""
獲取數(shù)據(jù)
: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()
# 重復(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()
如果本文對您有幫助,請給我留個言。謝謝!