天天看点

Class 17 - 1 动态渲染页面爬取 — Selenium使用

利用Selenium 可以驱动浏览器执行特定的动作,如点击、下拉等操作, 同时还可以获取浏览器当前呈现的页面的源代码 ,做到可见即可爬。

  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 EC
    from selenium.webdriver.support.wait import WebDriverWait
    browser = 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()      
    运行代码后,会自动弹出一个 Chrome 浏览器。首先会跳转到百度,然后在搜索框中输入 Python,接着跳转到搜索页。  搜索结果加载出来后,控制台分别会输出当前的 URL 、当前的 Cookies 和网页源代码.
  • 如果用 Selenium 来驱动浏览器加载网页的话,就可以直接拿到 JavaScript 渲染的结果,不用担心使用的是什么加密系统。声明浏览器对象  

声明浏览器对象

  • Selenium 支持非常多浏览器,如 Chrome/ Firefox/ Edge等,还有 Android 等手机端的浏览器,也支持无界面浏览器 PhantomJS。
    • 初始化方式:
      from selenium import webdriver
      browser = webdriver.Chrome()
      browser = webdriver.Firefox()
      browser = webdriver.Edge()
      browser = webdriver.PhantomJS()
      browser = webdriver.Safari()      
      浏览器对象的初始化并将其赋值为 browser对象。接下来,就是调用 browser 对象,让其执行各个动作以模拟浏览器操作

访问页面

  • 用 get()方法来请求网页,参数传入链接 URL 即可。如用 get()方法访问淘宝, 然后输出源代码,代码:
    from selenium import webdriver
    browser = webdriver.Chrome()
    browser.get('https://www.taobao.com/')
    print(browser.page_source)      
    弹出了 Chrome 浏览器并且自动访问了淘宝,然后控制台输出了淘宝页面的源代码, 随后浏览器关闭。可以实现浏览器的驱动并获取网页源码

