1. 程式人生 > 實用技巧 >08.Python網路爬蟲之圖片懶載入技術、selenium和PhantomJS

08.Python網路爬蟲之圖片懶載入技術、selenium和PhantomJS

08.Python網路爬蟲之圖片懶載入技術、selenium和PhantomJS

引入

今日概要

  • 圖片懶載入
  • selenium
  • phantomJs
  • 谷歌無頭瀏覽器

知識點回顧

  • 驗證碼處理流程

今日詳情

動態資料載入處理

一.圖片懶載入

  • 什麼是圖片懶載入?
    • 案例分析:抓取站長素材http://sc.chinaz.com/中的圖片資料
      #!/usr/bin/env python
      # -*- coding:utf-8 -*-
      import requests
      from lxml import etree
      
      if __name__ == "__main__":
           url = 'http://sc.chinaz.com/tupian/gudianmeinvtupian.html'
      headers = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36', } #獲取頁面文字資料 response = requests.get(url=url,headers=headers) response.encoding = 'utf-8' page_text = response.text #解析頁面資料(獲取頁面中的圖片連結)
      #建立etree物件 tree = etree.HTML(page_text) div_list = tree.xpath('//div[@id="container"]/div') #解析獲取圖片地址和圖片的名稱 for div in div_list: image_url = div.xpath('.//img/@src') image_name = div.xpath('.//img/@alt') print(image_url) #列印圖片連結 print(image_name)#列印圖片名稱

    • - 執行結果觀察發現,我們可以獲取圖片的名稱,但是連結獲取的為空,檢查後發現xpath表示式也沒有問題,究其原因出在了哪裡呢?

    • 圖片懶載入概念:

      • 圖片懶載入是一種網頁優化技術。圖片作為一種網路資源,在被請求時也與普通靜態資源一樣,將佔用網路資源,而一次性將整個頁面的所有圖片載入完,將大大增加頁面的首屏載入時間。為了解決這種問題,通過前後端配合,使圖片僅在瀏覽器當前視窗內出現時才載入該圖片,達到減少首屏圖片請求數的技術就被稱為“圖片懶載入”。

    • 網站一般如何實現圖片懶載入技術呢?

      • 在網頁原始碼中,在img標籤中首先會使用一個“偽屬性”(通常使用src2,original......)去存放真正的圖片連結而並非是直接存放在src屬性中。當圖片出現到頁面的視覺化區域中,會動態將偽屬性替換成src屬性,完成圖片的載入。

    • 站長素材案例後續分析:通過細緻觀察頁面的結構後發現,網頁中圖片的連結是儲存在了src2這個偽屬性中

      #!/usr/bin/env python
      # -*- coding:utf-8 -*-
      import requests
      from lxml import etree
      
      if __name__ == "__main__":
           url = 'http://sc.chinaz.com/tupian/gudianmeinvtupian.html'
           headers = {
               'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
           }
           #獲取頁面文字資料
           response = requests.get(url=url,headers=headers)
           response.encoding = 'utf-8'
           page_text = response.text
           #解析頁面資料(獲取頁面中的圖片連結)
           #建立etree物件
           tree = etree.HTML(page_text)
           div_list = tree.xpath('//div[@id="container"]/div')
           #解析獲取圖片地址和圖片的名稱
           for div in div_list:
               image_url = div.xpath('.//img/@src2') #src2偽屬性
               image_name = div.xpath('.//img/@alt')
               print(image_url) #列印圖片連結
               print(image_name)#列印圖片名稱

簡介

selenium最初是一個自動化測試工具,而爬蟲中使用它主要是為了解決requests無法直接執行JavaScript程式碼的問題 selenium本質是通過驅動瀏覽器,完全模擬瀏覽器的操作,比如跳轉、輸入、點選、下拉等,來拿到網頁渲染之後的結果,可支援多種瀏覽器

環境安裝

  • 下載安裝selenium:pip install selenium
  • 下載瀏覽器驅動程式:
    • http://chromedriver.storage.googleapis.com/index.html
  • 檢視驅動和瀏覽器版本的對映關係:
    • http://blog.csdn.net/huilan_same/article/details/51896672

