美文网首页Python
65.1-动态网页处理-PhantomJS和Selenium简单

65.1-动态网页处理-PhantomJS和Selenium简单

作者: BeautifulSoulpy | 来源:发表于2020-09-03 12:59 被阅读0次
    要学会新东西,要不断进步,就必须放低自己的姿势!

    总结:

    1. 显示等待: 满足条件后 ,才会处理网页;
      隐示等待: 设置一个等待时间;
    2. 注意: 搜索框 是元组
      element = WebDriverWait(driver, 10).until(
      EC.presence_of_element_located( # 元素加载到DOM,但是不保证他是可见的;
      (By.ID, "inp-query") # 找搜索框 是 元组
      )
    3. 显式等待比隐式等待更节约执行时间,因此更推荐使用显式等待方式判断页面元素是否存在

    动态网页处理

    很多网站都采用AJAX技术、SPA技术,部分内容都是异步动态加载的。可以提高用户体验,减少不必要的流量,方便CDN加速等。(大部分的网页,尤其是手机)

    但是,对于爬虫程序爬取到的HTML页面相当于页面模板了,动态内容不在其中。
    解决办法之一,如果能构造一个包含JS引擎的浏览器,让它加载网页并和网站交互,我们编程从这个浏览器获取内容包括动态内容。这个浏览器不需要和用户交互的界面,只要能支持HTTP、HTTPS协议和服务器端交互,能解析HTML、CSS、JS就行了

    1. PhantomJS

    它是一个headless无头浏览器,支持Javascript。可以运行在Windows、Linux、Mac OS等。
    所谓无头浏览器,就是包含Js引擎、浏览器排版引擎等核心组件,但是没有和用户交互的界面的浏览器。

    官网 http://phantomjs.org/
    官方文档 http://phantomjs.org/
    下载 http://phantomjs.org/
    下载对应操作系统的Phantomjs,解压缩就可以使用 ;

    测试:
    确定是否安装成功;

    // test.js
    "use strict";
    console.log('hello world');
    phantom.exit();
    
    $ phantomjs-2.1.1-windows\bin\phantomjs.exe test.js
    hello world   # 成功运行;
    
    # 自带exmple试运行;
    $ phantomjs-2.1.1-windows\bin\phantomjs.exe phantomjs-2.1.1-windows\examples\hello.js
    Hello, world!
    
    $ phantomjs-2.1.1-windows\bin\phantomjs.exe phantomjs-2.1.1-windows\examples\version.js
    using PhantomJS version 2.1.1
    

    2. Selenium

    它是一个WEB自动化测试工具。它可以直接运行在浏览器中,支持主流的浏览器,包括PhantomJS(无界面浏览器)。 动作测试:

    官网 https://www.seleniumhq.org/

    安装
    $ pip install selenium
    

    WebDriver 可以看一下


    3. 开发实战

    不同浏览器都会提供操作的接口,Selenium就是使用这些接口来操作浏览器

    Selenium最核心的对象就是webdriver,通过它就可以操作浏览器、截图、HTTP访问、解析HTML等。

    
    from selenium.webdriver import PhantomJS
    
    driver = PhantomJS('F:/BaiduNetdiskDownload/2019 Python网络期班配套资料/slides/chapter16爬虫/install/phantomjs-2.1.1-windows/bin/phantomjs.exe')
    driver.set_window_size(1280,1080)
    driver.get('https://www.baidu.com/?tn=78000241_5_hao_pg')
    
    driver.save_screenshot('F:/test/screen-baidu.png')
    #----------------------------------------------------------
    # 一般还是使用无头浏览器,忽略警告;
    UserWarning: Selenium support for PhantomJS has been deprecated, please use headless versions of Chrome or Firefox instead
      warnings.warn('Selenium support for PhantomJS has been deprecated, please use headless '  
    
    
    1. 处理异步请求

    bing的查询结果是通过异步请求返回结果,所以,直接访问页面不能直接获取到搜索结果。


    . 定位页面中的内容是否返回成功

    from urllib import parse
    from selenium.webdriver import PhantomJS
    import datetime, time, random
    
    # #如果没有在环境变量指定PhantomJS位置
    driver = PhantomJS('F:/BaiduNetdiskDownload/2019 Python网络期班配套资料/slides/chapter16爬虫/install/phantomjs-2.1.1-windows/bin/phantomjs.exe')
    driver.set_window_size(1280,1080)
    
    # 保存图片;
    def savepic():
        basepath = 'F:/test/'
        filename = "{:%Y%m%d%H%M%S}-{:03}.png".format(datetime.datetime.now(), random.randint(1,100))
        driver.save_screenshot(basepath+filename)
        # 生成页面快照并保存
    
    # 模拟浏览器输入网址;
    url = 'https://cn.bing.com/search?q=%E9%A9%AC%E5%93%A5%E6%95%99%E8%82%B2'  #自动转的;
    # url = 'https://cn.bing.com/search?' + parse.urlencode({'q':'马哥教育'})   # parse.urlencode转字符;
    driver.get(url)  # get方法会一直等到页面加载,然后才会继续执行程序,通常测试会在这里选择time.sleep(2)
    
    MAXRETRIES = 5
    for i in range(MAXRETRIES):
        try:
            ele = driver.find_element_by_id('b_results')
            print(i, ele.is_displayed())   # 元素看不看得见;
            if not ele.is_displayed():
                time.sleep(1)
                continue
            savepic()
            # break  # 正常加break;   
        except Exception as e:
            print(type(e), e)
            print('--------------------')
    
    driver.quit()
    # ----------------------------------------
    0 False
    1 True      # 页面内容返回成功;
    2 True
    3 True
    4 True
    

    可能结果未必能看到,说明数据回来了,而且组织好了,但是没有显示出来。
    可以增加判断元素是否显示的代码,直到等待的数据呈现在页面上;

    2. 下拉框处理

    Selenium专门提供了Select类来处理网页中的下拉框(下拉一次,整个页面重新刷新一次,效率低下)

    不过下拉框用的页面越来越少了,本次使用 https://www.oschina.net/search?scope=project&q=python

    select 下拉框的处理
    select下拉框

    from disk cache或者from memory cache又是在什么情况下会出现呢?

    从字面意思不难理解,这都是浏览器的一种缓存机制。disk 是从硬盘中读取资源,而 memory 则是从内存中获取资源,两者的区别就是内存和硬盘的区别:memory 中的资源是临时的,当关闭或者刷新页面后就会丢失;而 disk 是存在硬盘上的,可以从文件夹中找到。

    那是不是 memory 中的资源等下载加载页面的时候又要从服务器获取呢?其实不然,memory 中的资源其实也同时会存在 disk 中,所以下一次加载,浏览器会优先从 disk 中检索



    重新刷新请求,说明 disk cache 是在服务器中重新下载的;

    表单控件select
    from urllib import parse
    from selenium.webdriver import PhantomJS
    from selenium.webdriver.support.ui import Select    # 在 webdriver.support.ui 下找 Select;
    
    import datetime, time, random
    
    # #如果没有在环境变量指定PhantomJS位置
    driver = PhantomJS('F:/BaiduNetdiskDownload/2019 Python网络期班配套资料/slides/chapter16爬虫/install/phantomjs-2.1.1-windows/bin/phantomjs.exe')
    driver.set_window_size(1280,1080)
    
    # 保存图片;
    def savepic():
        basepath = 'F:/test/'
        filename = "{:%Y%m%d%H%M%S}-{:03}.png".format(datetime.datetime.now(), random.randint(1,100))
        driver.save_screenshot(basepath+filename)
        # 生成页面快照并保存
    
    # 模拟浏览器输入网址;
    url = 'https://www.oschina.net/search?scope=project'
    # url = 'https://cn.bing.com/search?' + parse.urlencode({'q':'马哥教育'})   # parse.urlencode转字符;
    driver.get(url)  # get方法会一直等到页面加载,然后才会继续执行程序,通常测试会在这里选择time.sleep(2)
    print(1, driver.current_url)
    savepic()
    
    # 模拟select 框 怎么使用;
    sel = driver.find_element_by_name('tag1')
    ele = Select(sel)
    ele.select_by_value('309')
    # ele.select_by_index(1)
    
    print(2, driver.current_url)
    savepic()
    driver.quit()
    
    
    3. 模拟键盘操作(模拟登陆)

    web driver提供了一些列find方法, 用户获取一个网页中的元素。元素对象可以使用send_keys模拟键盘输入。

    oschina的登录页, 登录成功后会跳转到首页, 首页右上角会显示会员信息, 如果未登录, 无此信息。


    在浏览器中 模拟登录过程

    分别 有: 打开登录页截图;输入账户截图;登陆成功 截图,拿到cookies ,可以随时登录;

    from urllib import parse
    from selenium.webdriver import PhantomJS
    from selenium.webdriver.support.ui import Select
    from selenium.webdriver.common.keys import Keys
    
    import datetime, time, random
    
    # #如果没有在环境变量指定PhantomJS位置
    driver = PhantomJS('F:/BaiduNetdiskDownload/2019 Python网络期班配套资料/slides/chapter16爬虫/install/phantomjs-2.1.1-windows/bin/phantomjs.exe')
    driver.set_window_size(1280,1080)
    
    # 保存图片;
    def savepic():
        basepath = 'F:/test/'
        filename = "{:%Y%m%d%H%M%S}-{:03}.png".format(datetime.datetime.now(), random.randint(1,100))
        driver.save_screenshot(basepath+filename)
        # 生成页面快照并保存
    
    # 模拟浏览器登录网址;
    url = 'https://www.oschina.net/home/login'
    # url = 'https://cn.bing.com/search?' + parse.urlencode({'q':'马哥教育'})   # parse.urlencode转字符;
    driver.get(url)  # get方法会一直等到页面加载,然后才会继续执行程序,通常测试会在这里选择time.sleep(2)
    print(1, driver.current_url)
    savepic()   # 打开登录页截图;
    
    # 模拟select 框 怎么使用;
    username = driver.find_element_by_name('username')
    username.send_keys('')
    username = driver.find_element_by_name('password')
    username.send_keys('')
    # driver.find_element_by_class_name()
    
    savepic()  # 输入账户截图
    password.send_keys(Keys.ENTER)
    
    time.sleep(1)
    savepic()  # 登陆成功 截图  或者 加上一个验证是否有 退出按钮的 验证;
    print(2, driver.current_url)
    
    cookies = driver.get_cookies()  # 
    print(cookies,type(cookies))    # 会有key : oscpid
    
    driver.quit()
    #----------------------------------
    登录成功 会有 oscpid;
    
    
    
    获得cookies 登录后 爬取页面
    # 获得cookies 登录后 爬取页面
    import requests
    from requests.cookies import RequestsCookieJar
    from fake_useragent import UserAgent
    
    headers = {'User-agent':UserAgent().random}
    
    # no cookies
    response = requests.request('GET', url, headers=headers)
    text = response.text
    print(response.url)  # url中有 login 是未登录状态;
    
    print('==========================================')
    # cookies   # 凑cookies 字典;
    jar = RequestsCookieJar()
    for cookies in cookies:
        jar.set(cookies.get('name'), cookies.get=('value'))  # 凑cookies 字典;
    
    response = requests.request('GET', url, headers=headers, cookies=jar)
    text = response.text
    print(response.url)   # url中没有 login登陆成功
    
    # 保存登录后的html
    with open('./cnblog-html.html','w' , encoding='UTF-8') as f:
        f.write(text)
    
    

    验证码的问题 不是重点;要看AI模型的成功率;

    4. 页面等待技术

    越来越多的页面使用Ajax这样的异步加载技术,这就会导致代码中要访问的页面元素,还没有被加载就被访问了(转圈),抛出异常。

    方法1 线程休眠

    使用time.sleep(n) 来等待数据加载。
    配合循环一直等到数据被加载完成,可以解决很多页面动态加载或加载慢的问题。当然可以设置一个最大重试次数,以免一直循环下去。参看本文"处理异步请求”

    方法2 Selenium等待

    Selenium的等待分为:显示等待 和 隐式等待
    隐式等待,等待特定的时间
    显式等待,指定一个条件,一直等到这个条件成立后继续执行,也可以设置超时时间,超时会抛异常

    参考:https://www.selenium.dev/documentation/en/

    # 官网案例:
    from selenium import webdriver
    from selenium.webdriver.common.by import By
    from selenium.webdriver.common.keys import Keys
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support.expected_conditions import presence_of_element_located
    
    #This example requires Selenium WebDriver 3.13 or newer
    with webdriver.Firefox() as driver:
        wait = WebDriverWait(driver, 10)
        driver.get("https://google.com/ncr")
        driver.find_element(By.NAME, "q").send_keys("cheese" + Keys.RETURN)
        first_result = wait.until(presence_of_element_located(By.CSS_SELECTOR, "h3>div"))
        print(first_result.get_attribute("textContent"))
      
    
    #-------------------------------------------------------------------------------------------
    from selenium import webdriver
    from selenium.webdriver.common.by import By
    from selenium.webdriver.common.keys import Keys
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support.expected_conditions import EC
    
    import datetime, time, random
    
    # #如果没有在环境变量指定PhantomJS位置
    driver = PhantomJS('F:/BaiduNetdiskDownload/2019 Python网络期班配套资料/slides/chapter16爬虫/install/phantomjs-2.1.1-windows/bin/phantomjs.exe')
    driver.set_window_size(1280,1080)
    
    
    # 保存图片;
    def savepic():
        basepath = 'F:/test/'
        filename = "{:%Y%m%d%H%M%S}-{:03}.png".format(datetime.datetime.now(), random.randint(1,100))
        driver.save_screenshot(basepath+filename)
        # 生成页面快照并保存
    
    url = ""
    driver.get(url)
    driver.find_element(By.ID, 'inp-qury')
    
    try:
        element = WebDriverWait(driver, 10) .until(
            EC.presence_of_element_locate(  # 元素加载到DOM,但是不保证他是可见的;
                (By.ID, "myDynamicElement")
            )
        )
    finally:
        driver.quit()
    
    

    表单

    expected_conditionsn内置条件 说明
    title_is 判断当前页面的title是否精确等于预期
    title_contains 判断当前页面的title是否包含预期字符串
    presence_of_element_located 判断某个元素是否被加到了dom树里,并不代表该元素一定可见
    visibility_of_element_located 判断某个元素是否可见。可见代表元素非隐藏,并且元素的宽和高都不等于0
    visibility_of 跟上面的方法做一样的事情,只是上面的方法要传入locator,这个方法直接传定位到的element就好了
    presence_of_all_elements_located 判断是否至少有1个元素存在于dom树中。举个例子,如果页面上有n个元素的class都是'column-md-3',那么只要有1个元素存在,这个方法就返回True
    text_to_be_present_in_element 判断某个元素中的text是否包含了预期的字符串
    text_to_be_present_in_element_value 判断某个元素中的value属性是否包含了预期的字符串
    frame_to_be_available_and_switch_to_it 判断该frame是否可以switch进去, 如果可以的话, 返回True 且switch进去, 否则返回False
    invisibility_of_element_located 判断某个元素中是否不存在于dom树或不可见
    element_to_be_clickable 判断某个元素中是否可见并且是enable的, 这样的话才叫clickable
    staleness_of 等某个元素从dom树中移除, 注意, 这个方法也是返回True或 False
    element_to_be_selected 判断某个元素是否被选中了,一般用在下拉列表
    element_selection_state_to_be 判断某个元素的选中状态是否符合预期
    element_located_selection_state_to_be 跟上面的方法作用一样,只是上面的方法传入定位到的element, 而这个方法传入locator
    alert_is_present 判断页面上是否存在alert

    显示等待
    结合WebDriverWait和expected_conditions判断元素是否存在,
    每间隔1秒判断一次,10s超时,存在返回True,不存返回False

    WebDriverWait类提供方法:
    (1)until(method, message='')
      在规定时间内,每隔一段时间调用一下method方法,至到期返回值不为False,如果超时抛出带有message的TimeoutException异常信息

    (1)until_not(method, message='')
      与until()方法相反,表示在规定时间内,每隔一段时间调用一下method方法,至到期返回值为False,如果超时抛出带有message的TimeoutException异常信息
    :param locator: locator为元组类型,如("id", "kw")

    定位搜索框,搜索电影;


    url中 要调取的框子
    from selenium import webdriver  
    from selenium.webdriver import PhantomJS  # 使用无头浏览器
    from selenium.webdriver.common.by import By   # 键盘操作
    from selenium.webdriver.common.keys import Keys  # 负责操作界面;
    from selenium.webdriver.support.ui import WebDriverWait  # 负责循环等待
    from selenium.webdriver.support import expected_conditions as EC  # 负责条件触发;
    
    import datetime, time, random
    
    # #如果没有在环境变量指定PhantomJS位置
    driver = PhantomJS('F:/BaiduNetdiskDownload/2019 Python网络期班配套资料/slides/chapter16爬虫/install/phantomjs-2.1.1-windows/bin/phantomjs.exe')
    driver.set_window_size(1280,1080)
    
    # 保存图片;
    def savepic():
        basepath = 'F:/test/'
        filename = "{:%Y%m%d%H%M%S}-{:03}.png".format(datetime.datetime.now(), random.randint(1,100))
        driver.save_screenshot(basepath+filename)
        # 生成页面快照并保存
    
    url = "https://movie.douban.com/"
    driver.get(url)
    # element = driver.find_element(By.ID, 'inp-qury')  # 找到搜索框 1;
    
    # 在10s内, 定位元素inp-query , 元素加载到DOM,但是不保证他是可见的;
    try:
        element = WebDriverWait(driver, 10).until(
            EC.presence_of_element_located(  # 元素加载到DOM,但是不保证他是可见的;
                (By.ID, "inp-query")   # 找搜索框 是 元组
            )
        )
        element.send_keys('TRON')
        element.send_keys(Keys.ENTER)
        print(driver.current_url)
        savepic()
    
    except Exception as e:
        print(type(e), e)
    finally:
        driver.quit()
    #----------------------------------------------------
    C:\ProgramData\Miniconda3\envs\blog\lib\site-packages\selenium\webdriver\phantomjs\webdriver.py:49: UserWarning: Selenium support for PhantomJS has been deprecated, please use headless versions of Chrome or Firefox instead
      warnings.warn('Selenium support for PhantomJS has been deprecated, please use headless '
    https://search.douban.com/movie/subject_search?search_text=TRON&cat=1002  #搜索网页;
    

    隐式等待
    找不到这个元素,继续等10s,还是找不到元素,结束程序;

    from selenium import webdriver
    from selenium.webdriver import PhantomJS  # 使用无头浏览器
    from selenium.webdriver.common.by import By   # 键盘操作
    from selenium.webdriver.common.keys import Keys  # 负责操作界面;
    from selenium.webdriver.support.ui import WebDriverWait  # 负责循环等待
    from selenium.webdriver.support import expected_conditions as EC  # 负责条件触发;
    
    import datetime, time, random
    
    # #如果没有在环境变量指定PhantomJS位置
    driver = PhantomJS('F:/BaiduNetdiskDownload/2019 Python网络期班配套资料/slides/chapter16爬虫/install/phantomjs-2.1.1-windows/bin/phantomjs.exe')
    driver.implicitly_wait(10) # 拿到driver等10s
    driver.set_window_size(1280,1080)
    
    # 保存图片;
    def savepic():
        basepath = 'F:/test/'
        filename = "{:%Y%m%d%H%M%S}-{:03}.png".format(datetime.datetime.now(), random.randint(1,100))
        driver.save_screenshot(basepath+filename)
        # 生成页面快照并保存
    
    url = "https://movie.douban.com/"
    driver.get(url)
    
    
    # 等 10s, 元素加载到DOM树,找id=inp-query
    try:
        element = driver.find_element(By.ID, 'inp-query')  # 找到搜索框 1;
        # element = WebDriverWait(driver, 1).until(
        #     EC.presence_of_element_located(  # 元素加载到DOM,但是不保证他是可见的;
        #         (By.ID, "inp-query")   # 找搜索框 是 元组
        #     )
        # )
        element.send_keys('TRON')
        element.send_keys(Keys.ENTER)
        print(driver.current_url) # 新的url;
        savepic()
    
    except Exception as e:
        print(type(e), e)
    finally:
        driver.quit()
    #----------------------------------
    https://search.douban.com/movie/subject_search?search_text=TRON&cat=1002
    
    
    <class 'selenium.common.exceptions.NoSuchElementException'> Message: {"errorMessage":"Unable to find element with id 'inp-qu1ery'","request":{"headers":{"Accept":"application/json","Accept-Encoding":"identity","Content-Length":"91","Content-Type":"application/json;charset=UTF-8","Host":"127.0.0.1:4483","User-Agent":"selenium/3.141.0 (python windows)"},"httpVersion":"1.1","method":"POST","post":"{\"using\": \"id\", \"value\": \"inp-qu1ery\", \"sessionId\": \"bb714600-ed9e-11ea-bb6d-6b286a00b9a6\"}","url":"/element","urlParsed":{"anchor":"","query":"","file":"element","directory":"/","path":"/element","relative":"/element","port":"","host":"","password":"","user":"","userInfo":"","authority":"","protocol":"","source":"/element","queryKey":{},"chunks":["element"]},"urlOriginal":"/session/bb714600-ed9e-11ea-bb6d-6b286a00b9a6/element"}}
    Screenshot: available via screen
    
    总结

    Selenium的Web Driver是其核心, 从Selenium 2开始就是最重要的编程核心对象, 在Selenium 3中更是如此。

    和浏览器交互全靠它,它可以:

    • 打开URL, 可以跟踪跳转, 可以返回当前页面的实际URL
    • 获取页面的title
    • 处理cookie
    • 控制浏览器的操作,例如前进、后退、刷新、关闭,最大化等
    • 执行JS脚本
    • 在DOM中搜索页面元素Web Element, 指定的或一批, find系方法
    • 操作网页元素
      • 模拟下拉框操作Select(element)
      • 在元素上模拟鼠标操作click()
      • 在元素上模拟键盘输入send_keys()
      • 获取元素文字text
      • 获取元素的属性get_attribute()

    Selenium通过Web Driver来驱动浏览器工作, 而浏览器是一个个独立的浏览器进程。

    相关文章

      网友评论

        本文标题:65.1-动态网页处理-PhantomJS和Selenium简单

        本文链接:https://www.haomeiwen.com/subject/awdisktx.html