查找节点

  • Selenium 可以驱动浏览器完成各种操作,如填充表单、模拟点击等。Selenium 提供了系列查找节点的方法,我们可以用这些方法来获取想要的节点,以便执行一些动作或者提取信息。
  1. 单个节点
    • 如,要从淘宝页面中提取搜索框这个节点,观察源代码(它的 id 是 q, name 也是q。此时就可以用多种方式获取它。如,find_element_by_name()是根据 name 值获取,find element_by_id()是根据 id 获取。 还有根据 XPath、css 选择器等获取的方式):
      from selenium import webdriver
      browser = webdriver.Chrome()
      browser.get('https://www.taobao.com')
      input_first = browser.find_element_by_id('q')
      input_second = browser.find_element_by_xpath('//*[@id ="q"]')
      input_third = browser.find_element_by_css_selector('#q')
      print(input_first, input_second, input_third)
      browser.close()      
      这里使用 3 种方式获取输入框,分别根据 ID, CSS 选择器和 XPath 获取,它们返回的结果完全一致。
    • 列出所有获取单个节点的方法:
      • find_element_by_id
      • find_element_by_name
      • find_element_by_xpath
      • find_element_by_link_text
      • find_element_by_partial_link_text
      • find_element_by_tag_name
      • find_element_by_class_name
      • find_element_by_css_selector
    • Selenium 提供了通用方法 find_element (),需要传入两个参数:查找方式 By 和值。就是 find_element_by_id()这种方法的通用函数版本,如 find_element_by_id(id )等价 find_element(By.ID, id),得到的结果完全一致。代码:
      from selenium import webdriver
      from selenium.webdriver.common.by import By
      
      browser = webdriver.Chrome()
      browser.get('https://www.taobao.com')
      input_first = browser.find_element(By.ID, 'q')
      print(input_first)
      browser.close()      
      查找方式的功能与上面列举函数一致,参数更加灵活。
  2. 多个节点
    • 如果有多个节点,用 find_element()方法查找,就只能得到第一个节点。如果要查找所有满足条件的节点, 需要用 find_elements()方法。注意,这个方法名称中,element 多了一个s 
      • 查找淘宝侧边导航条的所有条目:
        from selenium import webdriver
        browser = webdriver.Chrome()
        browser.get('https://www.taobao.com')
        lis = browser.find_elements_by_css_selector('.service-bd li')
        print(lis)
        browser.close()      

        得到的内容变成了列表类型,列表中的每个节点都是 WebElement 类型。

        如果用 find_element()方法,只能获取匹配的第一个节点,结果是 WebElement类型。如果用 find_elements()方法,结果是列表类型,列表中的每个节点是 WebElement 类型。

        • 列出所有获取多个节点的方法:
        • find_elements_by_id
        • find_elements_by_name
        • find_elements_by_xpath
        • find_elements_by_link_text
        • find_elements_by_tag_name
        • find_elements_by_class_name
        • find_elements_by_css_selector
        也可以直接用 find_elements()方法来选择,可以这样写,结果是完全一致的:
        •  lis = browser.find_elements(By.CSS_SELECTOR, ’.service-bd li')  

节点互交

  • 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('iPhone')
    time.sleep(1)
    input.clear()
    input.send_keys('iPad')
    button = browser.find_element_by_class_name('btn-search')
    button.click()      
    首先驱动浏览器打开淘宝,用 find_element_by_id()方法获取输入框,再用 send_keys() 方法输入 iPhone 文字,等待秒后用 clear()方法清空输入框,再次调用 send_keys()方法输入 iPad 文字,之后再 find_element_by_class_name()方法获取搜索按钮,最后调用 click ()方法完成搜索动作。
  • 更多的操作参见官方文档交互动作介绍:http://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.remote.webelement。

动作链

  • 动作链 没有特定的执行对象,如:鼠标拖曳、键盘按键等。
  • 如:现在实现一个节点的拖曳操作,将某个节点从一处拖曳到另外一处,实现代码:
    from selenium import webdriver
    from selenium.webdriver import ActionChains
    
    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      
    打开网页中一个拖曳实例,依次选中要拖曳的节点和拖曳到的目标节点,接着声明 ActionChains 对象并将其赋值为 actions 变量,然后调用 actions 变量的 drag_and_drop()方法,再调 perform()方法执行动作。
  • 更多动作链操作 官方文档:http://selenium-python.readthedocs.io/api.html#moduleselenium.webdriver.common.action_chains

执行JavaScript

  • 某些操作,Selenium API 并没有提供。如,下拉进度条,它可以直接模拟运行 JavaScript,此时使用 execute_script()方法即可实现,(注意:execute_script):
    from selenium import webdriver
    
    browser = webdriver.Chrome()
    browser.get('https://www.zhihu.com/explore')
    browser.execute_script('window.scrollTo(0,document.body.scrollHeight)')
    browser.execute_script('alert("To Bottom")')      

获取节点信息

  • 通过 page_source 属性可以获取网页的源代码,接着使用解析库(如正则表式、Beautiful Soup、pyquery 等)提取信息。 
  • 获取属性
    • 可以使用 get_attribute ()方法来获取节点的属性,但是其前提是先选中这个节点,示例:
      from selenium import webdriver
      browser = webdriver.Chrome()
      url = 'https://zhihu.com/explore'
      browser.get(url)
      logo = browser.find_element_by_id('zh-top-link-logo')
      print(logo)
      print(logo.get_attribute('class'))      

      结果获取知乎的 logo 节点,最后打印出它的 class。

      通过 get_attribute()方法,然后传人想要获取的属性名,就可以得到它的值了

  • 获取文本值
    • 每个 WebElement 节点都有 text 属性,直接调用这个属性就可以得到节点内部的文本信息,相当于 Beautiful Soup 的 get_text()方法、 pyquery 的 text()方法。示例:
      from selenium import webdriver
      browser = webdriver.Chrome()
      url = 'https://www.zhihu.com/explore'
      browser.get(url)
      input = browser.find_element_by_class_name('zu-top-add-question')
      print(input.text)      
  • 获取id、位置、标签名和大小
    • Web Element 节点还有 些其他属性,如 id 属性可以获取节点 id,location 属性可以获取该节点在页面中的相对位置,tag_name 属性可以获取标签名称,size 属性可以获取节点的大小(宽高),示例:
      from selenium import webdriver
      browser =webdriver.Chrome()
      url = 'https://www.zhihu.com/explore'
      browser.get(url)
      input = browser.find_element_by_class_name('zu-top-add-question')
      print(input.text)
      print(input.id)
      print(input.location)
      print(input.size)
      print(input.tag_name)      
      首先获得“提问”按钮节点,再调用其 id、location、tag_name、size 属性来获取对应的属性值

切换Frame

  • 网页中有种节点叫作 iframe,也就是子 Frame,相当于页面的子页面,它的结构和外部网页的结构完全一致。Selenium 打开页面后,默认在父级 Frame 里面操作,而此时如果页面中还有子Frame,是不能获取到子 Frame 里面的节点的。这时就需要使用 switch_to.frame()方法来切换Frame。示例:
    import time
    from selenium import webdriver
    from selenium.common.exceptions import NoSuchElementException
    browser = 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)      

    以前面动作链操作的网页例,首先通过 switch_to.frame()方法切换到子 Frame 里,再尝试获取父级 Frame 里的 logo 节点(这是不能找到的),如果找不到的话,就会抛出 NoSuchEle entException 异常,异常被捕捉之后,就会输出 NO LOGO。接下来,重新切换父级 Frame, 然后再次重新获取节点,发现此时可以成功获取了。 

    当页面中包含子 Frame 时,如果想获取子 Frame 中的节点,需要先调用 switch_to.frame() 方法切换到对应的 Frame,再进行操作。  