簡單使用/效果展示

from selenium import webdriver
from time import sleep

# 後面是你的瀏覽器驅動位置,記得前面加r'','r'是防止字元轉義的
driver = webdriver.Chrome(r'驅動程式路徑')
# 用get開啟百度頁面
driver.get("http://www.baidu.com")
# 查詢頁面的“設定”選項,並進行點選
driver.find_elements_by_link_text('設定')[0].click()
sleep(2)
# # 開啟設定後找到“搜尋設定”選項,設定為每頁顯示50條
driver.find_elements_by_link_text('搜尋設定')[0].click()
sleep(2)

# 選中每頁顯示50條
m = driver.find_element_by_id('nr')
sleep(2)
m.find_element_by_xpath('//*[@id="nr"]/option[3]').click()
m.find_element_by_xpath('.//option[3]').click()
sleep(2)

# 點選儲存設定
driver.find_elements_by_class_name("prefpanelgo")[0].click()
sleep(2)

# 處理彈出的警告頁面   確定accept() 和 取消dismiss()
driver.switch_to_alert().accept()
sleep(2)
# 找到百度的輸入框,並輸入 美女
driver.find_element_by_id('kw').send_keys('美女')
sleep(2)
# 點選搜尋按鈕
driver.find_element_by_id('su').click()
sleep(2)
# 在開啟的頁面中找到“Selenium - 開源中國社群”,並開啟這個頁面
driver.find_elements_by_link_text('美女_百度圖片')[0].click()
sleep(3)

# 關閉瀏覽器
driver.quit()

瀏覽器建立

Selenium支援非常多的瀏覽器,如Chrome、Firefox、Edge等,還有Android、BlackBerry等手機端的瀏覽器。另外,也支援無介面瀏覽器PhantomJS。

from selenium import webdriver
  
browser = webdriver.Chrome()
browser = webdriver.Firefox()
browser = webdriver.Edge()
browser = webdriver.PhantomJS()
browser = webdriver.Safari()

元素定位

webdriver 提供了一系列的元素定位方法,常用的有以下幾種:

find_element_by_id()
find_element_by_name()
find_element_by_class_name()
find_element_by_tag_name()
find_element_by_link_text()
find_element_by_partial_link_text()
find_element_by_xpath()
find_element_by_css_selector()

注意

1、find_element_by_xxx找的是第一個符合條件的標籤,find_elements_by_xxx找的是所有符合條件的標籤。

2、根據ID、CSS選擇器和XPath獲取,它們返回的結果完全一致。

3、另外,Selenium還提供了通用方法find_element(),它需要傳入兩個引數:查詢方式By和值。實際上,它就是find_element_by_id()這種方法的通用函式版本,比如find_element_by_id(id)就等價於find_element(By.ID, id),二者得到的結果完全一致。

節點互動

Selenium可以驅動瀏覽器來執行一些操作,也就是說可以讓瀏覽器模擬執行一些動作。比較常見的用法有:輸入文字時用send_keys()方法,清空文字時用clear()方法,點選按鈕時用click()方法。示例如下:

from selenium import webdriver
import time
 
browser = webdriver.Chrome()
browser.get('https://www.taobao.com')
input = browser.find_element_by_id('q')
input.send_keys('MAC')
time.sleep(1)
input.clear()
input.send_keys('IPhone')
button = browser.find_element_by_class_name('btn-search')
button.click()
browser.quit()

動作鏈

在上面的例項中,一些互動動作都是針對某個節點執行的。比如,對於輸入框,我們就呼叫它的輸入文字和清空文字方法;對於按鈕,就呼叫它的點選方法。其實,還有另外一些操作,它們沒有特定的執行物件,比如滑鼠拖曳、鍵盤按鍵等,這些動作用另一種方式來執行,那就是動作鏈。

比如,現在實現一個節點的拖曳操作,將某個節點從一處拖曳到另外一處,可以這樣實現:

