mirror of
https://gitee.com/g1879/DrissionPage.git
synced 2024-12-10 04:00:23 +08:00
2.0.0基本完成;s模式的ele()和eles()重新添加timeout参数;一些小调整
This commit is contained in:
parent
af69ca22c9
commit
6b9f0c42df
@ -20,11 +20,11 @@ class BaseParser(object):
|
||||
def __call__(self, loc_or_str):
|
||||
return self.ele(loc_or_str)
|
||||
|
||||
def ele(self, loc_or_ele):
|
||||
return self._ele(loc_or_ele, True)
|
||||
def ele(self, loc_or_ele, timeout=None):
|
||||
return self._ele(loc_or_ele, timeout, True)
|
||||
|
||||
def eles(self, loc_or_str: Union[Tuple[str, str], str]):
|
||||
return self._ele(loc_or_str, False)
|
||||
def eles(self, loc_or_str: Union[Tuple[str, str], str], timeout=None):
|
||||
return self._ele(loc_or_str, timeout, False)
|
||||
|
||||
@property
|
||||
def html(self) -> str:
|
||||
@ -70,10 +70,6 @@ class BaseElement(BaseParser):
|
||||
def tag(self):
|
||||
return
|
||||
|
||||
# @property
|
||||
# def html(self):
|
||||
# return
|
||||
|
||||
@property
|
||||
def parent(self):
|
||||
return
|
||||
@ -265,10 +261,6 @@ class BasePage(BaseParser):
|
||||
def url(self):
|
||||
return
|
||||
|
||||
# @property
|
||||
# def html(self):
|
||||
# return
|
||||
|
||||
@property
|
||||
def json(self):
|
||||
return
|
||||
|
@ -51,7 +51,7 @@ def get_ele_txt(e) -> str:
|
||||
str_list.append(el)
|
||||
|
||||
else:
|
||||
if sub('[ \n]', '', el) != '': # 字符除了回车和空格还有其它内容
|
||||
if sub('[ \n\t]', '', el) != '': # 字符除了回车和空格还有其它内容
|
||||
txt = el
|
||||
if not pre:
|
||||
txt = txt.replace('\n', ' ').strip(' ')
|
||||
|
@ -81,7 +81,6 @@ class DriverElement(DrissionElement):
|
||||
def text(self) -> str:
|
||||
"""返回元素内所有文本"""
|
||||
return get_ele_txt(make_session_ele(self.raw_html))
|
||||
# return get_ele_txt(self)
|
||||
|
||||
@property
|
||||
def raw_text(self) -> str:
|
||||
@ -106,7 +105,6 @@ class DriverElement(DrissionElement):
|
||||
:param attr: 属性名
|
||||
:return: 属性值文本
|
||||
"""
|
||||
# attr = 'innerText' if attr == 'text' else attr
|
||||
if attr in ('text', 'innerText'):
|
||||
return self.text
|
||||
|
||||
@ -343,7 +341,7 @@ class DriverElement(DrissionElement):
|
||||
"""
|
||||
if not insure_input: # 普通输入
|
||||
if clear:
|
||||
self.clear()
|
||||
self.inner_ele.clear()
|
||||
|
||||
self.inner_ele.send_keys(*vals)
|
||||
|
||||
@ -357,8 +355,7 @@ class DriverElement(DrissionElement):
|
||||
t1 = perf_counter()
|
||||
while self.is_valid() and self.attr('value') != full_txt and perf_counter() - t1 <= self.page.timeout:
|
||||
if clear:
|
||||
self.clear()
|
||||
|
||||
self.inner_ele.clear()
|
||||
self.inner_ele.send_keys(vals)
|
||||
|
||||
if enter:
|
||||
@ -378,7 +375,7 @@ class DriverElement(DrissionElement):
|
||||
|
||||
def clear(self) -> None:
|
||||
"""清空元素文本"""
|
||||
self.inner_ele.clear()
|
||||
self.input('')
|
||||
|
||||
def is_selected(self) -> bool:
|
||||
"""是否选中"""
|
||||
@ -861,7 +858,7 @@ def _wait_ele(page_or_ele,
|
||||
if mode.lower() not in ('del', 'display', 'hidden'):
|
||||
raise ValueError('mode参数只能是"del"、"display"或"hidden"。')
|
||||
|
||||
if isinstance(page_or_ele, DrissionElement): # TODO: 是否要改为 BaseElement
|
||||
if isinstance(page_or_ele, BaseElement):
|
||||
page = page_or_ele.page
|
||||
ele_or_driver = page_or_ele.inner_ele
|
||||
else:
|
||||
|
@ -132,7 +132,7 @@ class MixPage(SessionPage, DriverPage, BasePage):
|
||||
-> Union[DriverElement, SessionElement, str, List[SessionElement], List[DriverElement]]:
|
||||
"""返回第一个符合条件的元素、属性或节点文本 \n
|
||||
:param loc_or_ele: 元素的定位信息,可以是元素对象,loc元组,或查询字符串
|
||||
:param timeout: 查找元素超时时间,d模式专用
|
||||
:param timeout: 查找元素超时时间
|
||||
:return: 元素对象或属性、文本节点文本
|
||||
"""
|
||||
if self._mode == 's':
|
||||
@ -145,7 +145,7 @@ class MixPage(SessionPage, DriverPage, BasePage):
|
||||
timeout: float = None) -> Union[List[DriverElement], List[SessionElement], List[str]]:
|
||||
"""返回页面中所有符合条件的元素、属性或节点文本 \n
|
||||
:param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串
|
||||
:param timeout: 查找元素超时时间,d模式专用
|
||||
:param timeout: 查找元素超时时间
|
||||
:return: 元素对象或属性、文本组成的列表
|
||||
"""
|
||||
if self._mode == 's':
|
||||
|
@ -109,16 +109,18 @@ class SessionElement(DrissionElement):
|
||||
else:
|
||||
return self.inner_ele.get(attr)
|
||||
|
||||
def ele(self, loc_or_str: Union[Tuple[str, str], str]):
|
||||
def ele(self, loc_or_str: Union[Tuple[str, str], str], timeout=None):
|
||||
"""返回当前元素下级符合条件的第一个元素、属性或节点文本 \n
|
||||
:param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串
|
||||
:param timeout: 不起实际作用,用于和DriverElement对应,便于无差别调用
|
||||
:return: SessionElement对象或属性、文本
|
||||
"""
|
||||
return self._ele(loc_or_str)
|
||||
|
||||
def eles(self, loc_or_str: Union[Tuple[str, str], str]):
|
||||
def eles(self, loc_or_str: Union[Tuple[str, str], str], timeout=None):
|
||||
"""返回当前元素下级所有符合条件的子元素、属性或节点文本 \n
|
||||
:param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串
|
||||
:param timeout: 不起实际作用,用于和DriverElement对应,便于无差别调用
|
||||
:return: SessionElement对象或属性、文本组成的列表
|
||||
"""
|
||||
return self._ele(loc_or_str, single=False)
|
||||
|
@ -16,7 +16,7 @@ from requests import Session, Response
|
||||
from tldextract import extract
|
||||
|
||||
from .base import BasePage
|
||||
from .common import get_usable_path, format_html, make_valid_name
|
||||
from .common import get_usable_path, make_valid_name
|
||||
from .config import _cookie_to_dict
|
||||
from .session_element import SessionElement, make_session_ele
|
||||
|
||||
@ -96,17 +96,19 @@ class SessionPage(BasePage):
|
||||
|
||||
return self._url_available
|
||||
|
||||
def ele(self, loc_or_ele: Union[Tuple[str, str], str, SessionElement]) \
|
||||
def ele(self, loc_or_ele: Union[Tuple[str, str], str, SessionElement], timeout=None) \
|
||||
-> Union[SessionElement, List[SessionElement], str, None]:
|
||||
"""返回页面中符合条件的第一个元素、属性或节点文本 \n
|
||||
"""返回页面中符合条件的第一个元素、属性或节点文本 \n
|
||||
:param loc_or_ele: 元素的定位信息,可以是元素对象,loc元组,或查询字符串
|
||||
:param timeout: 不起实际作用,用于和DriverElement对应,便于无差别调用
|
||||
:return: SessionElement对象或属性、文本
|
||||
"""
|
||||
return self._ele(loc_or_ele)
|
||||
|
||||
def eles(self, loc_or_str: Union[Tuple[str, str], str]) -> List[SessionElement]:
|
||||
"""返回页面中所有符合条件的元素、属性或节点文本 \n
|
||||
def eles(self, loc_or_str: Union[Tuple[str, str], str], timeout=None) -> List[SessionElement]:
|
||||
"""返回页面中所有符合条件的元素、属性或节点文本 \n
|
||||
:param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串
|
||||
:param timeout: 不起实际作用,用于和DriverElement对应,便于无差别调用
|
||||
:return: SessionElement对象或属性、文本组成的列表
|
||||
"""
|
||||
return self._ele(loc_or_str, single=False)
|
||||
@ -300,7 +302,7 @@ class SessionPage(BasePage):
|
||||
return False, f'Status code: {r.status_code}.'
|
||||
|
||||
# -------------------获取文件名-------------------
|
||||
file_name = _get_download_file_name(file_url, r.headers)
|
||||
file_name = _get_download_file_name(file_url, r)
|
||||
|
||||
# -------------------重命名,不改变扩展名-------------------
|
||||
if rename:
|
||||
|
@ -10,7 +10,6 @@ from typing import Union, Any, Tuple, List
|
||||
from selenium.webdriver.remote.webelement import WebElement
|
||||
|
||||
from .base import BaseElement
|
||||
from .common import format_html
|
||||
from .driver_element import make_driver_ele, DriverElement
|
||||
from .session_element import make_session_ele
|
||||
|
||||
@ -41,11 +40,6 @@ class ShadowRootElement(BaseElement):
|
||||
"""元素标签名"""
|
||||
return 'shadow-root'
|
||||
|
||||
# @property
|
||||
# def html(self) -> str:
|
||||
# """内部已转码的html文本"""
|
||||
# return format_html(self.inner_ele.get_attribute('innerHTML'))
|
||||
|
||||
@property
|
||||
def raw_html(self) -> str:
|
||||
"""内部没有转码的html文本"""
|
||||
|
112
README.md
112
README.md
@ -1,3 +1,5 @@
|
||||
# 简洁!易用 !方便!
|
||||
|
||||
# 简介
|
||||
|
||||
***
|
||||
@ -10,65 +12,58 @@ DrissionPage,即 driver 和 session 组合而成的 page。
|
||||
它用 POM 模式封装了页面元素常用的方法,适合自动化操作功能扩展。
|
||||
更棒的是,它的使用方式非常简洁和人性化,代码量少,对新手友好。
|
||||
|
||||
**项目地址:**
|
||||
|
||||
- https://github.com/g1879/DrissionPage
|
||||
- https://gitee.com/g1879/DrissionPage
|
||||
|
||||
**示例地址:** [使用DrissionPage的网页自动化及爬虫示例](https://gitee.com/g1879/DrissionPage-demos)
|
||||
|
||||
**联系邮箱:** g1879@qq.com
|
||||
**交流QQ群:** 897838127 **联系邮箱:** g1879@qq.com
|
||||
|
||||
**交流QQ群:** 897838127
|
||||
|
||||
**理念**
|
||||
|
||||
**简洁、易用 、可扩展**
|
||||
|
||||
**背景**
|
||||
## 背景
|
||||
|
||||
requests 爬虫面对要登录的网站时,要分析数据包、JS 源码,构造复杂的请求,往往还要应付验证码、JS 混淆、签名参数等反爬手段,门槛较高。若数据是由 JS 计算生成的,还须重现计算过程,体验不好,开发效率不高。
|
||||
使用 selenium,可以很大程度上绕过这些坑,但 selenium 效率不高。因此,这个库将 selenium 和 requests 合而为一,不同须要时切换相应模式,并提供一种人性化的使用方法,提高开发和运行效率。
|
||||
除了合并两者,本库还以网页为单位封装了常用功能,简化了 selenium 的操作和语句,在用于网页自动化操作时,减少考虑细节,专注功能实现,使用更方便。
|
||||
一切从简,尽量提供简单直接的使用方法,对新手更友好。
|
||||
|
||||
# 特性
|
||||
# 特性和亮点
|
||||
|
||||
***
|
||||
|
||||
- 以简洁的代码为第一追求。
|
||||
- 允许在 selenium 和 requests 间无缝切换,共享 session。
|
||||
作者有多年自动化和爬虫经验,踩过无数坑,总结出的经验全写到这个库里了。内置了N多实用功能,对常用功能作了整合和优化。
|
||||
|
||||
## 特性
|
||||
|
||||
- 代码高度集成,以简洁的代码为第一追求。
|
||||
|
||||
- 页面对象可在 selenium 和 requests 模式间任意切换,保留登录状态。
|
||||
|
||||
- 两种模式提供一致的 API,使用体验一致。
|
||||
- 人性化的页面元素操作方式,减轻页面分析工作量和编码量。
|
||||
- 对常用功能作了整合和优化,更符合实际使用需要。
|
||||
- 兼容 selenium 代码,便于项目迁移。
|
||||
- 使用 POM 模式封装,便于扩展。
|
||||
- 统一的文件下载方法,弥补浏览器下载的不足。
|
||||
- 简易的配置方法,摆脱繁琐的浏览器配置。
|
||||
|
||||
# 项目结构
|
||||
- 人性化设计,集成众多实用功能,大大降低开发工作量。
|
||||
|
||||
***
|
||||
## 亮点
|
||||
|
||||
**结构图**
|
||||
|
||||

|
||||
|
||||
**Drission 类**
|
||||
|
||||
管理负责与网页通讯的 WebDriver 对象和 Session 对象,相当于驱动器的角色。
|
||||
|
||||
**MixPage 类**
|
||||
|
||||
MixPage 封装了页面操作的常用功能,它调用 Drission 类中管理的驱动器,对页面进行访问、操作。可在 driver 和 session 模式间切换。切换的时候会自动同步登录状态。
|
||||
|
||||
**DriverElement 类**
|
||||
|
||||
driver 模式下的页面元素类,可对元素进行点击、输入文本、修改属性、运行 js 等操作,也可在其下级搜索后代元素。
|
||||
|
||||
**SessionElement 类**
|
||||
|
||||
session 模式下的页面元素类,可获取元素属性值,也可在其下级搜索后代元素。
|
||||
- 每次运行程序可以反复使用已经打开的浏览器。如手动设置网页到某个状态,再用程序接管,或手动处理登录,再用程序爬内容。无须每次运行从头启动浏览器,超级方便。
|
||||
- 极简单但强大的元素查找功能,支持链式操作,代码极其简洁。
|
||||
- 使用 ini 文件保存常用配置,自动调用,也提供便捷的设置api,远离繁杂的配置项。
|
||||
- 强大的下载工具,操作浏览器时也能享受快捷可靠的下载功能。
|
||||
- 下载工具支持多种方式处理文件名冲突、自动创建目标路径、断链重试等。
|
||||
- 访问网址带自动重试功能,可设置间隔和超时时间。
|
||||
- 访问网页能自动识别编码,无须手动设置。
|
||||
- 链接参数默认自动生成 Host 和 Referer 属性。
|
||||
- 可随时直接隐藏或显示浏览器进程窗口,非 headless 或最小化。
|
||||
- 可自动下载合适版本的 chromedriver,免去麻烦的配置。
|
||||
- d 模式查找元素内置等待,可任意设置全局等待时间或单次查找等待时间。
|
||||
- 点击元素集成 js 点击方式,一个参数即可切换点击方式。
|
||||
- 点击支持失败重试,可用于保证点击成功、判读网页遮罩层是否消失等。
|
||||
- 输入文本能自动判断是否成功并重试,避免某些情况下输入或清空失效的情况。
|
||||
- d 模式下支持全功能的 xpath,可直接获取元素的某个属性,selenium 原生无此功能。
|
||||
- 支持直接获取 shadow-root,和普通元素一样操作其下的元素。
|
||||
- 支持直接获取 after 和 before 伪元素的内容。
|
||||
- 可以在元素下直接使用 > 以 css selector 方式获取当前元素直接子元素。原生不支持这种写法。
|
||||
- 可简单地使用 lxml 来解析 d 模式的页面或元素,爬取复杂页面数据时速度大幅提高。
|
||||
- 输出的数据均已转码及处理基本排版,减少重复劳动。
|
||||
- 可方便地与 selenium 或 requests 原生代码对接,便于项目迁移。
|
||||
- 使用 POM 模式封装,可直接用于测试,便于扩展。
|
||||
- 还有很多这里不一一列举…………
|
||||
|
||||
# 简单演示
|
||||
|
||||
@ -158,9 +153,10 @@ text = element.after
|
||||
shadow_element = webdriver.execute_script('return arguments[0].shadowRoot', element)
|
||||
|
||||
# 使用 DrissionPage:
|
||||
shadow_element = element.shadow_root
|
||||
# 或
|
||||
shadow_element = element.sr
|
||||
|
||||
# 在 shadow_root 下可继续执行查找,获取普通元素
|
||||
ele = shadow_element.ele('tag:div')
|
||||
```
|
||||
|
||||
- 用 xpath 直接获取属性或文本节点(返回文本)
|
||||
@ -174,6 +170,18 @@ class_name = element('xpath://div[@id="div_id"]/@class')
|
||||
text = element('xpath://div[@id="div_id"]/text()[2]')
|
||||
```
|
||||
|
||||
- 随时让浏览器窗口消失和显示
|
||||
|
||||
```python
|
||||
# selenium无此功能
|
||||
|
||||
# 使用 DrissionPage
|
||||
page.hide_browser() # 让浏览器窗口消失
|
||||
page.show_browser() # 重新显示浏览器窗口
|
||||
```
|
||||
|
||||
注:本功能只支持 Windows,且须设置了 debugger_address 参数时才能生效
|
||||
|
||||
**与 requests 代码对比**
|
||||
|
||||
以下代码实现一模一样的功能,对比两者的代码量:
|
||||
@ -185,7 +193,7 @@ url = 'https://baike.baidu.com/item/python'
|
||||
|
||||
# 使用 requests:
|
||||
from lxml import etree
|
||||
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36'}
|
||||
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36'}
|
||||
response = requests.get(url, headers = headers)
|
||||
html = etree.HTML(response.text)
|
||||
element = html.xpath('//h1')[0]
|
||||
@ -212,7 +220,7 @@ with open(f'{save_path}\\img.png', 'wb') as fd:
|
||||
fd.write(chunk)
|
||||
|
||||
# 使用 DrissionPage:
|
||||
page.download(url, save_path, 'img') # 支持重命名,处理文件名冲突
|
||||
page.download(url, save_path, 'img') # 支持重命名,处理文件名冲突,自动创建目标文件夹
|
||||
```
|
||||
|
||||
**模式切换**
|
||||
@ -237,7 +245,7 @@ print('登录后title:', page.title, '\n') # 登录后 session 模式的输
|
||||
登录后title: 个人资料 - 码云 Gitee.com
|
||||
```
|
||||
|
||||
**获取并打印元素属性**
|
||||
**获取并显示元素属性**
|
||||
|
||||
```python
|
||||
# 接上段代码
|
||||
@ -262,14 +270,6 @@ Git 命令学习 https://oschina.gitee.io/learn-git-branching/
|
||||
Git 命令学习
|
||||
```
|
||||
|
||||
**下载文件**
|
||||
|
||||
```python
|
||||
url = 'https://www.baidu.com/img/flexible/logo/pc/result.png'
|
||||
save_path = r'C:\download'
|
||||
page.download(url, save_path)
|
||||
```
|
||||
|
||||
# 使用方法
|
||||
|
||||
***
|
||||
|
Loading…
x
Reference in New Issue
Block a user