本节讲解 DrissionPage 的一些基本概念。了解它大概的构成和运作原理。 默认读者对网络协议、html、网页自动化、requests 等有基本认识。 # ✔️ 网页自动化 网页自动化的形式通常有两种,它们各有优劣: - 直接向服务器发送数据包,获取需要的数据 - 控制浏览器跟网页进行交互 前者轻量级,速度快,便于多线程、分布式部署,如 requests 库。但当数据包机构复杂,甚至加入加密技术时,开发过程烧脑程度直线上升。 后者使用浏览器模拟人的行为,如 selenium。写起来简单得多,免去复杂的分析过程,但速度非常慢,占用资源巨大。 鉴于此,DrissionPage 以页面为单位将两者整合,对 chromium 协议 和 requests 进行了重新封装,实现两种模式的互通,并加入常用的页面和元素控制功能,可大幅降低开发难度和代码量。 用于操作浏览器的对象叫 Driver,requests 用于管理连接的对象叫 Session,Drission 就是它们两者的合体。 在旧版本,本库是通过对 selenium 和 requests 的重新封装实现的。 从 3.0 版开始,作者另起炉灶,用 chromium 协议自行实现了 selenium 全部功能,从而摆脱了对 selenium 的依赖,功能更多更强,运行效率更高,开发更灵活。 # ✔️ 工作模式 如上所述,DrissionPage 既可控制浏览器,也可用数据包方式访问网络数据。它有两种工作方式:d 模式和 s 模式。 页面对象可以在这两种模式间切换,两种模式拥有一致的使用方法,但任一时间只能处于其中一种模式。 ## 📍 d 模式 d 模式既表示 Driver,还有 Dynamic 的意思。 d 模式用于控制浏览器,不仅可以读取浏览器获取到的信息,还能对页面进行操作,如点击、填写、开关标签页、改变元素属性、执行 js 脚本等等。 d 模式功能强大,但运行速度受浏览器制约非常缓慢,而且需要占用大量内存。 ## 📍 s 模式 s 模式既表示 Session,还有 speed、silence 的意思。 s 模式的运行速度比 d 模式快几个数量级,但只能基于数据包进行读取或发送,不能对页面进行操作,不能运行 js。 爬取数据时,如网站数据包较为简单,应首选 s 模式。 ## 📍 模式切换 页面对象可以在 d 模式和 s 模式之间切换,这通常用于以下情况: - 当登录验证很严格,难以解构,如有验证码的时候,用浏览器处理登录,然后转换成 s 模式爬取数据。既避免了处理烧脑的 js,又能享受 s 模式的速度。 - 页面数据由 js 产生,且页面结构极其复杂,可以用 d 模式读取页面元素,然后把元素转成 s 模式的元素进行分析。可以极大地提高 d 模式的处理速度。 上述两种情况,第一种是整个页面对象进行模式转换: ```python page.change_mode() ``` 第二种是在 d 模式时仅对部分元素进行转换,如把表格元素转换为 s 模式的元素,在解析时速度比直接解析 d 模式元素快几个数量级。甚至可以把整个页面转换为 s 模式的元素,也是可以的。 ```python # 获取页面上一个 table 元素 ele = page.ele('tag:table') # 把该元素转换成 s 模式的元素 ele = ele.s_ele() # 获取所有行元素 rows = ele.eles('tag:tr') ``` # ✔️ 主要对象 ## 📍 `WebPage` 是从 3.0 版开始推出的页面页面控制对象,整合了两种模式,d 模式所有页面的跳转、读取、操作、标签页控,s 模式发送和解析数据都由该对象进行。 该对象是作者用 chromium devtools protocol 开发而成,不依赖 selenium 对浏览器进行控制。 ```python # 创建页面对象 page = WebPage('d') # 访问百度 page.get('http://www.baidu.com') # 定位输入框并输入关键字 page.ele('#kw').input('python') # 点击“百度一下”按钮 page.ele('@value=百度一下').click() ``` 详细使用方法见“创建页面对象”和“操作页面”章节。 ## 📍 `ChromiumElement` `ChromiumElemnet`对象是 `WebPage`在 d 模式所产生的页面元素对象,用于可对其进行点击、文本输入、拖拽、运行 js 脚本等操作,也可以基于这个元素查找其下级或周围的元素。 ```python # 点击一个元素 ele1.click() # 输入文本 ele1.input('some text') # 获取 class 属性 attr = ele1.attr('class') # 设置 style 属性 ele1.set_attr('style', 'display:none;') # 获取其子元素中所有 a 元素 links = ele1.eles('tag:a') ``` 详细使用方法见“获取页面元素”、“获取元素信息”和“操作页面元素”章节。 ## 📍 `SessionElement` `SessionElement`对象是 s 模式所产生的页面元素对象,可以读取元素信息,或基于它进行下级元素查找、相对定位其它元素,但不能执行点击等操作。 这种对象解析效率非常高,当 d 模式页面太复杂时,可把 d 模式的元素转换为`SessionElement`进行解析,提高速度。转换的同时可以执行下级元素的查找。 ```python # 获取元素 tag 属性 tag = ele1.tag # 在元素下查找第一个 name 为 name1 的子元素 ele2 = ele1.ele('@name=name1') # 假设 d_ele 是一个 ChromiumElement,使用其 SessionElement 版本进行子元素查找 ele2 = d_ele.s_ele('@name=name1') ``` ## 📍 `MixPage` `MixPage`对象使用方法与`WebPage`大致相同,不同的是它控制浏览器方面的功能是通过封装 selenium 实现的。作者以后主要对`WebPage`进行更新,`MixPage`会一直维持当前状态。 ```python # 创建页面对象 page = MixPage('d') # 访问百度 page.get('http://www.baidu.com') # 定位输入框并输入关键字 page.ele('#kw').input('python') # 点击“百度一下”按钮 page.ele('@value=百度一下').click() ``` 详细使用方法见“创建页面对象”和“操作页面”章节。 ## 📍 `DriverElement` `DriverElemnet`对象与`ChromiumElement`大致相同。区别是它是由`MiPage`所产生。 ```python # 点击一个元素 ele1.click() # 输入文本 ele1.input('some text') # 获取 class 属性 attr = ele1.attr('class') # 设置 style 属性 ele1.set_attr('style', 'display:none;') # 获取其子元素中所有 a 元素 links = ele1.eles('tag:a') ``` 详细使用方法见“获取页面元素”、“获取元素信息”和“操作页面元素”章节。 ## 📍 `Drission` `Drission`对象用于管理`MixPage`中与网页通讯的`WebDriver`对象和`Session`对象,相当于驱动器的角色。能实现这两个对象间的登录状态传递等。但它提供的功能往往通过`MixPage` 调用,所以出场几率并不高。 `WebPage`自行实现了这些功能,所以`WebPage`不需要`Drission`对象。 ```python from DrissionPage import Drission # 创建 Drission 对象 d = Drission() # 通过创建 MixPage 对象 page = MixPage(drission=d) ``` 实际上,如果创建时不传入`Drission`对象,`MixPage`会自动根据配置信息创建一个。 ```python from DrissionPage import MixPage # 直接创建 MixPage 对象,隐性创建 Drission 对象 page = MixPage() ``` # ✔️ 结构图 ## 📍 `WebPage`结构图 待更新。 ## 📍 `MixPage`结构图 如图所示,`Drission`对象负责链接的创建、共享登录状态等工作,类似 selenium 中 driver 的概念。 `MixPage`对象负责对获取到的页面进行解析、操作。 `DriverElement`和`SessionElement`则是从页面对象中获取到的元素对象。负责对元素进行解析和操作。 ![](https://gitee.com/g1879/DrissionPage-demos/raw/master/pics/20201118170751.jpg) # ✔️ 配置管理 无论 requests 还是浏览器,都通常须要一些配置信息才能正常工作,如长长的`user_agent`、driver 路径、浏览器配置等。这些代码往往是繁琐而重复的,不利于代码的简洁。 因此,DrissionPage 使用配置文件记录常用配置信息,程序会自动读取默认配置文件里的内容。所以,在示例中,通常看不见配置信息的代码。 这个功能支持用户保存不同的配置文件,按情况调研,也可以支持直接把配置写在代码里面,屏蔽读取配置文件。 ?> **Tips:**
当须要打包程序时,必须把配置写到代码里,或打包后手动复制配置文件到运行路径,否则会报错。 # ✔️ 定位符 定位符用于定位页面中的元素,是本库一大特色,能够用非常简明的方式来获取元素,简洁易用。可读性和易用性高于 xpath 等其它方式,并且兼容 xpath、css selector、selenium 定位符。 以下是一组对比: 定位文本包含`'abc'`的元素: ```python # DrissionPage ele = page('abc') # selenium ele = driver.find_element(By.XPATH, '//*[contains(text(), "abc"]') ``` 定位 class 为`'abc'`的元素 ```python # DrissionPage ele = page('.abc') # selenium ele = driver.find_element(By.CLASS_NAME, 'abc') ``` 定位 ele 元素的兄弟元素 ```python # DrissionPage ele1 = ele.next() # 获取后一个元素 ele1 = ele.prev(index=2) # 获取前面第二个元素 # selenium ele1 = ele.find_element(By.XPATH, './/following-sibling::*') # 获取有i一个元素 ele1 = ele.find_element(By.XPATH, './/preceding-sibling::*[2]') # 获取前面第二个元素 ``` 显然,本库的定位语句更简洁易懂,还有很多灵活好用的方法,详见“使用方法”里“查找元素章节”。