from selenium import webdriver
from selenium.webdriver import ActionChains
import time
browser = webdriver.Chrome()
url = 'http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable'
browser.get(url)
browser.switch_to.frame('iframeResult')
source = browser.find_element_by_css_selector('#draggable')
target = browser.find_element_by_css_selector('#droppable')
actions = ActionChains(browser)
# actions.drag_and_drop(source, target)
# actions.perform() #執行動作鏈
actions.click_and_hold(source)
time.sleep(3)
for i in range(5):
    actions.move_by_offset(xoffset=17,yoffset=0).perform()
    time.sleep(0.5)

actions.release()

執行JavaScript

對於某些操作,Selenium API並沒有提供。比如,下拉進度條,它可以直接模擬執行JavaScript,此時使用execute_script()方法即可實現,程式碼如下:

from selenium import webdriver
 
browser = webdriver.Chrome()
browser.get('https://www.jd.com/')
browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')
browser.execute_script('alert("123")')

獲取頁面原始碼資料

通過page_source屬性可以獲取網頁的原始碼,接著就可以使用解析庫(如正則表示式、Beautiful Soup、pyquery等)來提取資訊了。

前進和後退

#模擬瀏覽器的前進後退
import time
from selenium import webdriver
 
browser=webdriver.Chrome()
browser.get('https://www.baidu.com')
browser.get('https://www.taobao.com')
browser.get('http://www.sina.com.cn/')
 
browser.back()
time.sleep(10)
browser.forward()
browser.close()

Cookie處理

使用Selenium,還可以方便地對Cookies進行操作,例如獲取、新增、刪除Cookies等。示例如下:

from selenium import webdriver
 
browser = webdriver.Chrome()
browser.get('https://www.zhihu.com/explore')
print(browser.get_cookies())
browser.add_cookie({'name': 'name', 'domain': 'www.zhihu.com', 'value': 'germey'})
print(browser.get_cookies())
browser.delete_all_cookies()
print(browser.get_cookies())

異常處理

from selenium import webdriver
from selenium.common.exceptions import TimeoutException,NoSuchElementException,NoSuchFrameException

try:
    browser=webdriver.Chrome()
    browser.get('http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
    browser.switch_to.frame('iframssseResult')

except TimeoutException as e:
    print(e)
except NoSuchFrameException as e:
    print(e)
finally:
    browser.close()

phantomJS

PhantomJS是一款無介面的瀏覽器,其自動化操作流程和上述操作谷歌瀏覽器是一致的。由於是無介面的,為了能夠展示自動化操作流程,PhantomJS為使用者提供了一個截圖的功能,使用save_screenshot函式實現。

from selenium import webdriver
import time

# phantomjs路徑
path = r'PhantomJS驅動路徑'
browser = webdriver.PhantomJS(path)

# 開啟百度
url = 'http://www.baidu.com/'
browser.get(url)

time.sleep(3)

browser.save_screenshot(r'phantomjs\baidu.png')

# 查詢input輸入框
my_input = browser.find_element_by_id('kw')
# 往框裡面寫文字
my_input.send_keys('美女')
time.sleep(3)
#截圖
browser.save_screenshot(r'phantomjs\meinv.png')

# 查詢搜尋按鈕
button = browser.find_elements_by_class_name('s_btn')[0]
button.click()

time.sleep(3)

browser.save_screenshot(r'phantomjs\show.png')

time.sleep(3)

browser.quit()

谷歌無頭瀏覽器

由於PhantomJs最近已經停止了更新和維護,所以推薦大家可以使用谷歌的無頭瀏覽器,是一款無介面的谷歌瀏覽器。

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import time
 
# 建立一個引數物件,用來控制chrome以無介面模式開啟
chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
# 驅動路徑
path = r'C:\Users\ZBLi\Desktop\1801\day05\ziliao\chromedriver.exe'
 
# 建立瀏覽器物件
browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options)
 
# 上網
url = 'http://www.baidu.com/'
browser.get(url)
time.sleep(3)
 
browser.save_screenshot('baidu.png')
 
