該文章僅用於技術討論,若有侵權,聯繫作者刪除。
目標是輸入一個IP地址後,獲取查IP網(http://chaipip.com/ip.php)的查詢結果。正常使用我們發現——流程是輸入IP地址——進行滑動驗證碼驗證後查詢——跳轉一個新的窗口——獲取新窗口的查詢結果。
我們先來看一下最後的流程和結果。
接下來,我們就一步一步討論如何解決。
一、輸入IP地址
第一步時清除輸入框中的IP地址,然後輸入要查詢的地址。並點擊驗證碼按鈕,出現滑動式驗證碼以便進行下一步操作。
#輸入數據
def input_message():
browser.get(url)
ip_put = wait.until(EC.presence_of_element_located((By.ID, 'ip')))
ip_put.clear()
ip_put.send_keys(ip)
click_btn=wait.until(EC.presence_of_element_located((By.ID,'embed-captcha')))
#隨機延時點擊
time.sleep(random.random()*3)
click_btn.click()
二、進行滑動驗證碼驗證
此處的思路是將滑動驗證碼的完整圖片、有缺口的圖片和缺口圖片截取出來,通過比對後發現需要移動的距離後,用selenium模擬用戶拖動。
#設置元素的可見性用於截圖
def show_element(element):
browser.execute_script("arguments[0].style = arguments[1]", element, "display: block;")
def hide_element(element):
browser.execute_script("arguments[0].style = arguments[1]", element, "display: none;")
#截圖
def save_pic(obj, name):
try:
pic_url = browser.save_screenshot('.\\ipaddress.png')
#開始獲取元素位置信息
left = obj.location['x']
top = obj.location['y']
right = left + obj.size['width']
bottom = top + obj.size['height']
im = Image.open('.\\ipaddress.png')
im = im.crop((left, top, right, bottom))
file_name = 'ipaddress_' + name + '.png'
im.save(file_name)
except BaseException as msg:
print("截圖失敗:%s" % msg)
def cut():
c_background = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_bg.geetest_absolute')))
c_slice = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'canvas.geetest_canvas_slice.geetest_absolute')))
c_full_bg = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'canvas.geetest_canvas_fullbg.geetest_fade.geetest_absolute')))
hide_element(c_slice)
save_pic(c_background, 'back')
show_element(c_slice)
save_pic(c_slice, 'slice')
show_element(c_full_bg)
save_pic(c_full_bg, 'full')
#判斷元素是否相同
def is_pixel_equal(bg_image, fullbg_image, x, y):
#bg_image是缺口的圖片
#fullbg_image是完整圖片
bg_pixel = bg_image.load()[x, y]
fullbg_pixel = fullbg_image.load()[x, y]
threshold = 60
if (abs(bg_pixel[0] - fullbg_pixel[0] < threshold) and abs(bg_pixel[1] - fullbg_pixel[1] < threshold) and abs(bg_pixel[2] - fullbg_pixel[2] < threshold)):
return True
else:
return False
#計算滑塊移動的距離
def get_distance(bg_image, fullbg_image):
distance = 57
for i in range(distance, fullbg_image.size[0]):
for j in range(fullbg_image.size[1]):
if not is_pixel_equal(fullbg_image, bg_image, i, j):
return i
#構造滑動軌跡
def get_trace(distance):
#distance是缺口離滑塊的距離
trace = []
faster_distance = distance*(4/5)
start, v0, t = 0, 0, 0.3
while start < distance:
if start < faster_distance:
a = 1.5
else:
a = -3
move = v0 * t + 1 / 2 * a * t * t
v = v0 + a * t
v0 = v
start += move
trace.append(round(move))
return trace
#模擬拖動
def move_to_gap(trace):
slider=wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'div.geetest_slider_button')))
# 使用click_and_hold()方法懸停在滑塊上,perform()方法用於執行
ActionChains(browser).click_and_hold(slider).perform()
for x in trace:
# 使用move_by_offset()方法拖動滑塊,perform()方法用於執行
ActionChains(browser).move_by_offset(xoffset=x, yoffset=0).perform()
time.sleep(0.5)
ActionChains(browser).release().perform()
def slide():
distance=get_distance(Image.open('.\\ipaddress_back.png'),Image.open('.\\ipaddress_full.png'))
trace = get_trace(distance-5)
move_to_gap(trace)
time.sleep(1)
三、查詢
此處就直接獲取查詢按鈕的id,然後用click方法進行模擬點擊操作。
#登錄
def login():
login_btn=wait.until(EC.presence_of_element_located((By.ID,'embed-submit')))
#passwd.send_keys(Keys.ENTER)
login_btn.click()
四、跳轉至新的窗口,並獲取查詢結果
首先要做的是窗口跳轉了,要將selenium操作的頁面定位到新窗口。此處用到的是句柄,每個窗口都要自己的句柄,通過獲取所有句柄後,判斷並轉入新窗口進行操作。其次就是獲取IP地址的查詢結果,由於詳細信息需要點擊查看周圍的按鈕,所以我們先模擬點擊後,再獲取相應的值。
#獲取登錄後的信息
def get_id_messgae():
n = browser.window_handles # 獲取當前頁句柄
browser.switch_to.window (n[1]) # 切換到新的網頁窗口
details_btn=wait.until(EC.presence_of_element_located((By.ID,'btn1')))
details_btn.click()
time.sleep(1)
message = browser.find_element_by_id("Fu3lC").text
message = message.split('\n')
message_latitude = message[4]
message_longitude = message[5]
message_content = message[6]
message_details = browser.find_element_by_id("location_description").text
#print(message)
print(message_latitude)
print(message_longitude)
print(message_content)
print(message_details)
到此處,整個功能就已經完成了。但是由於該網址對於同一個用戶ID每天只能查20個IP地址,所以想通過這個辦法進行大規模數據查詢是不可能的。由於使用selenium網頁的加載速度本來就慢,再使用代理IP的話只會更慢。這也是一個弊端,目前筆者還沒有很好的解決辦法。