繼續(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