动态渲染页面抓取 JavaScript 动态渲染的页面不止 Ajax 这一种,它的分页部分是由 JavaScript 生成的,并非原始 HTML 代码,这其中并不包含 Ajax 请求。再有淘宝这种页面,它即使是 Ajax 获取的数据,但是其 Ajax 接口含有很多加密参数,我们难以直接找出其规律,也很难直接分析 Ajax 来抓取。
可以直接使用模拟浏览器运行的方式来实现,这样就可以做到在浏览器中看到是什么样,抓取的源码就是什么样,也就是可见即可爬。这样我们就不用再去管网页内部的 JavaScript 用了什么算法渲染页面,不用管网页后台的 Ajax 接口到底有哪些参数。
Selenium的使用 基本使用 from selenium import webdriverfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.common.keys import Keysfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.support.wait import WebDriverWaitbrowser = webdriver.Chrome() try : browser.get('https://www.baidu.com' ) input = browser.find_element(By.ID,'kw' ) input .send_keys('Python' ) input .send_keys(Keys.ENTER) wait = WebDriverWait(browser, 10 ) wait.until(EC.presence_of_element_located((By.ID, 'content_left' ))) print (browser.current_url) print (browser.get_cookies()) print (browser.page_source) finally : browser.close()
声明浏览器对象 from selenium import webdriverbrowser = webdriver.Chrome() browser = webdriver.Firefox() browser = webdriver.Edge() browser = webdriver.PhantomJS() browser = webdriver.Safari()
访问页面 get() 方法来请求网页,参数传入链接 URL 即可
from selenium import webdriverbrowser = webdriver.Chrome() browser.get('https://www.taobao.com' ) print (browser.page_source)browser.close()
查找节点 单个节点 from selenium import webdriverfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.common.keys import Keysbrowser = webdriver.Chrome() browser.get('https://www.taobao.com' ) input_first = browser.find_element(By.ID,'q' ) input_second = browser.find_element(By.CSS_SELECTOR,'#q' ) input_third = browser.find_element(By.XPATH,'//*[@id="q"]' ) print (input_first, input_second, input_third)browser.close()
多个节点 find_element() 方法查找,就只能得到第一个节点。如果要查找所有满足条件的节点,需要用 find_elements() 这样的方法,得到的内容变成了列表类型
节点交互 from selenium import webdriverimport timebrowser = webdriver.Chrome() browser.get('https://www.taobao.com' ) input = browser.find_element_by_id('q' )input .send_keys('iPhone' )time.sleep(1 ) input .clear()input .send_keys('iPad' )button = browser.find_element_by_class_name('btn-search' ) button.click()
动作链 还有另外一些操作,它们没有特定的执行对象,比如鼠标拖曳、键盘按键等,这些动作用另一种方式来执行,那就是动作链
实现一个节点的拖曳操作,将某个节点从一处拖曳到另外一处
from selenium import webdriverfrom selenium.webdriver import ActionChainsfrom selenium.webdriver.common.by import Bybrowser = 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()
首先,打开网页中的一个拖曳实例,然后依次选中要拖曳的节点和拖曳到的目标节点,接着声明 ActionChains 对象并将其赋值为 actions 变量,然后通过调用 actions 变量的 drag_and_drop() 方法,再调用 perform() 方法执行动作,此时就完成了拖曳操作
执行 JavaScript 对于某些操作,Selenium API 并没有提供。比如,下拉进度条,它可以直接模拟运行 JavaScript,此时使用 execute_script() 方法即可实现
from selenium import webdriverbrowser = webdriver.Chrome() browser.get('https://www.zhihu.com/explore' ) browser.execute_script('window.scrollTo(0, document.body.scrollHeight)' ) browser.execute_script('alert("To Bottom")' )
切换 Frame 网页中有一种节点叫作 iframe,也就是子 Frame,相当于页面的子页面,它的结构和外部网页的结构完全一致。Selenium 打开页面后,它默认是在父级 Frame 里面操作,而此时如果页面中还有子 Frame,它是不能获取到子 Frame 里面的节点的。这时就需要使用 switch_to.frame() 方法来切换 Frame
import timefrom selenium import webdriverfrom selenium.common.exceptions import NoSuchElementExceptionbrowser = webdriver.Chrome() url = 'http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable' browser.get(url) browser.switch_to.frame('iframeResult' ) try : logo = browser.find_element_by_class_name('logo' ) except NoSuchElementException: print ('NO LOGO' ) browser.switch_to.parent_frame() logo = browser.find_element_by_class_name('logo' ) print (logo)print (logo.text)
延时等待 Selenium 中,get() 方法会在网页框架加载结束后结束执行,此时如果获取 page_source,可能并不是浏览器完全加载完成的页面,如果某些页面有额外的 Ajax 请求,我们在网页源代码中也不一定能成功获取到。所以,这里需要延时等待一定时间,确保节点已经加载出来。
这里等待的方式有两种:一种是隐式等待,一种是显式等待
显式等待 它指定要查找的节点,然后指定一个最长等待时间。如果在规定时间内加载出来了这个节点,就返回查找的节点;如果到了规定时间依然没有加载出该节点,则抛出超时异常
from selenium import webdriverfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECbrowser = webdriver.Chrome() browser.get('https://www.taobao.com/' ) wait = WebDriverWait(browser, 10 ) input = wait.until(EC.presence_of_element_located((By.ID, 'q' )))button = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '.btn-search' ))) print (input , button)
前进后退 import timefrom selenium import webdriverbrowser = webdriver.Chrome() browser.get('https://www.baidu.com/' ) browser.get('https://www.taobao.com/' ) browser.get('https://www.python.org/' ) browser.back() time.sleep(1 ) browser.forward() browser.close()
Cookies from selenium import webdriverbrowser = 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())
选项卡管理 import timefrom selenium import webdriverbrowser = webdriver.Chrome() browser.get('https://www.baidu.com' ) browser.execute_script('window.open()' ) print (browser.window_handles)browser.switch_to_window(browser.window_handles[1 ]) browser.get('https://www.taobao.com' ) time.sleep(1 ) browser.switch_to_window(browser.window_handles[0 ]) browser.get('https://python.org' )
传入 window.open() 这个 JavaScript 语句新开启一个选项卡
调用 window_handles 属性获取当前开启的所有选项卡,返回的是选项卡的代号列表。要想切换选项卡,只需要调用 switch_to_window() 方法即可,其中参数是选项卡的代号
异常处理 from selenium import webdriverfrom selenium.common.exceptions import TimeoutException, NoSuchElementExceptionbrowser = webdriver.Chrome() try : browser.get('https://www.baidu.com' ) except TimeoutException: print ('Time Out' ) try : browser.find_element_by_id('hello' ) except NoSuchElementException: print ('No Element' ) finally : browser.close()