延时等待

  • Selenium 中, get()方法会在网页框架加载结束后结束执行,此时如果获取 page_source ,可能并不是浏览器完全加载完成的页面,某些页面有额外的 Ajax 请求,在网页惊代码中也不一定能成功获取到。所以,这里需要延时等待一定时间,确保节点已经加载出来。
    • 延时等待方式有两种:隐式等待和显式等待
  • 隐式等待
    • 使用隐式等待执行测试的时候,如果 Selenium 没有在 DOM 中找到节点,将继续等待,超出设定时间后(默认时间为 0 ),则抛出找不到节点的异常。示例:
      from selenium import webdriver
      browser = webdriver.Chrome()
      browser.implicitly_wait(10)
      browser.get('https://www.zhihu.com/explore')
      input = browser.find_element_by_class_name('zu-top-add-question')
      print(input)      
      用 implicitly_wait()方法实现隐式等待。
  • 显式等待
    • 指定要查找的节点,然后指定一个最长等待时间。如果在规定时间内加载出来了这个节点,就返回查找的节点;如果到了规定时间依然没有加载出该节点, 则抛出超时异常。示例:
      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
      
      browser = 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)      

      首先引人 WebDriverWait 对象,指定最长等待时间,调用 until()方法,传人要等待条件 expected_conditions。如:传入 presence_of_element_located 条件,代表节点出出现意思,参数是节点的定位元组,也就是 ID 为 q 的节点搜索框。

      效果:在 10 秒内如果 ID 为 q的节点(搜索框)成功加载出来,就返回该节点;如果超过 10 秒还没有加载出来,就抛出异常。

      • 对于按钮,更改一下等待条件,如改为 element_to_be_clickable,也就是可点击,所以找按钮时查找 CSS 选择器为 .btn_search 的按钮,如果 10 秒内它是可点击的,也就是成功加载,返回这个按钮节点;如果超过 10 秒还不可点击,就抛出异常.
        • 等待条件,如判断标题内容,判断某个节点内是否出现了某文字。等待条件:
        • title_is:标题是某内容
        • title_contains: 标题包含某内容
        • presence_of_element_located:节点加载出来,传入定位元组,如(By.ID,‘q’)
        • visibility_of_element_located:节点课件,传入定位元组
        • visibility_of:可见,传入节点对象
        • presence_of_all_elements_ilocated:所有节点加载出来
        • text_to_be_present_in_element:某个节点文本包含某文字
        • text_to_be_present_in_element_value:某个节点值包含某文字
        • frame_to_be_available_and_switch_to_it:加载并切换
        • invisibility_of_element_located:节点不可见
        • element_to_be_clickable:节点可点击
        • staleness_of:判断一个节点是否仍在DOM,可判断页面是否已经刷新
        • element_to_be_selected:节点可选择,传节点对象
        • element_located_to_be_selected:节点可选择,传入定位元组
        • element_election_state_to_be:传入节点对象以及状态,相等返回True,否则返回False
        • element_located_selection_state_to_be:传入定位元组。以及状态,相等返回True,否则返回False
        • alert_is_present:是否出现警告
      • 更多等待条件的参数及用法,参考官方文档: http://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.support.expected_conditions。

