我最近使用Python爬取網(wǎng)頁(yè)內(nèi)容時(shí)遇到Flex渲染的動(dòng)態(tài)頁(yè)面,比如下圖的課程目錄標(biāo)題,此時(shí)按鼠標(biāo)右鍵,菜單里沒(méi)有復(fù)制鏈接的選項(xiàng)。
我的目的是:獲取各個(gè)視頻標(biāo)題、鏈接。
按F12進(jìn)入開發(fā)者模式分析網(wǎng)頁(yè),可見有多個(gè)flex標(biāo)簽,像這種通過(guò)flex動(dòng)態(tài)渲染的網(wǎng)頁(yè),視頻鏈接隱藏在JS代碼里,需要人工點(diǎn)擊才能運(yùn)算出正確的鏈接,普通的requests庫(kù)的get是無(wú)法直接獲取的。
于是改變思路,嘗試selenium的webdriver來(lái)打開瀏覽器,打開該網(wǎng)頁(yè),然后用find_element的By來(lái)搜索關(guān)鍵詞“視頻”,看看能不能定位到“視頻”的元素:
from selenium import webdriver
from selenium.webdriver.common.by import By
options = webdriver.ChromeOptions()
# 關(guān)掉密碼彈窗
options.add_experimental_option("prefs", prefs)
# 關(guān)閉提示“您的連接不是私密連接”
options.add_argument("--ignore-certificate-errors")
# 關(guān)閉提示“Chrome受自動(dòng)控制提示”
options.add_experimental_option('useAutomationExtension', False)
# 關(guān)閉提示“Chrome受自動(dòng)控制提示”
options.add_experimental_option('excludeSwitches', ['enable-automation'])
options.add_argument("--disable-extensions")
driver = webdriver.Chrome(options=options)
driver.get('......') # 打開網(wǎng)頁(yè)url
l1=driver.find_element(By.PARTIAL_LINK_TEXT,'視頻')
l2=driver.find_element(By.LINK_TEXT,'視頻')
結(jié)果無(wú)論是l1還是l2,都會(huì)報(bào)錯(cuò)。
再嘗試別的辦法,如selenium的locate with:
from selenium.webdriver.support.relative_locator import locate_with, with_tag_name
l3=locate_with(By.LINK_TEXT, '視頻')
l3.click()
l3=locate_with(...) 這一行通過(guò)了,但下一句l3.click()報(bào)錯(cuò),提示沒(méi)有click()的屬性。
再想辦法,改為:
l4=driver.find_element(l3)
l4.click()
但同樣報(bào)錯(cuò):selenium.common.exceptions.NoSuchElementException: Message: Cannot locate relative element with: {'link text': '視頻'}
上面都使用了By.LINK_TEXT查找關(guān)鍵詞來(lái)定位,是希望能精確定位,但在Flex渲染的頁(yè)面里定位不了。
那么find_element改用By.CLASS_NAME又行不行?
l5=driver.find_element(By.CLASS_NAME,'item-title')
print(l5.text)
好!這下沒(méi)有報(bào)錯(cuò)。結(jié)果返回l5的值是字符串:'一張圖了解技術(shù)指標(biāo)(上)'
接下來(lái)發(fā)送點(diǎn)擊命令,如果順利的話就通過(guò)driver.current_url獲取播放視頻頁(yè)面的鏈接。
使用while 1循環(huán),遍歷查找所有class為“item-title”,直至查找報(bào)錯(cuò),跳出循環(huán)。
links=[]
# 參考來(lái)源:https://blog.csdn.net/saber_sss/article/details/103460706
new_location=WebDriverWait(driver, 5).until(EC.presence_of_element_located((By.CLASS_NAME,'item-title')))
while 1:
try:
# 查詢窗口總數(shù),返回一個(gè)包含所有窗口句柄handles的列表
handles=driver.window_handles
title=new_location.text # 獲取視頻標(biāo)題
new_location.click()
# 對(duì)比一開始獲取的窗口總數(shù),確認(rèn)新窗口出現(xiàn)了再去切換
WebDriverWait(driver,5).until(EC.new_window_is_opened(handles))
# 切換到新窗口
handles=driver.window_handles #再次獲取窗口句柄handles
#執(zhí)行切換窗口操作
driver.switch_to.window(handles[-1])
links.append([title, driver.current_url])
# 關(guān)閉當(dāng)前窗口
driver.close()
# 記得還要再切換去原來(lái)的窗口
driver.switch_to.window(handles[0])
last_location=new_location
# 查找下一行
new_location=driver.find_element(locate_with(By.CLASS_NAME,'item-title').below(last_location))
except Exception:
break
for i in links: print(i)
運(yùn)行結(jié)果:
貌似成功了。但是對(duì)比原網(wǎng)頁(yè)上的視頻標(biāo)題,獲取的結(jié)果少了一半,find_element代碼每次都隔行獲取,為什么會(huì)隔行?離得太近了嗎?
后來(lái)我到selenium的官網(wǎng)文檔里查看關(guān)于locators的用法,發(fā)現(xiàn)除了below是查找下一行之外,還有“near”查找,于是把below改為near,結(jié)果卻是查找到視頻標(biāo)題的第一行、第二行、第一行、第二行。。。如此循環(huán)。
為解決這個(gè)問(wèn)題,只能先后處理查找奇數(shù)行、偶數(shù)行,最后合并奇、偶行結(jié)果并重新排序。
# 初始化webdriver的過(guò)程不寫了
links = []
l1 = WebDriverWait(driver, 5).until(EC.presence_of_element_located((By.CLASS_NAME, 'item-title')))
l2 = driver.find_element(locate_with(By.CLASS_NAME,'item-title').near(l1))
index = 0
for locator in (l1,l2):
while 1:
try:
# 查詢窗口總數(shù),返回一個(gè)包含所有窗口句柄handles的列表
handles = driver.window_handles
title = locator.text
locator.click()
# 對(duì)比一開始獲取的窗口總數(shù),確認(rèn)新窗口出現(xiàn)了再去切換
WebDriverWait(driver, 5).until(EC.new_window_is_opened(handles))
# 再次獲取窗口句柄handles
handles = driver.window_handles
# 新老句柄列表可以看出,新出現(xiàn)的句柄在列表里面排在后面
# 執(zhí)行切換窗口操作
driver.switch_to.window(handles[-1])
print(index, title, driver.current_url)
links.append([index, title, driver.current_url])
driver.close()
# 記得還要切換回原來(lái)的窗口
driver.switch_to.window(handles[0])
locator = driver.find_element(locate_with(By.CLASS_NAME,'item-title').below(locator))
# 為解決隔行,index加2
index+=2
except Exception:
# 查找不到再多的元素就退出循環(huán)
break
# 然后處理偶數(shù)行,index設(shè)為1
index = 1
# 把結(jié)果重新排序
links = sorted(links)
for i in links: print(i)
driver.quit()
運(yùn)行結(jié)果截圖:
很不錯(cuò)!但還有一個(gè)bug:運(yùn)行結(jié)果的第2、3項(xiàng)重復(fù),估計(jì)是selenium的定位元素存在誤差造成的。
2023年4月13日更新:簡(jiǎn)單而優(yōu)雅的寫法
今天更新一下簡(jiǎn)單一點(diǎn)又沒(méi)有上述 bug 的方法。先來(lái)看第一幅圖:
之前的笨方法是先定位 class="item-title",由于有很多個(gè)相同的 class 元素,導(dǎo)致Selenium定位不準(zhǔn)確。
簡(jiǎn)單而優(yōu)雅的方法是:先定位 class="item-title" 的上一級(jí) class="column_catalog_item_wrap",然后從這個(gè)元素開始往下循環(huán)遍歷定位?class="item-title",再進(jìn)行點(diǎn)擊鏈接、獲取鏈接。
下面是優(yōu)化后的代碼:
# 初始化webdriver的過(guò)程不寫了
links = []
l1 = WebDriverWait(driver, 5).until(EC.presence_of_element_located((By.CLASS_NAME, 'column_catalog_item_wrap')))
l2 = l1.find_elements(By.CLASS_NAME, 'item-title')
for index, locator in enumerate(l2):
# 查詢窗口總數(shù),返回一個(gè)包含所有窗口句柄handles的列表
handles=driver.window_handles
title=locator.text
locator.click()
# 對(duì)比一開始獲取的窗口總數(shù),確認(rèn)新窗口出現(xiàn)了再去切換
WebDriverWait(driver,5).until(EC.new_window_is_opened(handles))
# 再次獲取窗口句柄handles
handles=driver.window_handles
# 新老句柄列表可以看出,新出現(xiàn)的句柄在列表里面排在后面
# 執(zhí)行切換窗口操作
driver.switch_to.window(handles[-1])
print(index, title, driver.current_url)
links.append([index, title, driver.current_url])
driver.close()
# 記得還要切換回原來(lái)的窗口
driver.switch_to.window(handles[0])
for i in links: print(i)
driver.quit()
參考來(lái)源:
【1】?爬蟲實(shí)例(5)網(wǎng)頁(yè)動(dòng)態(tài)內(nèi)容的識(shí)別_網(wǎng)頁(yè)內(nèi)容分類識(shí)別_演技拉滿的白馬的博客-CSDN博客
【2】淺談selenium4新增功能之相對(duì)定位_the-ruffian的博客-CSDN博客_selenium相對(duì)定位法
【3】?python+selenium之窗口切換三種操作_saber_sss的博客-CSDN博客_python wd 切換窗口文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-441847.html
【4】?Locator strategies | Selenium文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-441847.html
到了這里,關(guān)于Python爬取網(wǎng)頁(yè)Flex渲染的動(dòng)態(tài)內(nèi)容的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!