爬蟲實戰(zhàn)(二):Selenium 模擬登錄并爬取信息

原文地址:https://blogof33.com/post/12/

前言

前一篇文章 爬蟲實戰(zhàn)(一):爬取微博用戶信息 中爬取的是 https://weibo.cn 這個網(wǎng)頁,但是由于該網(wǎng)頁缺少維護(hù),微博官方可能加了一些限制,導(dǎo)致稍微頻繁一點(diǎn)的訪問都會報 403 錯誤,加上每次手動獲取 cookies 也比較麻煩,不友好,所以針對這些情況,我使用了一種新的抓取方式,也是一種更為高級的爬蟲手段。

我之前在文章里面提到“ 爬取微博主頁 https://weibo.com/ 或者 https://m.weibo.cn/ 較為困難 ”,為什么會這么說呢?因為這兩種頁面較新,所以采用的技術(shù)比較新穎,反爬措施做得要好一些。特別是它們采用了滾動式頁面,每次向下滾動到底后會加載出新的內(nèi)容,這種動態(tài)加載模式使得傳統(tǒng)的改變網(wǎng)頁地址中的頁碼獲得相應(yīng)內(nèi)容的方法失效了,含有用戶信息內(nèi)容的源碼需要抓包獲取,或者直接操作瀏覽器獲取。后者一般都是Selenium+PhantomJS來實現(xiàn)。

由于 Phantom.js 的維護(hù)者 Slobodin 在Google論壇上發(fā)帖表示,鑒于Chrome 59推出了無頭瀏覽特性,他認(rèn)為“Chrome比PhantomJS更快,更穩(wěn)定”,沒有理由再繼續(xù)維護(hù)Phantom.js(開發(fā)者很有自知之明:P,不過 Phantom.js 確實是一個很好用的東西),所以本文采用 Selenium+Chrome/Firefox 無頭瀏覽器的方式進(jìn)行模擬登錄和抓取用戶動態(tài)信息的操作。

Selenium

Selenium 是一個瀏覽器自動化測試框架,起初是為了自動化測試開發(fā)的,在爬蟲流行起來以后,也成為了一種爬蟲的工具。它的功能簡單來說就是可以控制瀏覽器,用代碼模擬人對瀏覽器的操作,實現(xiàn)自動化。

安裝

和大多數(shù) python 包一樣,selenium 可以使用 pip 進(jìn)行安裝:

# python 2
pip install selenium

# python 3
pip3 install selenium

因為 selenium 是對瀏覽器進(jìn)行控制,所以首先要裝對應(yīng)的驅(qū)動(driver),Selenium 針對幾個主流的瀏覽器都有相應(yīng)的官方 driver。讀者可以根據(jù)自己的情況下載并安裝。比如筆者是使用的 Linux 系統(tǒng)上的 Chrome 瀏覽器最新版本,那么便下載相應(yīng)版本的 driver ,下載完成以后,執(zhí)行命令:

#/usr/bin 或者 /usr/local/bin
sudo cp 下載的driver位置 /usr/bin
sudo chmod +x /usr/bin/chromedriver

安裝完成以后測試一下是否成功。

測試

首先來測試一下是否安裝成功:

from selenium import webdriver
 
browser = webdriver.Chrome()
browser.get('http://www.baidu.com/')

運(yùn)行這段代碼,會自動打開瀏覽器訪問百度。

如果程序執(zhí)行錯誤,瀏覽器沒有打開,那么可能是沒有裝 Chrome 瀏覽器或者 Chrome 驅(qū)動沒有配置在環(huán)境變量里或者驅(qū)動和瀏覽器版本不匹配。

模擬登錄

登錄微博需要使用驗證碼,自動識別驗證碼這一塊我研究了一下,使用圖像識別,也不難,但是因為我們可以將cookies 持久化保存下來,使用手動輸入驗證碼并不麻煩,所以自動識別驗證碼可以暫時先放一放,后面慢慢來研究。

使用 selenium 控制瀏覽器,通過對頁面的元素進(jìn)行定位來模擬人的操作,API 詳細(xì)介紹請見 參考文檔 。模擬登錄代碼如下:

def get():
    conf, engine = Connect('conf.yaml')  # 獲取配置文件的內(nèi)容
    loginname = conf.get('loginname')
    password = conf.get('password')

    loginname = list(loginname.values())
    password = list(password.values())
    with open('cookies.pkl', 'wb') as f:
        for i in range(len(password)):  # 將每個賬號的cookies保存下來.
            try:
                driver = webdriver.Chrome()
                driver.set_window_size(1124, 850)  # 防止得到的WebElement的狀態(tài)is_displayed為False,即不可見
                driver.get("http://www.weibo.com/login.php")
                time.sleep(5)
                #自動點(diǎn)擊并輸入用戶名
                driver.find_element_by_xpath('//*[@id="loginname"]').clear()
                driver.find_element_by_xpath('//*[@id="loginname"]').send_keys(loginname[i])
                driver.find_element_by_xpath('//*[@id="pl_login_form"]/div/div[3]/div[2]/div/input').clear()

                time.sleep(2)
                #自動點(diǎn)擊并輸入登錄的密碼
                driver.find_element_by_xpath('//*[@id="pl_login_form"]/div/div[3]/div[2]/div/input').send_keys(
                    password[i])
                driver.find_element_by_xpath('//*[@id="pl_login_form"]/div/div[3]/div[6]/a').click()
                
                #輸入驗證碼
                driver.find_element_by_xpath('//*[@id="pl_login_form"]/div/div[3]/div[3]/div/input').send_keys(
                    input("輸入驗證碼: "))

                time.sleep(1)
                driver.find_element_by_xpath('//*[@id="pl_login_form"]/div/div[3]/div[6]/a').click()
            except Exception as e:
                print("驗證碼輸入錯誤,請重新輸入!")
                driver.find_element_by_xpath('//*[@id="pl_login_form"]/div/div[3]/div[3]/div/input').send_keys(
                    input("輸入驗證碼: "))
                time.sleep(1)
                driver.find_element_by_xpath('//*[@id="pl_login_form"]/div/div[3]/div[6]/a').click()
            cookies = driver.get_cookies()
            pickle.dump(cookies, f)#序列化cookies對象

代碼注釋應(yīng)該寫得比較清楚,其中有一個細(xì)節(jié)就是我們需要將獲取的 cookies 序列化。什么是序列化?

我們把變量從內(nèi)存中變成可存儲或傳輸?shù)倪^程稱之為序列化,即把數(shù)據(jù)寫入臨時或持久性存儲區(qū),而把變量內(nèi)容從序列化的對象重新讀到內(nèi)存里稱之為反序列化。

意思是在這里將 cookies 以二進(jìn)制形式保存下來,這樣可以方便后續(xù)爬蟲使用。

使用 selenium 爬取用戶信息

爬取用戶信息的大致思路和上一篇文章 爬蟲實戰(zhàn)(一):爬取微博用戶信息 差不多 ,但仍然有以下區(qū)別:

  • 爬取 https://m.weibo.cn/ 而不是 https://weibo.cn/
  • 使用 seenium 代替 requests 獲取源碼
  • 使用 selenium 加載滾動頁面直到所有動態(tài)信息加載完成
  • 先使用正常的Chrome調(diào)試,調(diào)試完成以后再改成無頭瀏覽器

首先我們來看微博 html5 移動端的頁面長什么樣:

微博HTML5界面

為什么選這個網(wǎng)址而不是PC端的頁面呢?因為PC端的頁面每向下滑動三次需要跳頁,操作要繁瑣一些,而且 selenium 容易因為失去焦點(diǎn)導(dǎo)致跳轉(zhuǎn)失敗,我也沒找到很好的解決方法,而 html5 移動端的頁面多次滑動到底便可以獲得所有動態(tài)信息,不需要跳頁,所以要簡單很多。

再來看看使用 selenium 如何操作瀏覽器滑動到底,下面是相關(guān)的處理函數(shù),這個函數(shù)將 web 頁面滑動多次直到無法再滑動(即滑動到底了)并使用正則表達(dá)式提取出動態(tài)和動態(tài)發(fā)布時間:

#獲取用戶所有動態(tài)信息和動態(tài)發(fā)布時間并返回
def execute_times(driver):
    dynamic = []
    T = []
    d = re.compile(r'og"><div class="weibo-text">(.*?)<', re.S)  # 匹配動態(tài)
    t = re.compile(r'<span class="time">(.*?)<', re.S)  # 匹配動態(tài)發(fā)布時間
    
    #返回滾動高度
    last_height = driver.execute_script("return document.body.scrollHeight")

    while True:
        # 滑動一次
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")

        # 等待加載
        time.sleep(random.random())

        # 計算新的滾動高度并與上一個滾動高度進(jìn)行比較
        new_height = driver.execute_script("return document.body.scrollHeight")
        if new_height == last_height:
            break
        last_height = new_height

    html = driver.page_source

    dynamic += re.findall(d, html)
    T += re.findall(t, html)
    return dynamic, T #返回用戶所有動態(tài)信息和動態(tài)發(fā)布時間列表

得到用戶所有動態(tài)信息和動態(tài)發(fā)布時間列表以后,其他處理和前一篇文章類似,在此不再累述,詳情請見源碼 weibo_spider.py

因為每次運(yùn)行程序都需要彈出瀏覽器窗口,而且速度較慢,所以可以將瀏覽器設(shè)置成無頭模式:

#Chrome
opt = webdriver.ChromeOptions()  # 創(chuàng)建chrome參數(shù)對象
opt.set_headless()  # 把chrome設(shè)置成無頭模式,不論windows還是linux都可以,自動適配對應(yīng)參數(shù)
driver = webdriver.Chrome(options=opt)#不制定options選項則是普通有頭瀏覽器

#Firefox
opt = webdriver.FirefoxOptions()  
opt.set_headless() 
driver = webdriver.Firefox(options=opt)

至此模擬登錄并爬取信息方法介紹完畢。

源碼地址:https://github.com/starFalll/Spider# 爬蟲實戰(zhàn)(二):Selenium 模擬登錄并爬取信息

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容