browser.quit()

登入qq空間,爬取資料

import requests
from selenium import webdriver
from lxml import etree
import time

driver = webdriver.Chrome(executable_path='/Users/bobo/Desktop/chromedriver')
driver.get('https://qzone.qq.com/')
#在web 應用中經常會遇到frame 巢狀頁面的應用,使用WebDriver 每次只能在一個頁面上識別元素,對於frame 巢狀內的頁面上的元素,直接定位是定位是定位不到的。這個時候就需要通過switch_to_frame()方法將當前定位的主體切換了frame 裡。
driver.switch_to.frame('login_frame')
driver.find_element_by_id('switcher_plogin').click()

#driver.find_element_by_id('u').clear()
driver.find_element_by_id('u').send_keys('328410948')  #這裡填寫你的QQ號
#driver.find_element_by_id('p').clear()
driver.find_element_by_id('p').send_keys('xxxxxx')  #這裡填寫你的QQ密碼
    
driver.find_element_by_id('login_button').click()
time.sleep(2)
driver.execute_script('window.scrollTo(0,document.body.scrollHeight)')
time.sleep(2)
driver.execute_script('window.scrollTo(0,document.body.scrollHeight)')
time.sleep(2)
driver.execute_script('window.scrollTo(0,document.body.scrollHeight)')
time.sleep(2)
page_text = driver.page_source

tree = etree.HTML(page_text)
#執行解析操作
li_list = tree.xpath('//ul[@id="feed_friend_list"]/li')
for li in li_list:
    text_list = li.xpath('.//div[@class="f-info"]//text()|.//div[@class="f-info qz_info_cut"]//text()')
    text = ''.join(text_list)
    print(text+'\n\n\n')
    
driver.close()

儘可能多的爬取豆瓣網中的電影資訊

from selenium import webdriver
from time import sleep
import time

if __name__ == '__main__':
    url = 'https://movie.douban.com/typerank?type_name=%E6%81%90%E6%80%96&type=20&interval_id=100:90&action='
    # 發起請求前,可以讓url表示的頁面動態加載出更多的資料
    path = r'C:\Users\Administrator\Desktop\爬蟲授課\day05\ziliao\phantomjs-2.1.1-windows\bin\phantomjs.exe'
    # 建立無介面的瀏覽器物件
    bro = webdriver.PhantomJS(path)
    # 發起url請求
    bro.get(url)
    time.sleep(3)
    # 截圖
    bro.save_screenshot('1.png')

    # 執行js程式碼(讓滾動條向下偏移n個畫素(作用:動態載入了更多的電影資訊))
    js = 'window.scrollTo(0,document.body.scrollHeight)'
    bro.execute_script(js)  # 該函式可以執行一組字串形式的js程式碼
    time.sleep(2)

    bro.execute_script(js)  # 該函式可以執行一組字串形式的js程式碼
    time.sleep(2)
    bro.save_screenshot('2.png') 
    time.sleep(2) 
    # 使用爬蟲程式爬去當前url中的內容 
    html_source = bro.page_source # 該屬性可以獲取當前瀏覽器的當前頁的原始碼(html) 
    with open('./source.html', 'w', encoding='utf-8') as fp: 
        fp.write(html_source) 
    bro.quit()

selenium規避被檢測識別

現在不少大網站有對selenium採取了監測機制。比如正常情況下我們用瀏覽器訪問淘寶等網站的 window.navigator.webdriver的值為
undefined。而使用selenium訪問則該值為true。那麼如何解決這個問題呢?

只需要設定Chromedriver的啟動引數即可解決問題。在啟動Chromedriver之前,為Chrome開啟實驗性功能引數excludeSwitches,它的值為['enable-automation'],完整程式碼如下:

from selenium.webdriver import Chrome
from selenium.webdriver import ChromeOptions

option = ChromeOptions()
option.add_experimental_option('excludeSwitches', ['enable-automation'])
driver = Chrome(options=option)

作業

  • 爬取網易新聞國內板塊下的新聞標題和新聞內容