圖片懶加載技術
什么是圖片懶加載技術
圖片懶加載是一種網頁優化技術。圖片作為一種網絡資源,在被請求時也與普通靜態資源一樣,將占用網絡資源,而一次性將整個頁面的所有圖片加載完,將大大增加頁面的首屏加載時間。為了解決這種問題,通過前后端配合,使圖片僅在瀏覽器當前視窗內出現時才加載該圖片,達到減少首屏圖片請求數的技術就被稱為“圖片懶加載”。
如何實現圖片懶加載技術
在網頁源碼中,在img標簽中首先會使用一個“偽屬性”(通常使用src2,original......)去存放真正的圖片鏈接而並非是直接存放在src屬性中。當圖片出現到頁面的可視化區域中,會動態將偽屬性替換成src屬性,完成圖片的加載。
如何破解圖片懶加載技術
其實很簡單,在對標簽屬性進行定位的時候,仔細觀察它真正的屬性進行提取。
例如:
import requests from lxml import etree def main(): responses = requests.get(url=url, headers=headers) coding = responses.apparent_encoding responses.encoding = coding res_text = responses.text # 創建etree對象
tree = etree.HTML(res_text) div_lst = tree.xpath('//div[@id="container"]/div') for one_div in div_lst: image_name = one_div.xpath('.//img/@alt')[0] image_url = one_div.xpath('.//img/@src2') print(image_name) print(image_url) if __name__ == '__main__': headers = {"User-Agent":'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0'} url = 'http://sc.chinaz.com/tupian/gudianmeinvtupian.html' main()
以上例子中就將src屬性換為了src2屬性。
selenium
1 什么是Selenium?
selenium是Python的一個第三方庫,對外提供的接口可以操作瀏覽器,然后讓瀏覽器完成自動化的操作。
selenium最初是一個自動化測試工具,而爬蟲中使用它主要是為了解決requests無法直接執行JavaScript代碼的問題selenium本質是通過驅動瀏覽器,完全模擬瀏覽器的操作,比如跳轉、輸入、點擊、下拉等,來拿到網頁渲染之后的結果,可支持多種瀏覽器。
2 前期准備
selenium安裝就不再概述了
查看自己的谷歌瀏覽器版本
查看自己對用的谷歌驅動: https://blog.csdn.net/huilan_same/article/details/51896672
下載自己的谷歌驅動: http://chromedriver.storage.googleapis.com/index.html
這里我下載是:
然后加壓后把它放到python解釋器的scripts目錄中即可.
3 簡單示例
from selenium import webdriver from time import sleep # 啟動你的谷歌瀏覽
driver = webdriver.Chrome() # 用get方法打開百度首頁
driver.get('https://www.baidu.com') # 找到第一個鏈接標簽的文本並點擊
driver.find_elements_by_link_text('設置')[0].click() # 睡一秒
sleep(1) # 找到搜索設置並點擊
driver.find_elements_by_link_text('搜索設置')[0].click() sleep(2) # 找到id為nr的標簽
ch = driver.find_element_by_id('nr') sleep(1) # 選擇第3個標簽點擊 # ch.find_element_by_xpath('//*[@id="nr"]/option[3]').click()
ch.find_element_by_xpath('.//option[3]').click() sleep(2) # 保存設置
driver.find_elements_by_class_name("prefpanelgo")[0].click() sleep(2) # 處理彈出的警告
driver.switch_to_alert().accept() sleep(2) # 網搜索框里傳入數據
driver.find_element_by_id('kw').send_keys('nba') sleep(2) # 點擊搜索按鈕
driver.find_element_by_id('su').click() sleep(2) # 關閉驅動
driver.close() # 關閉瀏覽器
driver.quit()
這是一個設置百度搜索每個頁面顯示50條數據並搜索nba的程序
Selenium支持的瀏覽器非常多,如Chrome、Firefox、Edge等,還有Android、BlackBerry等手機端的瀏覽器。另外,也支持無界面瀏覽器PhantomJS,也就是后面要說的內容。
4 webdriver的元素定位
# 通過id屬性
find_element_by_id() # 通過name屬性
find_element_by_name() # 通過class的名字
find_element_by_class_name() # 通過標簽的名字
find_element_by_tag_name() # 通過鏈接標簽的文本
find_element_by_link_text() # 通過鏈接標簽的部分文本
find_element_by_partial_link_text() # 通過xpath
find_element_by_xpath() # 通過css選擇器
find_element_by_css_selector() 注意: 1 find_element_by_xxx找的是第一個符合條件的標簽,find_elements_by_xxx找的是所有符合條件的標簽。 2 elenium還提供了通用方法find_element(),它需要傳入兩個參數:查找方式By和值。實際上,它就是find_element_by_id() 這種方法的通用函數版本,比如find_element_by_id(id)就等價於find_element(By.ID, id),二者得到的結果完全一致。
5 對節點進行交互
# 通過id知道輸入框
input = browser.find_element_by_id('q') # 向里面出入文本MAC
input.send_keys('MAC') # 清楚文本框
input.clear() # 再傳入一個文本IPhone
input.send_keys('IPhone') # 通過classname找到一個事件
button = browser.find_element_by_class_name('btn-search') # 點擊它
button.click()
6 動作鏈
什么是動作鏈呢?
在節點交互中,交互動作都是針對某個節點執行的。比如對於輸入框,就調用它輸入和清空的方法,對於按鈕,就調用它點擊的方法,其實還有一些操作,它們沒有特定的執行對象,比如鼠標拖拽,鍵盤按鍵,它們使用另一種方式來執行,就是動作鏈。
以下例子演示了把一個節點從一處拖動到另一處:
from selenium import webdriver from time import sleep from selenium.webdriver import ActionChains driver = webdriver.Chrome() # 菜鳥教程的js演示代碼
url = 'http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable' driver.get(url) # 進入嵌套的頁面
driver.switch_to.frame('iframeResult') # 點位要拖動的標簽
start = driver.find_element_by_css_selector('.ui-draggable') # 拖動的目標標簽
end = driver.find_element_by_css_selector('.ui-droppable') # 創建一個鼠標事件
actions = ActionChains(driver) # 點擊要拖動的標簽不松動
sleep(1) actions.click_and_hold(start).perform() # 然后移動到另一個標簽
actions.move_to_element(end).perform() # 往x軸移動40,y軸移動20
sleep(1) actions.move_by_offset(xoffset=40, yoffset=20).perform() # 釋放動作
actions.release()
可能有人會對driver.switch_to.frame('iframeResult')這句代碼有疑問
這個就是frame嵌套的。
在 web 應用中經常會出現 iframe 嵌套的應用,假設頁面上有 A、B 兩個 iframe,其中 B 在 A 內,那么定位 B 中的內容則需要先到 A,然后再到 B。
所以我們使用driver.switch_to.frame('iframeResult')直接進入嵌套的頁面。
7 執行js
有些api是selenium沒有提供的,比如下拉進度條,但是可以通過嗎模擬運行js來實現。
from selenium import webdriver from time import sleep driver = webdriver.Chrome() driver.get('https://www.jd.com/') # 移動到頁面最底部
driver.execute_script("window.scrollTo(0, document.body.scrollHeight)") sleep(2) # 執行alert123這個代碼 就是彈出123
driver.execute_script("alert('123')")
這一塊自動化測試用的挺多的,想學自動化測試的同學可以多學習下。
8 獲取節點信息
通過page_source屬性可以獲取網頁的源代碼,接着就可以使用解析庫(如正則表達式、Beautiful Soup、pyquery等)來提取信息了。
selenium自己也提供了提取節點信息的方法,如屬性、文本等。這樣的話,我們就可以不用通過解析源代碼來提取信息了,非常方便。
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as E from selenium.webdriver.support.wait import WebDriverWait driver = webdriver.Chrome() # 訪問亞馬遜首頁
driver.get("https://www.amazon.cn/") # 顯示等待10秒
wait = WebDriverWait(driver, 10) # 判斷元素是否被加載到了dom中
wait.until(E.presence_of_element_located((By.ID,'cc-lm-tcgShowImgContainer'))) # 通過選擇器找到這個元素
tag=driver.find_element(By.CSS_SELECTOR,'#cc-lm-tcgShowImgContainer img') #獲取標簽屬性,
print(tag.get_attribute('src')) #獲取標簽ID,位置,名稱,大小(了解)
print(tag.id) print(tag.location) print(tag.tag_name) print(tag.size) driver.close()
9 延時等待
在selenium中,get()方法會在頁面框加載結束后,結束執行。此時如果獲得get_source,可能不是瀏覽器完全加載完成后的頁面,某些額外的js,ajax可能不一定獲取到,所以需要延時等待一下,確保節點加載出來了,這里等待的方式有2中:顯式等待和隱式等待。
1 顯式等待
上個例子就用到了顯式等待,顯式等待會設置一個固定的等待時間,但是實際情況是由於外界因素的影響,我們也不可能判斷出需要多少時間,所有顯示等待還有一種寫法,如果在規定時間加載出了這個節點,就對這個節點進行操作,如果沒有加載出,就拋出超時異常。
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support import expected_conditions as E from selenium.webdriver.support.wait import WebDriverWait driver = webdriver.Chrome() driver.get('https://www.baidu.com/') input_tag = driver.find_element_by_id('kw') input_tag.send_keys('美女') # 模擬鍵盤回車鍵
input_tag.send_keys(Keys.ENTER) # 創建一個顯式等待
wait = WebDriverWait(driver, 10) # 等待直到加載出content_left(content是左半區)
wait.until(E.presence_of_element_located((By.ID, 'content_left'))) content = driver.find_element(By.CSS_SELECTOR, "#content_left") print(content) driver.close()
如果將webDriverWait中的時間設置過低,會報錯:
2 隱式等待
當使用隱式等待執行測試的時候,如果Selenium沒有在DOM中找到節點,將繼續等待,超出設定時間后,則拋出找不到節點的異常。
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys driver = webdriver.Chrome() # 設置隱式等待
driver.implicitly_wait(10) driver.get('https://www.baidu.com/') input_tag = driver.find_element_by_id('kw') input_tag.send_keys('NBA') # 模擬鍵盤回車鍵
input_tag.send_keys(Keys.ENTER) content = driver.find_element(By.CSS_SELECTOR, "#content_left") print(content) driver.close()
10 cookie
使用selenium還可以對cookie進行操作,獲取,添加,刪除cookie:
from selenium import webdriver driver = webdriver.Chrome() driver.get('https://www.zhihu.com/explore') # 獲取cookie
print(driver.get_cookies()) # 添加cookie
driver.add_cookie({'name': 'name', 'domain': 'www.zhihu.com', 'value': 'germey'}) print(driver.get_cookies()) # 刪除cookie
driver.delete_all_cookies() print(driver.get_cookies())
[{'domain': '.zhihu.com', 'httpOnly': False, 'name': 'l_n_c', 'path': '/', 'secure': False, 'value': '1'}, {'domain': 'www.zhihu.com', 'expiry': 1555763561.220174, 'httpOnly': False, 'name': 'tgw_l7_route', 'path': '/', 'secure': False, 'value': '537a925d07d06cecbf34cd06a153f671'}, {'domain': '.zhihu.com', 'expiry': 1650370661.220268, 'httpOnly': False, 'name': 'q_c1', 'path': '/', 'secure': False, 'value': '9ca2467feeaa4987a63360951034c876|1555762662000|1555762662000'}, {'domain': 'www.zhihu.com', 'httpOnly': False, 'name': '_xsrf', 'path': '/', 'secure': False, 'value': '270095e8c0bc05224a3eb61674d59ce0'}, {'domain': '.zhihu.com', 'expiry': 1558354661.220341, 'httpOnly': False, 'name': 'r_cap_id', 'path': '/', 'secure': False, 'value': '"ZjhkMmMxZWE5NjlhNDY3OWE4ODA1NjI4MjhkYzc0Y2Y=|1555762662|257a989723432c66a22d10794cd998b22d419794"'}, {'domain': '.zhihu.com', 'expiry': 1558354661.220391, 'httpOnly': False, 'name': 'cap_id', 'path': '/', 'secure': False, 'value': '"NTY2OWZiYmI2ODdlNDFkZWE1MzBiZTEyYTQ5MDFmYzk=|1555762662|5c5e5d6a7150c0da99f86af9d85cc63cafbea752"'}, {'domain': '.zhihu.com', 'expiry': 1558354661.220438, 'httpOnly': False, 'name': 'l_cap_id', 'path': '/', 'secure': False, 'value': '"Njk2NWI1ZTIxYjg2NGZhZDhiOGJiYjM5ODg0Y2U2ZmM=|1555762662|6a4e7d4ab33c72012e5bf133ffadfe34aa88b8eb"'}, {'domain': '.zhihu.com', 'httpOnly': False, 'name': 'n_c', 'path': '/', 'secure': False, 'value': '1'}, {'domain': '.zhihu.com', 'expiry': 1650370664.061236, 'httpOnly': False, 'name': 'd_c0', 'path': '/', 'secure': False, 'value': '"AJDm50C_Tg-PTkHkupcfIBQqf8xnPhGsu4M=|1555762665"'}, {'domain': '.zhihu.com', 'expiry': 1633522664.358119, 'httpOnly': False, 'name': '_xsrf', 'path': '/', 'secure': False, 'value': 'mzatpYVVTALiHdu45yqz9q4yljxCPZ7n'}, {'domain': '.zhihu.com', 'expiry': 1618834664, 'httpOnly': False, 'name': '_zap', 'path': '/', 'secure': False, 'value': 'ec95407d-3b81-4e43-81d5-277adf4b33b9'}, {'domain': '.zhihu.com', 'expiry': 1618834664, 'httpOnly': False, 'name': '__utma', 'path': '/', 'secure': False, 'value': '51854390.2113439272.1555762665.1555762665.1555762665.1'}, {'domain': '.zhihu.com', 'expiry': 1555764464, 'httpOnly': False, 'name': '__utmb', 'path': '/', 'secure': False, 'value': '51854390.0.10.1555762665'}, {'domain': '.zhihu.com', 'httpOnly': False, 'name': '__utmc', 'path': '/', 'secure': False, 'value': '51854390'}, {'domain': '.zhihu.com', 'expiry': 1571530664, 'httpOnly': False, 'name': '__utmz', 'path': '/', 'secure': False, 'value': '51854390.1555762665.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)'}, {'domain': '.zhihu.com', 'expiry': 1618834664, 'httpOnly': False, 'name': '__utmv', 'path': '/', 'secure': False, 'value': '51854390.000--|3=entry_date=20190420=1'}] [{'domain': '.zhihu.com', 'httpOnly': False, 'name': 'l_n_c', 'path': '/', 'secure': False, 'value': '1'}, {'domain': 'www.zhihu.com', 'expiry': 1555763561.220174, 'httpOnly': False, 'name': 'tgw_l7_route', 'path': '/', 'secure': False, 'value': '537a925d07d06cecbf34cd06a153f671'}, {'domain': '.zhihu.com', 'expiry': 1650370661.220268, 'httpOnly': False, 'name': 'q_c1', 'path': '/', 'secure': False, 'value': '9ca2467feeaa4987a63360951034c876|1555762662000|1555762662000'}, {'domain': 'www.zhihu.com', 'httpOnly': False, 'name': '_xsrf', 'path': '/', 'secure': False, 'value': '270095e8c0bc05224a3eb61674d59ce0'}, {'domain': '.zhihu.com', 'expiry': 1558354661.220341, 'httpOnly': False, 'name': 'r_cap_id', 'path': '/', 'secure': False, 'value': '"ZjhkMmMxZWE5NjlhNDY3OWE4ODA1NjI4MjhkYzc0Y2Y=|1555762662|257a989723432c66a22d10794cd998b22d419794"'}, {'domain': '.zhihu.com', 'expiry': 1558354661.220391, 'httpOnly': False, 'name': 'cap_id', 'path': '/', 'secure': False, 'value': '"NTY2OWZiYmI2ODdlNDFkZWE1MzBiZTEyYTQ5MDFmYzk=|1555762662|5c5e5d6a7150c0da99f86af9d85cc63cafbea752"'}, {'domain': '.zhihu.com', 'expiry': 1558354661.220438, 'httpOnly': False, 'name': 'l_cap_id', 'path': '/', 'secure': False, 'value': '"Njk2NWI1ZTIxYjg2NGZhZDhiOGJiYjM5ODg0Y2U2ZmM=|1555762662|6a4e7d4ab33c72012e5bf133ffadfe34aa88b8eb"'}, {'domain': '.zhihu.com', 'httpOnly': False, 'name': 'n_c', 'path': '/', 'secure': False, 'value': '1'}, {'domain': '.zhihu.com', 'expiry': 1650370664.061236, 'httpOnly': False, 'name': 'd_c0', 'path': '/', 'secure': False, 'value': '"AJDm50C_Tg-PTkHkupcfIBQqf8xnPhGsu4M=|1555762665"'}, {'domain': '.zhihu.com', 'expiry': 1633522664.358119, 'httpOnly': False, 'name': '_xsrf', 'path': '/', 'secure': False, 'value': 'mzatpYVVTALiHdu45yqz9q4yljxCPZ7n'}, {'domain': '.zhihu.com', 'expiry': 1618834664, 'httpOnly': False, 'name': '_zap', 'path': '/', 'secure': False, 'value': 'ec95407d-3b81-4e43-81d5-277adf4b33b9'}, {'domain': '.zhihu.com', 'expiry': 1618834664, 'httpOnly': False, 'name': '__utma', 'path': '/', 'secure': False, 'value': '51854390.2113439272.1555762665.1555762665.1555762665.1'}, {'domain': '.zhihu.com', 'expiry': 1555764464, 'httpOnly': False, 'name': '__utmb', 'path': '/', 'secure': False, 'value': '51854390.0.10.1555762665'}, {'domain': '.zhihu.com', 'httpOnly': False, 'name': '__utmc', 'path': '/', 'secure': False, 'value': '51854390'}, {'domain': '.zhihu.com', 'expiry': 1571530664, 'httpOnly': False, 'name': '__utmz', 'path': '/', 'secure': False, 'value': '51854390.1555762665.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)'}, {'domain': '.zhihu.com', 'expiry': 1618834664, 'httpOnly': False, 'name': '__utmv', 'path': '/', 'secure': False, 'value': '51854390.000--|3=entry_date=20190420=1'}, {'domain': 'www.zhihu.com', 'expiry': 2186482664, 'httpOnly': False, 'name': 'name', 'path': '/', 'secure': True, 'value': 'germey'}] []
11 異常處理
from selenium import webdriver from selenium.common.exceptions import TimeoutException, NoSuchFrameException try: driver = webdriver.Chrome() driver.get('http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable') driver.switch_to.frame('iframssseResult') except TimeoutException as e: print(e) except NoSuchFrameException as e: print(e) finally: driver.close()
超時等待哪里就可以這樣寫。
phantomJs
phantomJs是一款無界面的瀏覽器,其自動化操作流程和上面的谷歌瀏覽器是一致的。
由於是無界面的,為了能夠展示自動化操作流程,PhantomJS為用戶提供了一個截屏的功能,使用save_screenshot函數實現。
from selenium import webdriver import time # exe路徑
path = r'C:\phantomjs-2.1.1-windows\bin\phantomjs.exe' driver = webdriver.PhantomJS(path) driver.get("https://www.baidu.com") time.sleep(2) driver.save_screenshot(r'baidu.png') my_input = driver.find_element_by_id('kw') my_input.send_keys('nba') driver.save_screenshot(r'nba.png') # 查找搜索按鈕
button = driver.find_elements_by_class_name('s_btn')[0].click() time.sleep(3) driver.save_screenshot(r'show.png') driver.close() driver.quit()
使用selenum+phantomJs可以解決某些網頁js動態加載問題。
selenium+phantomJs爬取豆瓣電影的電影信息
首先來看下這個頁面:https://movie.douban.com/typerank?type_name=%E6%81%90%E6%80%96&type=20&interval_id=100:90&action=
查看他的網頁源代碼:發現它是js動態加載顯示的
from selenium import webdriver import time def main(): path = r'C:\phantomjs-2.1.1-windows\bin\phantomjs.exe' driver = webdriver.PhantomJS(path) driver.get(url) time.sleep(2) driver.save_screenshot(r'1.png') driver.execute_script('window.scrollTo(0,document.body.scrollHeight)') time.sleep(2) driver.save_screenshot('2.png') time.sleep(1) html_source = driver.page_source with open('source.html', 'w', encoding='utf-8') as f: f.write(html_source) driver.quit() if __name__ == "__main__": url = 'https://movie.douban.com/typerank?type_name=%E6%81%90%E6%80%96&type=20&interval_id=100:90&action=' main()
這樣就可以拿到它的源碼了:
谷歌無頭瀏覽器 Headless Chrome
由於phantomJs已經停止了更新和維護,所以推薦使用谷歌的無頭瀏覽器,這是一款無界面的谷歌瀏覽器
from selenium import webdriver from selenium.webdriver.chrome.options import Options from time import sleep # 創建一個無頭瀏覽器對象
chrome_options = Options() # 設置它為無框模式
chrome_options.add_argument('--headless') # 如果在windows上運行需要加代碼
chrome_options.add_argument('--disable-gpu') browser = webdriver.Chrome(chrome_options=chrome_options) browser.get('https://www.baidu.com/') sleep(2) browser.save_screenshot('first_page.png') browser.quit()