python3的爬蟲筆記9——Selenium的用法

繼續(xù)介紹下selenium的用法,一共以兩個例子來呈現(xiàn)。分別對應(yīng)的是QQ空間模擬登錄(如何傳送登錄信息和確認(rèn),iframe如何切換),爬取簡書7日熱門(頁面如何拉到底)。
瀏覽器用的是chrome。

(1)QQ空間模擬登錄(如何傳送登錄信息和確認(rèn),iframe如何切換)

網(wǎng)址: http://user.qzone.qq.com/


我們是如何一般登錄的:
第一步:點擊賬號密碼登錄


第二步:輸入賬號密碼


第三步:點擊登錄。
然后我們就進入登錄界面了。
因此,任務(wù)的關(guān)鍵就是能用selenium模擬瀏覽器的一些點擊操作和輸入數(shù)據(jù)。

①首先我們看下如何實現(xiàn)點擊

我們用開發(fā)者工具先獲取“賬號密碼登錄”按鍵對應(yīng)的元素


#調(diào)用查找id的方法定位“賬號密碼登錄”按鍵的位置
switcher_plogin = driver.find_element_by_id('switcher_plogin')
#調(diào)用click的方法實現(xiàn)鼠標(biāo)點擊
switcher_plogin.click()
②接下來看看如何傳登錄信息

我們用開發(fā)者工具先獲取帳號輸入框?qū)?yīng)的元素

#調(diào)用查找id的方法定位帳號輸入框的位置
username = driver.find_element_by_id('u')
#先把輸入框清空,防止一些預(yù)填充的情況
username.clear()
#填寫QQ號碼
username.send_keys('你的QQ號')

準(zhǔn)備就緒,直接上代碼走起。

from selenium import webdriver
import time

url = 'http://user.qzone.qq.com/'
driver = webdriver.Chrome()
driver.get(url)
time.sleep(5)

#點擊“帳號密碼登錄”按鈕
driver.find_element_by_id('switcher_plogin').click()
#定位帳號輸入框
username = driver.find_element_by_id('u')
#清空帳號輸入框內(nèi)容
username.clear()
#填寫帳號
username.send_keys('你的qq號')
password = driver.find_element_by_id('p')
password.clear()
password.send_keys('你的密碼')
#點擊“登錄”按鈕
driver.find_element_by_id('login_button').click()

#driver.quit()

結(jié)果發(fā)現(xiàn)報錯


提示“no such element”錯,代碼中明明有id = "switcher_plogin"的,為什么會報錯呢?
有的頁面由幾個frame組成,如果要訪問的元素不在當(dāng)前的frame中,那么必須先切換到該元素所在的frame,才能進一步選定元素。

frame框架是網(wǎng)頁中常用的技術(shù),可以讓多個URL的內(nèi)容顯示在一個頁面中。常用標(biāo)簽FRAMESET,F(xiàn)RAME實現(xiàn)。FRAMESET是用以劃分框窗,每一框窗由一個FRAME標(biāo)記所標(biāo)示,F(xiàn)RAME必須在FRAMESET范圍中使用。iframe在frame的基礎(chǔ)上提供了更多好用的特性。

又看了下頁面的代碼,整個登錄方塊,果然是一個iframe。


它相當(dāng)于又插了個新的html


因此在我們的執(zhí)行點擊或填寫信息之前,還需要切換到這個frame上。

driver.switch_to.frame('login_frame')

因此,正確的代碼是:

from selenium import webdriver
import time

url = 'http://user.qzone.qq.com/'
driver = webdriver.Chrome()
driver.get(url)
time.sleep(5)

#切換到登錄的frame上
driver.switch_to.frame('login_frame')
#點擊“帳號密碼登錄”按鈕
driver.find_element_by_id('switcher_plogin').click()
#定位帳號輸入框
username = driver.find_element_by_id('u')
#清空帳號輸入框內(nèi)容
username.clear()
#填寫帳號
username.send_keys('你的qq號')
password = driver.find_element_by_id('p')
password.clear()
password.send_keys('你的密碼')
#點擊“登錄”按鈕
driver.find_element_by_id('login_button').click()

#driver.quit()

登錄成功:


因此(重要):我們以后分析源代碼時,首先要注意有沒有frame模塊。

此外,如果需要切回原本的frame,可使用driver.switch_to.parent_frame()

(2)爬取簡書7日熱門(頁面如何拉到底)

進入簡書7日熱門,我們發(fā)現(xiàn)一開始頁面并沒有把全部的信息都顯示出來,隨著我們鼠標(biāo)滾輪下拉,我們發(fā)現(xiàn)頁面會新增一些文章信息,同時在開發(fā)者工具中看到,相應(yīng)的元素有所增加,這就是異步加載。


并且在增加了2次文章信息后,頁面不能再往下拉了,頁面底部出現(xiàn)了閱讀更多的橫條:


相應(yīng)的開發(fā)者工具的element中出現(xiàn)與其相對應(yīng)的代碼:

點擊幾次閱讀更多后,會繼續(xù)加載幾次文章,最終完全加載完成。此時頁面不能拉動,“閱讀更多”的按鍵也消失了。
因此我們看到了問題的關(guān)鍵,就是頁面的下拉和點擊“閱讀更多”,直到頁面完全加載。之后我們就可以愉快的用正則或BeautifulSoup來提取信息了。
如何實現(xiàn)頁面下拉呢,官方文檔8.3中有提及(點我查閱
方法如下

#用execute_script方法調(diào)用JavaScript API在一個加載完成的頁面中去執(zhí)行js代碼
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")

無須知道它太多的含義,只需要知道執(zhí)行了這句話就相當(dāng)于把瀏覽器右邊的滾動條拉到底。
實際中一次下拉后,頁面會刷新,滾動條又會回到大概中間的位置,因此我們需要多次下拉,直到滿足我們的需求。頁面下拉的模板如下(為了能直觀的看到瀏覽器進行到哪個步驟,這邊用了很多的print):

from selenium import webdriver

def scroll_down(driver, times):
    for i in range(times):
        print("開始執(zhí)行第", str(i + 1), "次下拉操作")
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")  # 執(zhí)行JavaScript實現(xiàn)網(wǎng)頁下拉倒底部
        print("第", str(i + 1), "次下拉操作執(zhí)行完畢")
        print("第", str(i + 1), "次等待網(wǎng)頁加載......")
        time.sleep(5)  # 等待5秒(時間可以根據(jù)自己的網(wǎng)速而定),頁面加載出來再執(zhí)行下拉操作

有了第一個QQ空間登錄的例子,我們已經(jīng)知道如何點擊頁面了。

driver.find_element_by_css_selector('a.load-more').click()

點擊閱讀更多的模板如下:

from selenium import webdriver

def load_more_click(driver, times):
    for i in range(times):
        print("第", str(i + 1), "次點擊閱讀更多")
        driver.find_element_by_css_selector('a.load-more').click()# 執(zhí)行閱讀更多的點擊
        print("第", str(i + 1), "次等待網(wǎng)頁加載......")
        time.sleep(5)

在簡書7日熱門中,無論是頁面下拉還是點擊閱讀更多的次數(shù)都是有限的,我們并不能一開始就知道實際次數(shù)times要幾次,因此為了配合本次任務(wù)的實際情況,這兩個模板都應(yīng)該進行調(diào)整,調(diào)整為我們不需要輸入執(zhí)行次數(shù)times,它也能自己執(zhí)行正確的次數(shù)。

from selenium import webdriver

def scroll_down(driver):
    i = 1
    while True:
        print("開始執(zhí)行第", str(i), "次下拉操作")
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")  # 執(zhí)行JavaScript實現(xiàn)網(wǎng)頁下拉倒底部
        print("第", str(i), "次下拉操作執(zhí)行完畢")
        print("第", str(i), "次等待網(wǎng)頁加載......")
        i += 1
        time.sleep(4)  # 等待4秒(時間可以根據(jù)自己的網(wǎng)速而定),頁面加載出來再執(zhí)行下拉操作
        if '閱讀更多' in driver.page_source:
            break
from selenium import webdriver

def load_more_click(driver):
    i = 1
    while True:
        if '閱讀更多' not in driver.page_source:
            break
        print("第", str(i), "次點擊閱讀更多")
        driver.find_element_by_css_selector('a.load-more').click()
        print("第", str(i), "次等待網(wǎng)頁加載......")
        i += 1
        time.sleep(5)

主要是增加了停止的條件判斷,這里都是以是否出現(xiàn)“閱讀更多”這個按鈕為判斷條件。

看一下完整的代碼

from bs4 import BeautifulSoup as BS
from selenium import webdriver
import time,csv

#下拉操作函數(shù)
def scroll_down(driver):
    i = 1
    while True:
        print("開始執(zhí)行第", str(i), "次下拉操作")
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")  # 執(zhí)行JavaScript實現(xiàn)網(wǎng)頁下拉倒底部
        print("第", str(i), "次下拉操作執(zhí)行完畢")
        print("第", str(i), "次等待網(wǎng)頁加載......")
        i += 1
        time.sleep(4)  # 等待4秒(時間可以根據(jù)自己的網(wǎng)速而定),頁面加載出來再執(zhí)行下拉操作
        if '閱讀更多' in driver.page_source:
            break

#點擊閱讀更多的函數(shù)
def load_more_click(driver):
    i = 1
    while True:
        if '閱讀更多' not in driver.page_source:
            break
        print("第", str(i), "次點擊閱讀更多")
        driver.find_element_by_css_selector('a.load-more').click()
        print("第", str(i), "次等待網(wǎng)頁加載......")
        i += 1
        time.sleep(3)

#存儲到csv中的函數(shù)
def csv_write(tablelist):
    tableheader = ['標(biāo)題', '作者', '發(fā)表日期', '閱讀量']
    with open('weekly.csv', 'w', newline='') as f:
        writer = csv.writer(f)
        writer.writerow(tableheader)
        for row in tablelist:
            #由于編碼問題有些內(nèi)容無法存儲,暫時沒找到解決方法,用try的方法先跳過
            try:
                writer.writerow(row)
            except:
                print('編碼問題導(dǎo)致無法正常存儲', row)
                continue

#獲得文章相關(guān)信息的列表
def get_tablelist(html):
    soup = BS(html, 'lxml')
    namelist = soup.select('a.blue-link')
    titlelist = soup.select('a.title')
    timelist = soup.select('span.time')
    readlist = soup.select('div.meta > a:nth-of-type(1)')
    tablelist = []
    for a, b, c, d in zip(namelist, titlelist, timelist, readlist):
        name = a.get_text()
        title = b.get_text()
        time = c.get_text()
        read = d.get_text()
        #不知道為什么用了replace后,還是沒辦法去除\n,好在存到csv中顯示沒啥問題,有沒有大佬幫忙看看
        read.replace('\n', '')
        tablelist.append([title, name, time, read])
    #print(tablelist)
    return tablelist

#主函數(shù)
def mainfun():
    driver = webdriver.Chrome()
    url = 'http://www.lxweimin.com/trending/weekly'
    driver.get(url)
    time.sleep(2)
    scroll_down(driver=driver)
    load_more_click(driver=driver)
    html = driver.page_source
    tablelist = get_tablelist(html)
    csv_write(tablelist)
    driver.quit()

if __name__ == '__main__':
    mainfun()
 

輸出框如下,不知道為啥含有\(zhòng)n的那個字符串去不掉\n。。輸出確實有一些奇怪的符號,難怪存儲時會報錯。



csv文件如下:



看了下,確實是全部都保存了。

參考:
(1) http://www.lxweimin.com/p/6ba5b0786a64
(2) http://zmister.com/archives/98.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容