前进和后退

  • Selenium 可以完成前进和后退操作,它使用 back()方法后退,使用 forward()方法前进。示例:
    import time
    from selenium import webdriver
    
    browser = 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()      
    连续访问3个页面,调用 back()方法回到第二个页面,再调用 forward()方法前进到第 3个页面。

Cookies

  • Selenium 可以方便地对 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())
          
    访问知乎,加载完成后,浏览器已经生成 Cookies。调用 get_cookies() 方法获取所有的 Cookies。然后,添加一个 Cookie,传入一个字典,有name、domain、value内容。再次获取所有 Cookies。结果就多了新加的 Cookie。调用 delete_all_cookies()方法删除所有 Cookies。再重新获取,结果就为空了。

选项卡管理

  • 访问网页时候,会开启一个个选项卡。Selenium 中,也可以对选项卡进行操作。示例:
    import time
    from selenium import webdriver
    
    browser = 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.taobap.com')
    time.sleep(1)
    browser.switch_to_window(browser.window_handles[0])
    browser.get('https://python.org')      
    首先访问百度,然后调用 execute_script()方法,传入 window.open()这个 JavaScript 语句新开启一个选项卡。接下来,想切换到该选项卡,调用 window_handles 属性获取当前开启的所有选项卡,返回的是选项卡的代号列表。要想切换选项卡,只需要调用 switch_to_window()方法即可,其中参数是选项卡的代号。将第二个选项卡代号传人,即跳转到第二个选项卡,接下来在第二个选项卡下打开一个新页面,然后切换回第一个选项卡重新调用 switch_to_window()方法,再执行其他操作。

异常处理

  • 在使用 Selenium 过程中,可以使用 try except 语句来捕获各种异常。(模拟节点未找到异常)示例:
    from selenium import webdriver
    browser = webdriver.Chrome()
    browser.get('https://www.baidu.com')
    browser.find_element_by_id('hello')      
    首先打开百度页面,尝试选择一个并不存在的节点 ,此时就会遇到异常。捕获异常示例:
    from selenium import webdriver
    from selenium.common.exceptions import TimeoutException, NoSuchElementException
    
    browser = webdriver.Chrome()
    try:
        browser.get('https://www.baidu.com')
    except TimeoutException:
        print('Timeout')
    try:
        browser.find_element_by_id('hello')
    except NoSuchElementException:
        print('No Element')
    finally:
        browser.close()      
    这里使用 try except 来捕获各类异常。如,对 find_element_by_id()查找节点方法捕获 NoSuchElementException 异常,一旦出现这样的错误,就进行异常处理,程序也不会中断。
  • 更多异常类,参考官方文档 http://selenium-python.readthedocs.io/api.html#module-selenium.common.exceptions。

转载于:https://www.cnblogs.com/Mack-Yang/p/10184662.html