Python爬蟲實例:爬取“查IPIP”查詢結果,查詢IP地址歸屬地

該文章僅用於技術討論,若有侵權,聯繫作者刪除。

目標是輸入一個IP地址後,獲取查IP網(http://chaipip.com/ip.php)的查詢結果。正常使用我們發現——流程是輸入IP地址——進行滑動驗證碼驗證後查詢——跳轉一個新的窗口——獲取新窗口的查詢結果。

我們先來看一下最後的流程和結果。

selenium流程
selenium模擬流程
查詢結果
查詢結果

接下來,我們就一步一步討論如何解決。

一、輸入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的話只會更慢。這也是一個弊端,目前筆者還沒有很好的解決辦法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章