scrapy使用十:動態網頁技術之selenium、splinter


Selenium瀏覽器自動化測試框架

簡介
  •   Selenium 是一個用於Web應用程序測試的工具。Selenium測試直接運行在瀏覽器中,就像真正的用戶在操作一樣。
  •   支持的瀏覽器包括IE(7, 8, 9, 10, 11),Mozilla Firefox,Safari,Google Chrome,Opera等。
  •   這個工具的主要功能包括:測試與瀏覽器的兼容性——測試你的應用程序看是否能夠很好得工作在不同瀏覽器和操作系統之上。
  •   測試系統功能——創建回歸測試檢驗軟件功能和用戶需求。支持自動錄制動作和自動生成 .Net、Java、Perl等不同語言的測試腳本
 
功能
  •   框架底層使用JavaScript模擬真實用戶對瀏覽器進行操作。測試腳本執行時,瀏覽器自動按照腳本代碼做出點擊,輸入,打開,驗證等操作,就像真實用戶所做的一樣,從終端用戶的角度測試應用程序。
  •   使瀏覽器兼容性測試自動化成為可能,盡管在不同的瀏覽器上依然有細微的差別。
  •   使用簡單,可使用Java,Python等多種語言編寫用例腳本

安裝

1 pip install selenium

 

官方文檔
1 http://selenium-python.readthedocs.io/

 

驅動下載
Chrome  https://sites.google.com/a/chromium.org/chromedriver/downloads

Edge    https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/

Firefox https://github.com/mozilla/geckodriver/releases
phantomjs  http://phantomjs.org/download.html
splash  https://github.com/scrapy-plugins/scrapy-splash
grid https://www.oschina.net/question/tag/selenium-grid

 基本使用之模擬登陸知乎

from selenium import webdriver
from scrapy.selector import Selector
#通過executable_path參數指定Chrome驅動文件所在位置
browser = webdriver.Chrome(
    executable_path="E:\Python Project\scrapyproject\_ArticleSpider\chromedriver_win32\chromedriver.exe"
)
# 打開知乎的登錄頁
browser.get("https://www.zhihu.com/#signin")
# 獲取輸入框account
webElement = browser.find_element_by_css_selector(".view-signin input[name='account']")
# 清除account輸入框內的內容
webElement.clear()
# 在account輸入框內,輸入18412542552
webElement.send_keys("18412542552")
# 獲取密碼輸入框,並輸入密碼
browser.find_element_by_css_selector(".view-signin input[name='password']").send_keys("as15fQAfa")
#點擊提交按鈕
browser.find_element_by_css_selector(".view-signin button.sign-button").click()

# browser.quit()

在python.org搜索:如果拋出webdriver錯誤,可以通過Options設置selenium瀏覽器屬性來調整。

chrome設計的時候是不允許root用戶直接使用的。為了程序的正常運行,需要添加額外的參數 '--no-sandbox'

options.add_argument('--headless'),不啟動圖形chrome瀏覽器

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.options import Options

options = Options()
options.add_argument('--headless')
options.add_argument('--no-sandbox')
# options.add_argument('--disable-dev-shm-usage')
driver = webdriver.Chrome(executable_path="/home/json/Downloads/chromedriver", chrome_options=options)
driver.get("http://www.python.org")
assert "Python" in driver.title
elem = driver.find_element_by_name("q")
elem.clear()
elem.send_keys("pycon")
elem.send_keys(Keys.RETURN)
assert "No results found." not in driver.page_source
driver.close()

 

 

基本使用之模擬登陸微博並下拉滾動條

from selenium import webdriver
import time

browser = webdriver.Chrome(
    executable_path="E:\Python Project\scrapyproject\_ArticleSpider\chromedriver_win32\chromedriver.exe"
)

browser.get("https://weibo.com/")

time.sleep(5)
browser.find_element_by_css_selector("#loginname").send_keys("<username>")
browser.find_element_by_css_selector(".info_list.password input[node-type='password']").send_keys("<password>")
browser.find_element_by_css_selector(".info_list.login_btn a[node-type='submitBtn']").click()

for i in range(3):
    browser.execute_script("window.scrollTo(0, document.body.scrollHeight); var lenOfPage=document.body.scrollHeight; return lenOfPage;")
    time.sleep(3)

