• 第七部分(一) 动态渲染页面爬取(Selenium的使用)



    Ajax分析和抓取方式,是JavaScript动态渲染页面的一种情形,可使用 requests 或 urllib 爬取数据。JavaScript动态渲染的页面不是只有Ajax一种,比如中国青年网 http://news.youth.cn/gn/ 的分页部分由JavaScript生成的,不是原始的HTML代码,但是不包含Ajax请求。又比如ECharts的官方实例 http://echarts.baidu.com/demo.html#bar-negative ,其图形都是经过JavaScript计算后生成的。另外的淘宝页面,有Ajax获取的数据,但是Ajax接口含有很多加密参数,不容易找出规律,很难直接分析Ajax来获取。

    这些问题可以通过使用模拟浏览器运行的方式来实现,这样在浏览器中看到什么样,抓取的源码就是什么样,也就是可见即可爬。不用管网页内部的JavaScript用的什么算法渲染页面,也不用管网页后台的Ajax接口到底有哪些参数。

    Python有许多模拟浏览器运行的库,如Selenium、Splash、PyV8、Ghost等。下面了解下Selenium和Splash的用法,以应对动态渲染的页面。

    一、 Selenium的使用

    Selenium是自动化测试工具,它可以驱动浏览器执行特定的动作,如点击、下拉等操作,同时还可以获取浏览器当前呈现的页面的源代码,做到可见即可爬。对于JavaScript动态渲染的页面,这种抓取方法非常有效。

    下面以Chrome为例说明Selenium的用法。首先要正确安装Chrome浏览器并配置好ChromeDriver。还要安装好Python的Selenium库。

    1、 开始使用
    首先看下Selenium大致有哪些功能。例如下面代码所示:

    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和网页源代码。Cookies以字典列表形式输出。

    这就是使用Selenium驱动浏览器加载网页拿到JavaScript渲染的结果,不必担心是什么加密系统。

    2、 声明浏览器对象
    Selenium支持的浏览器非常多,如Chrome、Firefox、Edge等,还有Android、BlackBerry等手机端浏览器。还支持无界面浏览器PhangtomJS。可用下面这些方式初始这些浏览器对象:

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


    这就是在初始化浏览对象并将其赋值为browser对象。接下来可以调用browser对象,让其执行各个动作以模拟浏览器操作。

    3、 访问页面
    使用前面创建的浏览器对象的 get() 方法,参数是要访问的链接URL。例如访问淘宝首页并输出源代码,示例如下:

    from selenium import webdriver

    chrome_options = webdriver.ChromeOptions()

    chrome_options.add_argument('--headless')       # 无界面模式
    browser = webdriver.Chrome(chrome_options=chrome_options)
    browser.get('https://www.taobao.com')
    print(browser.current_url)
    print(browser.page_source)
    browser.close()

    运行代码后不弹出Chrome浏览器并自动访问淘宝,然后在控制台输出淘宝的网址和页面的源代码,随后关闭浏览器。这几行简单的代码就实现了浏览器的驱动并获取网页源代码,非常便捷。

    4、 查找节点
    Selenium还可以驱动浏览器完成各种操作,如填充表单、模拟点击等。比如要完成向某个输入框输入文字的操作,首先要找到输入框。Selenium有提供一系列查找节点的方法,可用这些方法获取想要的节点,以便下一步执行一些动作或者提取信息。

    4.1、 提取单个节点
    例如要提取淘宝页面中的搜索框节点,需要先观察它的源代码。如图1-1所示。

    图1-1  源代码

                                               图1-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_css_selector('#q')
    input_third = browser.find_element_by_xpath('//*[@id="q"]')
    print(input_first, input_second, input_third)
    browser.close()

    这里用了3种方式获取输入框,分别是根据ID、CSS选择器和XPath获取,3种方式返回的结果完全一致,并且都是WebElement类型。输出如下所示:
    <selenium.webdriver.remote.webelement.WebElement (session="5c88916914b54ea71fd04dc64adf2bc1", element="0.056290961173190324-1")>
    <selenium.webdriver.remote.webelement.WebElement (session="5c88916914b54ea71fd04dc64adf2bc1", element="0.056290961173190324-1")>
    <selenium.webdriver.remote.webelement.WebElement (session="5c88916914b54ea71fd04dc64adf2bc1", element="0.056290961173190324-1")>

    获取单个节点的方法有下面这些:
    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()

    这种查找方式的参数更灵活,功能与前面列举的是一样的。

    4.2、 多个节点
    find_element() 方法只能查找单个节点,就算有多个节点,也只能得到第一个节点。节点类型是:WebElement。
    find_elements() 方法可以查找所有满足条件的节点。结果是列表类型,每个节点类型是:WebElement

    例如查找淘宝左侧导航条的所有条目,通过源代码分析可知,每一个导航条都是用 li 标签包起来的,这些导航条都有一个共同的父标签
    ul,ul标签有class属性,其属性值是service-bd。可先根据class属性值找到ul标签,继而找到下面的子标签即可找到左侧导航条
    的所有节点。代码如下:

    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()

    输出如下所示:
    [<selenium.webdriver.remote.webelement.WebElement (session="4b1d6b1ad2469591d0fa83017d2aa992", element="0.7683288407073237-1")>,
    <selenium.webdriver.remote.webelement.WebElement (session="4b1d6b1ad2469591d0fa83017d2aa992", element="0.7683288407073237-2")>,
    <selenium.webdriver.remote.webelement.WebElement (session="4b1d6b1ad2469591d0fa83017d2aa992", element="0.7683288407073237-3")>,
    <selenium.webdriver.remote.webelement.WebElement (session="4b1d6b1ad2469591d0fa83017d2aa992", element="0.7683288407073237-4")>,
    <selenium.webdriver.remote.webelement.WebElement (session="4b1d6b1ad2469591d0fa83017d2aa992", element="0.7683288407073237-5")>,
    <selenium.webdriver.remote.webelement.WebElement (session="4b1d6b1ad2469591d0fa83017d2aa992", element="0.7683288407073237-6")>,
    <selenium.webdriver.remote.webelement.WebElement (session="4b1d6b1ad2469591d0fa83017d2aa992", element="0.7683288407073237-7")>,
    <selenium.webdriver.remote.webelement.WebElement (session="4b1d6b1ad2469591d0fa83017d2aa992", element="0.7683288407073237-8")>,
    <selenium.webdriver.remote.webelement.WebElement (session="4b1d6b1ad2469591d0fa83017d2aa992", element="0.7683288407073237-9")>,
    <selenium.webdriver.remote.webelement.WebElement (session="4b1d6b1ad2469591d0fa83017d2aa992", element="0.7683288407073237-10")>,
    <selenium.webdriver.remote.webelement.WebElement (session="4b1d6b1ad2469591d0fa83017d2aa992", element="0.7683288407073237-11")>,
    <selenium.webdriver.remote.webelement.WebElement (session="4b1d6b1ad2469591d0fa83017d2aa992", element="0.7683288407073237-12")>,
    <selenium.webdriver.remote.webelement.WebElement (session="4b1d6b1ad2469591d0fa83017d2aa992", element="0.7683288407073237-13")>,
    <selenium.webdriver.remote.webelement.WebElement (session="4b1d6b1ad2469591d0fa83017d2aa992", element="0.7683288407073237-14")>,
    <selenium.webdriver.remote.webelement.WebElement (session="4b1d6b1ad2469591d0fa83017d2aa992", element="0.7683288407073237-15")>,
    <selenium.webdriver.remote.webelement.WebElement (session="4b1d6b1ad2469591d0fa83017d2aa992", element="0.7683288407073237-16")>]

    输出结果是列表类型,列表中的每个节点都是WebElement类型。获取多个节点的所有方法如下:
    find_elements_by_id
    find_elements_by_name
    find_elements_by_xpath
    find_elements_by_link_text
    find_elements_by_partial_link_text
    find_elements_by_tag_name
    find_elements_by_class_name
    find_elements_by_css_selector
    通用方法:find_elements()


    使用通用方法find_elements()方法选择时,可这样写:
    lis = browser.find_elements(By.CSS_SELECTOR, '.service-bd li')

    5、 节点交互
    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("Mate20")                         # 输入Mate20
    time.sleep(1)
    input.clear()                                                # 等待1秒后清空输入框
    input.send_keys("P20")                              # 重新输入P20
    button = browser.find_element_by_class_name('btn-search')   # 获取搜索按钮
    button.click()     # 点击搜索

    上面代码执行过程:首先驱动浏览器打开淘宝网站,然后用find_element_by_id()方法获取输入框,接着用send_keys()方法输入Mate20文字,等待1秒后用clear()方法清空输入框,再次调用send_keys()方法输入P20,之后再用find_element_by_class_name()方法获取搜索按钮,最后调用click()方法完成搜索动作。

    官方文档的交互动作介绍:
    http://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.remote.webelement

    6、 动作链
    前面的交互动作是针对某个节点执行的。例如,对于输入框,可调用它的输入文字和清空文字方法;对于按钮,可调用它的点击方法。有一些操作,它们没有特定的执行对象,比如鼠标拖曳、键盘按键等,这些动作用另一种方式来执行,就是动作链

    例如要实现一个节点的拖曳操作,将某个节点从一处拖曳到另一处,可像下面这样实现:

    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#module-selenium.webdriver.common.action_chains

    7、 执行JavaScript
    某些操作,SeleniumAPI 没有提供。比如,下拉进度条操作就没有,它可以使用 execute_script() 方法直接模拟运行JavaScript来实现。示例如下:

    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")')

    代码中利用execute_script()方法将进度条下拉到最底部,然后弹出alert提示框。有了这个方法,基本上API没有提供的所有功能都可以用执行JavaScript的方式来实现。

    8、 获取节点信息
    page_source属性可获取网页源代码,解析库(有正则表达式、Beautiful Soup、pyquery等)用来提取信息。Selenium有提供节点
    选择方法,返回的是WebElement类型,对应也有相关的方法和属性直接提取节点信息,如属性、文本等。

    8.1、 获取属性
    get_attribute()方法获取节点属性,需要先选中节点,代码示例如下:

    from selenium import webdriver
    browser = webdriver.Chrome()
    url = 'https://www.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。输出信息如下所示:
    <selenium.webdriver.remote.webelement.WebElement (session="db40cefb1cf4ac278c6832791fe74b26", element="0.46332722296830897-1")>
    zu-top-link-logo

    这样通过get_attribute()方法传入属性名参数就可获取到属性值。

    8.2、 获取文本值
    每个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)                                #  输出:提问

    8.3、 获取id、位置、标签名和大小
    WebElement节点的其它属性如下:
    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.id)                                   # 获取节点id
    print(input.location)                         # 节点在页面中的相对位置
    print(input.tag_name)                      # 标签名称
    print(input.size)                                # 标签的宽高

    输出如下所示:
    0.46332722296830897-2
    {'x': 758, 'y': 7}
    button
    {'height': 32, 'width': 66}

    9、 切换Frame
    网页中有一种节点叫作 iframe,也是子Frame,相当于页面的子页面,子页面结构与外部网页结构完全一致。Selenium打开页面默认是在父级Frame里面操作,页面中如果有子Frame,它是不能获取到子Frame里面的节点。这时可使用switch_to.frame()方法可切换frame。示例如下:

    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') # 切换到子frame
    try:
    # 获取父级Frame的logo节点,不成功就抛出NoSuchElementException异常
    logo = browser.find_element_by_class_name('logo')
    except NoSuchElementException:
    print('NO LOGO')
    browser.switch_to.parent_frame() # 切换回父级Frame
    logo = browser.find_element_by_class_name('logo') # 获取logo节点
    print(logo)
    print(logo.text) # 输出父级logo节点的文本


    输出如下所示:
    NO LOGO
    <selenium.webdriver.remote.webelement.WebElement (session="6e2ef8fd8e5d576d31cf86557ad39b67", element="0.4057476934894335-2")>
    RUNOOB.COM

    代码中switch_to.frame()方法切换到子Frame,接着find_element_by_class_name('logo')获取父级Frame的logo节点,未能获取就抛出异常NoSuchElementException。切换回父级Frame,重新获取logo节点,可以成功获取。如果页面中有子Frame时,要获取子Frame的节点,要先调用switch_to.frame()方法切换到对应的Frame后再进行操作。

    10、 延时等待
    在Selenium中,get()方法在网页框架加载结束后结束执行,此时获取page_source,并不是浏览器完全加载完成的页面,如果有额外的Ajax请求,在网页源代码中也不一定能成功获取到。所以需要延时等待一定时间,确保节点已经加载出来。

    延时等待有两种方式:隐式等待;显式等待

    10.1、 隐式等待,implicitly_wait()
    使用隐式等待测试时,如果Selenium没有在DOM中找到节点,将继续等待,超出设定时间后,就抛出找不到节点的异常。也就是说,在查找节点时节点没有立即出现时,隐式等待将等待一段时间再查找DOM,默认等待时间是0。示例如下:

    from selenium import webdriver
    browser = webdriver.Chrome()
    browser.implicitly_wait(10)            # 调用隐式等待,等待10秒
    browser.get('https://www.zhihu.com/explore')
    input = browser.find_element_by_class_name('zu-top-add-question')
    print(input)

    10.2、 显式等待,WebDriverWait()
    隐式等待方式会受到网络条件影响,有的页面加载时间过长。显式等待是指定要查找的节点,并指定一个最长等待时间。如果在规定时间内加载出来了这个节点,就返回查找的节点;到了规定时间依然没有加载出该节点,则抛出异常。示例如下:

    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)       # 参数:等待对象及时长
    # 在等待时间内获取输入框节点,通过ID查找
    input = wait.until(EC.presence_of_element_located((By.ID, 'q')))
    # 在等待时间内获取点击按钮节点,通过CSS选择器查找
    button = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '.btn-search')))
    print(input, button)

    在代码中引入WebDriverWait对象,指定最长等待时间为10秒,接着调用它的 until() 方法,传入要等待条件 expected_conditions。这里传入了 presence_of_element_located 这个条件,表示节点出现的意思,其参数是节点的定位元组,也就是ID为q的节点搜索框。在10秒内ID为q的节点(即搜索框)成功加载,就返回该节点;如果超过10秒还没有加载出来,就抛出异常。

    按钮的等待条件是 element_to_be_clickable,也就是可点击。参数(By.CSS_SELECTOR, '.btn-search')意思是查找按钮时查找CSS选择器为 .btn-search 的按钮,如果10秒内它是可点击,就成功加载出来并返回这个按钮节点;如果10秒还不可点击,就是没有加载出来,则抛出异常。运行这段代码,在网速好的情况下可正常加载出来,并且输出如下:
    <selenium.webdriver.remote.webelement.WebElement (session="12fc5fa8bc80295340f5fd22433c6ec1", element="0.5824054028692756-1")>
    <selenium.webdriver.remote.webelement.WebElement (session="12fc5fa8bc80295340f5fd22433c6ec1", element="0.5824054028692756-2")>

    从输出可知,输出了两个节点,都是WebElement类型。如果网络有问题就抛出异常。在这段代码用到了两个等待条件,这些等待条件还
    有很多,比如判断标题内容,判断某个节点内是否出现某文字等。表1-1是所有的等待条件。

    表1-1 等待条件及其含义
        image

    更多等待条件参数及用法,参考官方文档:
    http://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.support.expected_conditions

    11、 前进和后退
    在使用浏览器时有前进和后退功能,Selenium 也可完成这个操作。使用 back() 方法后退,使用 forward() 方法前进

    import time
    from selenium import webdriver
    browser = webdriver.Chrome()
    browser.get('https://www.taobao.com/')     # 连续访问3个页面
    browser.get('https://www.baidu.com/')
    browser.get('https://www.sina.com.cn/')
    browser.back()                                            # 后退到百度页面
    time.sleep(1)
    browser.forward()                                       # 前进到sina页面
    time.sleep(3)
    browser.close()

    这段代码连续访问3个页面后调用back()方法回到第二个页面,接下来调用forward()方法又前进到第三个页面。

    12、 对Cookies的操作
    对Cookies进行操作方法主要有:获取、添加、删除等。
    get_cookies()方法:获取所有Cookies。
    add_cookie(字典参数):添加cookie,参数是字典。
    delete_all_cookies():删除所有的cookies。

    示例如下:

    from selenium import webdriver
    browser = webdriver.Chrome()
    browser.get('https://www.zhihu.com/explore')
    print("第一次cookies:", browser.get_cookies())     # 获取cookies,接着下面添加cookies
    browser.add_cookie({'name': 'name', 'domain': 'www.zhihu.com', 'value': 'michael'})
    print("第二次cookies:", browser.get_cookies())     # 再次获取cookies,核实是否添加成功
    browser.delete_all_cookies()                                   # 删除所有cookies
    print("第三次cookies:", browser.get_cookies())     # 核实是否完全删除cookies
    browser.close()

    输出如下,第二次输出的cookies包含了添加的cookie:
    第一次cookies: [{'domain': '.zhihu.com', 'httpOnly': False, ...}, ......]
    第二次cookies: [{'domain': '.zhihu.com', 'httpOnly': False, ...}, ......, {'domain': 'www.zhihu.com', 'name': 'name', 'value': 'michael'}]
    第三次cookies: []

    13、 Selenium模拟开启选项卡
    比如第一个选项卡打开百度网页,第二个选项卡打开淘宝网页。这些操作也可用Selenium来对选项卡进行操作。
    window.open()是JavaScript语句的开启一个选项卡。
    execute_script('window.open()') 执行JavaScript语句开启一个选项卡。
    window_handles获取当前开启的选项卡,结果是选项卡代号列表。window_handles[0]是指选项卡列表中的第1个选项卡。
    switch_to.window(选项卡参数):切换到选项卡参数指定的选项卡。
    代码示例如下:

    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])      # 切换到新选项卡,也是第2个选项卡
    browser.get('https://www.taobao.com')       # 在第2个选项卡中打开淘宝页面
    time.sleep(1)
    browser.switch_to.window(browser.window_handles[0])    # 切换到第1个选项卡
    browser.get('https://www.sina.com.cn')        # 在第1个选项卡打开新浪页面
    browser.close()                                              # 关闭当前选项卡,也是第1个选项卡

    输出如下所示:
    ['CDwindow-AAC4839C9E18D601645AC4D868050F5D', 'CDwindow-37138AB8D7A2E7501E48014FB506977C']

    14、 异常处理
    使用Selenium的时候,可能会遇到访问超时异常、节点未找到异常等情况。出现异常程序就中断运行。为了避免程序中断执行,可使用try except语句捕获各种异常。

    使用Selenium时,常遇到的异常是:TimeoutException(超时异常),NoSuchElementException(节点未找到异常),此外还可用
    WebDriverException异常捕获所有由Selenium产生的异常。异常模块所在位置是:selenium.common.exceptions


    导入WebDriverException的命令:
    from selenium.common.exceptions import WebDriverException

    Selenium的异常类官方文档参考:
    http://selenium-python.readthedocs.io/api.html#module-selenium.common-exceptions

  • 相关阅读:
    解决The current branch is not configured for pull No value for key branch.master.merge found in confi
    使用Eclipse构建Maven项目 (step-by-step)
    ecpilise引入Maven项目目录不正常,无JRE,无Maven Dependencies
    解决Eclipse建立Maven项目后无法建立src/main/java资源文件夹的办法
    Could not resolve archetype org.apache.maven.archetypes:maven-archetype-webapp:RELEASE from any of the configured repositories.
    (转) launch failed.Binary not found in Linux/Ubuntu解决方案
    .c_str()/atoi()/
    (转)Should I use char** argv or char* argv[]
    (转) int argc, char* argv[] 的用法
    (转) 制作 Clonezilla live 启动盘
  • 原文地址:https://www.cnblogs.com/Micro0623/p/10641989.html
Copyright © 2020-2023  润新知