# browser.quit()

基本使用之不加載圖片提升頁面加載速度

from selenium import webdriver

chrome_opt = webdriver.ChromeOptions()
prefs = {"profile.managed_default_content_settings.images":2}
chrome_opt.add_experimental_option("prefs", prefs)

browser = webdriver.Chrome(
    executable_path="E:\Python Project\scrapyproject\_ArticleSpider\chromedriver_win32\chromedriver.exe",
    chrome_options=chrome_opt
)

browser.get("https://www.taobao.com/")

# browser.quit()

基本使用之隱藏chrom圖形界面

注意: 目前只能在linux中使用
 
下載相關模塊
1 pip install pyvirtualdisplay
相關依賴下載
sudo apt-get install xvfb

pip install xvfbwrapper
使用步驟
復制代碼
from pyvirtualdisplay import Display
display = Display(visible=0, size=(800, 600))
display.start()

browser = webdriver.Chrome(
    executable_path="E:\Python Project\scrapyproject\_ArticleSpider\chromedriver_win32\chromedriver.exe"
)

browser.get(https://www.taobao.com/)

# browser.quit()
復制代碼

 

基本使用之phantomjs
特點
  •   無界面的瀏覽器,效率高
  •   在linux無圖形化界面時使用較多
  •   多進程下phantomjs性能會嚴重下降
  •   多線程執行時不穩定
 
下載
1 http://phantomjs.org/download.html
簡單使用
復制代碼
from selenium import webdriver

browser = webdriver.PhantomJS(
    executable_path="E:\Python Project\scrapyproject\_ArticleSpider\phantomjs-2.1.1-windows\bin\phantomjs.exe"
)

browser.get("https://item.taobao.com/item.htm?id=558638145403&ali_refid=a3_430673_1006:1109358544:N:%E6%89%8B%E6%9C%BA%E8%8B%B9%E6%9E%9C%E6%89%8B%E6%9C%BA:5d77c360cd1e64043b2f430be7531705&ali_trackid=1_5d77c360cd1e64043b2f430be7531705&spm=a2e15.8261149.07626516002.2")
print(browser.page_source)

browser.quit()
復制代碼

 

其它瀏覽器自動化測試工具
更加輕量型的加載動態頁面的工具splash、grid
特點
  •   比chrom和phantomjs性能更優
  •   支持分布式爬蟲
  •   穩定性不如chrom高
splash-github項目
1 https://github.com/scrapy-plugins/scrapy-splash
selenium擴展grid
1 https://www.oschina.net/question/tag/selenium-grid

 

 

 

集成selenium到scrapy框架中

為每一個spider創建一個chrom瀏覽器對象
復制代碼
import scrapy
from scrapy.xlib.pydispatch import dispatcher
from scrapy import signals
from selenium import webdriver

class JobboleSpider(scrapy.Spider):
    name = "jobbole"
    allowed_domains = ["blog.jobbole.com"]
    start_urls = ['http://blog.jobbole.com/all-posts/']

    def __init__(self):
        self.browser = webdriver.Chrome(
            executable_path="E:\Python Project\scrapyproject\_ArticleSpider\chromedriver_win32\chromedriver.exe"
        )
        super(JobboleSpider, self).__init__()
        dispatcher.connect(self.spider_closed, signals.spider_closed)

    def spider_closed(self, spider):
        self.browser.quit()

    def parse(self, response):
        pass
復制代碼
編寫middleware在下載時使用chrom打開網頁
復制代碼
import time
from scrapy.http import HtmlResponse
class JSPageMiddleware(object):

    def process_request(self, request, spider):
        if spider.name == "jobbole":
            spider.browser.get(request.url)
            time.sleep(3)
            return HtmlResponse(url=spider.browser.current_url, body=spider.browser.page_source, encoding="utf-8", request=request)
復制代碼
配置settings
DOWNLOADER_MIDDLEWARES = {
    'ArticleSpider.middlewares.JSPageMiddleware': 1,
}

 

重寫downloader實現selenium支持異步請求
需要我們熟悉並遵守scrapy編程規范,可以參考
1 https://github.com/flisky/scrapy-phantomjs-downloader

 

 

 

 


其它瀏覽器自動化測試工具splinter (純python開發):是對selenium的進一步抽象
1 https://github.com/cobrateam/splinter

 splinter官方文檔:https://splinter-docs-zh-cn.readthedocs.io/zh/latest/index.html

 

補充:

selenium提供了八種元素定位:id、name、class、tag、link、patail_link、xpath、css

定義元素:先是通過簡單的元素屬性定位-->有些元素的屬性沒有,或者很多重復,此時可以用xpath-->xpath定位基本上可以解決80%的元素定位問題。xpath可以看成定位界的寶刀屠龍,雖然威力大,但是比較笨重,定位元素慢,語法很長,還不穩定-->css定位速度快,穩准狠,定位界的倚天劍,輕巧,語法簡潔。但也不是萬能的,有些模糊匹配的地方,還是得靠xpath大哥來搞定-->有些場景是selenium無法完成的,比如瀏覽器的滾動條,此時需要用到js

獲取頁面元素的方法,示例:

<input type="text" name="passwd" id="passwd-id" />
<div class="cheese"><span>Cheddar</span></div><divclass="cheese"><span>Gouda</span></div>
<ahref="http://www.google.com/search?q=cheese">cheese</a>>

通過find_element_by_*()系列的方法獲取到這個標簽中第一個標簽

通過find_elements_by_*()系列的方法獲取到這個標簽中所有的標簽

或者通過find_element(By.*(value))系列的方法獲取這個標簽中的第一個標簽,或者所有find_elements(By.*(value))

如果頁面上沒有要查找的元素,selenium就會拋出NoSuchElement異常

from selenium import webdriver
browser = webdriver.Chrome(executable_path="E:\Python Project\scrapyproject\_ArticleSpider\chromedriver_win32\chromedriver.exe")
# 匹配id屬性值的(第一個)元素
element = browser.find_element_by_id("password-id")
# 匹配id屬性值的所有元素
element = browser.find_elements_by_id("password-id")
# 匹配name屬性值的(第一個)元素
element = browser.find_element_by_name("passwd")
# 匹配xpath的元素
element = browser.find_element_by_xpath("//input[@id='passwd-id']")
# 匹配CSS selector的元素
driver.find_element_by_css_selector(selector)
# 匹配class屬性的元素
cheeses = driver.find_elements_by_class_name("cheese")
# 完全匹配提供的text的<a>元素
driver.find_element_by_link_text(text)
# 包含提供的text的<a>元素
driver.find_element_by_partial_link_text(text)
# 匹配標簽name的元素(不區分大小寫<a>匹配'a'和'A')
driver.find_element_by_tag_name(name)
driver.find_elements_by_tag_name(name)

 

 獲取到的元素,都是element對象

WebElement的屬性和方法:

屬性或方法 描述
tag_name 標簽名,例如'a'代表<a>元素
get_attribute(name) 該元素name屬性的值
text 該元素內的文本,例如<span>hello</span>中的hello
clear() 清除其中輸入的文本
is_displayed() 如果該元素可見返回True
is_enabled() 對於輸入元素,如果該元素啟用,返回True
is_selected() 對於復選框或單選框元素,如果被選中返回True
location 一個字典,包含鍵'x'和'y',表示該元素在頁面上的位置

 

 

 

 

 

 

 

 

 

 

控制瀏覽器操作的一些方法
方法    說明
set_window_size()    設置瀏覽器的大小
back()    控制瀏覽器后退
forward()    控制瀏覽器前進
refresh()    刷新當前頁面
clear()    清除文本
send_keys (value)    模擬按鍵輸入
click()    單擊元素
submit()    用於提交表單
get_attribute(name)    獲取元素屬性值
is_displayed()    設置該元素是否用戶可見
size    返回元素的尺寸
text    獲取元素的文本

在 WebDriver 中, 將這些關於鼠標操作的方法封裝在 ActionChains 類提供。

方法    說明
ActionChains(driver)    構造ActionChains對象
context_click()    執行鼠標懸停操作
move_to_element(above)    右擊
double_click()    雙擊
drag_and_drop()    拖動
move_to_element(above)    執行鼠標懸停操作
context_click()    用於模擬鼠標右鍵操作, 在調用時需要指定元素定位
perform()    執行所有 ActionChains 中存儲的行為,可以理解成是對整個操作的提交動作
Selenium中的Key模塊為我們提供了模擬鍵盤按鍵的方法,那就是send_keys()方法。它不僅可以模擬鍵盤輸入,也可以模擬鍵盤的操作。

常用的鍵盤操作如下:

模擬鍵盤按鍵    說明
send_keys(Keys.BACK_SPACE)    刪除鍵(BackSpace)
send_keys(Keys.SPACE)    空格鍵(Space)
send_keys(Keys.TAB)    制表鍵(Tab)
send_keys(Keys.ESCAPE)    回退鍵(Esc)
send_keys(Keys.ENTER)    回車鍵(Enter)
組合鍵的使用

模擬鍵盤按鍵    說明
send_keys(Keys.CONTROL,‘a’)    全選(Ctrl+A)
send_keys(Keys.CONTROL,‘c’)    復制(Ctrl+C)
send_keys(Keys.CONTROL,‘x’)    剪切(Ctrl+X)
send_keys(Keys.CONTROL,‘v’)    粘貼(Ctrl+V)
send_keys(Keys.F1…Fn)    鍵盤 F1…Fn

不管是在做功能測試還是自動化測試,最后一步需要拿實際結果與預期進行比較。這個比較的稱之為斷言。通過我們獲取title 、URL和text等信息進行斷言。

屬性    說明
title    用於獲得當前頁面的標題
current_url    用戶獲得當前頁面的URL
text    獲取搜索條目的文本信息

在頁面操作過程中有時候點擊某個鏈接會彈出新的窗口,這時就需要主機切換到新打開的窗口上進行操作。WebDriver提供了switch_to.window()方法,可以實現在不同的窗口之間切換。

方法    說明
current_window_handle    獲得當前窗口句柄
window_handles    返回所有窗口的句柄到當前會話
switch_to.window()    用於切換到相應的窗口,與上一節的switch_to.frame()類似,前者用於不同窗口的切換,后者用於不同表單之間的切換。
在WebDriver中處理JavaScript所生成的alert、confirm以及prompt十分簡單,具體做法是使用 switch_to.alert 方法定位到 alert/confirm/prompt,然后使用text/accept/dismiss/ send_keys等方法進行操作。

方法    說明
text    返回 alert/confirm/prompt 中的文字信息
accept()    接受現有警告框
dismiss()    解散現有警告框
send_keys(keysToSend)    發送文本至警告框。keysToSend:將文本發送至警告框。


導入選擇下拉框Select類,使用該類處理下拉框操作。
from selenium.webdriver.support.select import Select
方法    說明
select_by_value(“選擇值”)    相當於我們使用鼠標選擇下拉框的值

WebDriver操作cookie的方法:
方法    說明
get_cookies()    獲得所有cookie信息
get_cookie(name)    返回字典的key為“name”的cookie信息
add_cookie(cookie_dict)    添加cookie。“cookie_dict”指字典對象,必須有name 和value 值
delete_cookie(name,optionsString)    刪除cookie信息。“name”是要刪除的cookie的名稱,“optionsString”是該cookie的選項,目前支持的選項包括“路徑”,“域”
delete_all_cookies()    刪除所有cookie信息

通過set_window_size()方法將瀏覽器窗口設置為固定寬高顯示,目的是讓窗口出現水平和垂直滾動條。然后通過execute_script()方法執行JavaScripts代碼來移動滾動條的位置。

通過javascript設置瀏覽器窗口的滾動條位置

js="window.scrollTo(100,450);"

driver.execute_script(js)

 

窗口截圖:get_screenshot_as_file(self, filename)用於截取當前窗口,並把圖片保存到本地

 

selenium之 定位以及切換frame(iframe):

selenium定位頁面元素的時候會遇到定位不到的問題,明明元素就在那兒,用firebug也可以看到,就是定位不到,這種情況很有可能是frame在搞鬼。

frame標簽有frameset、frame、iframe三種,frameset跟其他普通標簽沒有區別,不會影響到正常的定位。而frame與iframe對selenium定位而言是一樣的,selenium有一組方法對frame進行操作。

示例:

<body>
<iframe src="a.html" id="frame1" name="myframe"></iframe>
</body>

想要定位其中的iframe並切進去,可以通過如下代碼:

from selenium import webdriver
driver = webdriver.Firefox()
driver.switch_to.frame(0)  # 1.用frame的index來定位,第一個是0
# driver.switch_to.frame("frame1")  # 2.用id來定位
# driver.switch_to.frame("myframe")  # 3.用name來定位
# driver.switch_to.frame(driver.find_element_by_tag_name("iframe"))  # 4.用WebElement對象來定位

通常采用id和name就能夠解決絕大多數問題。但有時候frame並無這兩項屬性,則可以用index和WebElement來定位:

  • index從0開始,傳入整型參數即判定為用index定位,傳入str參數則判定為用id/name定位
  • WebElement對象,即用find_element系列方法所取得的對象,我們可以用tag_name、xpath等來定位frame對象

示例:

<iframe src="myframetest.html" />

用xpath定位,傳入WebElement對象:

driver.switch_to.frame(driver.find_element_by_xpath("//iframe[contains(@src,'myframe')]"))

注意:在使用element.switch_to,切到frame中之后,我們便不能繼續操作主文檔的元素,這時如果想操作主文檔內容,則需切回主文檔。

driver.switch_to.default_content()

嵌套frame的操作:switch_to.parent_frame()

示例:

<html>
    <iframe id="frame1">
        <iframe id="frame2" / >
    </iframe>
</html>

切到frame2:從主文檔切到frame2,一層層切進去

driver.switch_to.frame("frame1")
driver.switch_to.frame("frame2")

 從frame2切到frame1:selenium給我們提供了一個方法能夠從子frame切回到父frame,而不用我們切回主文檔再切進來。

driver.switch_to.parent_frame()  # 如果當前已是主文檔,則無效果

從frame2 切回到主文檔:

driver.switch_to.default_content()

 

 有了parent_frame()這個相當於后退的方法,我們可以隨意切換不同的frame,隨意的跳來跳去了。

所以只要善用以下三個方法,遇到frame分分鍾搞定:

1 driver.switch_to.frame(reference)
2 driver.switch_to.parent_frame()
3 driver.switch_to.default_content()

切換瀏覽器窗口的用法,同切換frame類似:driver.switch_to.windows("")

等待頁面加載完成(Waits)

大多數的Web應用程序是使用Ajax技術。當一個頁面被加載到瀏覽器時, 該頁面內的元素可以在不同的時間點被加載。這使得定位元素變得困難, 如果元素不再頁面之中,會拋出 ElementNotVisibleException 異常。 使用 waits, 我們可以解決這個問題。waits提供了一些操作之間的時間間隔- 主要是定位元素或針對該元素的任何其他操作。

Selenium Webdriver 提供兩種類型的waits - 隱式和顯式。 顯

式等待會讓WebDriver等待滿足一定的條件以后再進一步的執行。

而隱式等待讓Webdriver等待一定的時間后再才是查找某元素。

顯示等待:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver = webdriver.Firefox()
driver.get("http://somedomain/url_that_delays_loading")
try:
    element = WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.ID, "myDynamicElement"))
    )
finally:
    driver.quit()

在拋出TimeoutException異常之前將等待10秒或者在10秒內發現了查找的元素。

WebDriverWait 默認情況下會每500毫秒調用一次ExpectedCondition直到結果成功返回。

ExpectedCondition成功的返回結果是一個布爾類型的true或是不為null的返回值。

預期的條件

自動化的Web瀏覽器中一些常用的預期條件,下面列出的是每一個實現, Selenium Python binding都提供了一些方便的方法,這樣你就不用去編寫 expected_condition類或是創建至今的工具包去實現他們。

from selenium.webdriver.support import expected_conditions as EC

wait = WebDriverWait(driver, 10)
element = wait.until(EC.element_to_be_clickable((By.ID,'someid')))

隱式等待

如果某些元素不是立即可用的,隱式等待是告訴WebDriver去等待一定的時間后去查找元素。 默認等待時間是0秒,一旦設置該值,隱式等待是設置該WebDriver的實例的生命周期。

from selenium import webdriver

driver = webdriver.Firefox()
driver.implicitly_wait(10) # seconds
driver.get("http://somedomain/url_that_delays_loading")
myDynamicElement = driver.find_element_by_id("myDynamicElement")

 

 

 

 

 

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM