From 89769f3caa5ad84d8fe221af09ab456d7b303b27 Mon Sep 17 00:00:00 2001 From: g1879 Date: Mon, 4 Jan 2021 00:35:55 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=A7=E7=BB=AD=E7=A0=94=E7=A9=B6text?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E7=9A=84=E6=A0=BC=E5=BC=8F=EF=BC=8C=E6=9C=AA?= =?UTF-8?q?=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DrissionPage/common.py | 26 +++++++++++++++++----- DrissionPage/driver_element.py | 34 +++++++++++++++++++++++----- DrissionPage/session_element.py | 39 ++++++++++++++++++++++++--------- 3 files changed, 78 insertions(+), 21 deletions(-) diff --git a/DrissionPage/common.py b/DrissionPage/common.py index 3c24128..d4a7044 100644 --- a/DrissionPage/common.py +++ b/DrissionPage/common.py @@ -5,7 +5,7 @@ @File : common.py """ from abc import abstractmethod -from html import unescape +# from html import unescape from pathlib import Path from re import split as re_SPLIT from shutil import rmtree @@ -96,7 +96,12 @@ def str_to_loc(loc: str) -> tuple: text:search_text - 文本含有search_text的元素 \n text=search_text - 文本等于search_text的元素 \n xpath://div[@class="ele_class"] - 用xpath查找 \n - css:div.ele_class - 用css selector查找 + css:div.ele_class - 用css selector查找 \n + xpath://div[@class="ele_class"] - 等同于 x://div[@class="ele_class"] \n + css:div.ele_class - 等同于 c:div.ele_class \n + tag:div - 等同于 t:div \n + text:search_text - 等同于 tx:search_text \n + text=search_text - 等同于 tx=search_text \n """ loc_by = 'xpath' @@ -195,7 +200,7 @@ def _make_xpath_str(tag: str, arg: str, val: str, mode: str = 'fuzzy') -> str: def _make_search_str(search_str: str) -> str: - """将"转义,不知何故不能直接用\来转义 \n + """将"转义,不知何故不能直接用 \ 来转义 \n :param search_str: 查询字符串 :return: 把"转义后的字符串 """ @@ -212,9 +217,20 @@ def _make_search_str(search_str: str) -> str: return search_str -def format_html(text: str) -> str: +def format_html(text: str, replace_space: bool = True) -> str: """处理html编码字符""" - return unescape(text) if text else text + if not text: + return text + + # text = unescape(text) + + # if '&' in text: + # html = unescape(text) + + if replace_space: + text = text.replace('\xa0', ' ') + + return text def translate_loc(loc: tuple) -> tuple: diff --git a/DrissionPage/driver_element.py b/DrissionPage/driver_element.py index 88e6544..65a3b37 100644 --- a/DrissionPage/driver_element.py +++ b/DrissionPage/driver_element.py @@ -4,6 +4,7 @@ @Contact : g1879@qq.com @File : driver_element.py """ +import re from pathlib import Path from time import sleep from typing import Union, List, Any, Tuple @@ -78,7 +79,13 @@ class DriverElement(DrissionElement): @property def text(self) -> str: """返回元素内所有文本""" - return self.attr('innerText') + # return self.inner_ele.get_attribute('innerText') + re_str = self.inner_ele.get_attribute('innerText') + re_str = re.sub(r'\n{2,}', '\n', re_str) + re_str = re.sub(r' {2,}', ' ', re_str) + + return format_html(re_str.strip('\n ')) + # return re_str.strip('\n ') @property def link(self) -> str: @@ -178,7 +185,10 @@ class DriverElement(DrissionElement): :param attr: 属性名 :return: 属性值文本 """ - attr = 'innerText' if attr == 'text' else attr + # attr = 'innerText' if attr == 'text' else attr + if attr in ('text', 'innerText'): + return self.text + return format_html(self.inner_ele.get_attribute(attr)) def ele(self, @@ -188,7 +198,7 @@ class DriverElement(DrissionElement): """返回当前元素下级符合条件的子元素、属性或节点文本,默认返回第一个 \n 示例: \n - 用loc元组查找: \n - ele.ele((By.CLASS_NAME, 'ele_class')) - 返回第一个class为ele_class的子元素 \n + ele.ele((By.CLASS_NAME, 'ele_class')) - 返回第一个class为ele_class的子元素 \n - 用查询字符串查找: \n 查找方式:属性、tag name和属性、文本、xpath、css selector、id、class \n @表示属性,.表示class,#表示id,=表示精确匹配,:表示模糊匹配,无控制字符串时默认搜索该字符串 \n @@ -209,6 +219,12 @@ class DriverElement(DrissionElement): ele.ele('text=some_text') - 返回第一个文本等于some_text的子元素 \n ele.ele('xpath://div[@class="ele_class"]') - 返回第一个符合xpath的子元素 \n ele.ele('css:div.ele_class') - 返回第一个符合css selector的子元素 \n + - 查询字符串还有最精简模式,用x代替xpath、c代替css、t代替tag、tx代替text: \n + ele.ele('xpath://div[@class="ele_class"]') - 等同于 ele.ele('x://div[@class="ele_class"]') \n + ele.ele('css:div.ele_class') - 等同于 ele.ele('c:div.ele_class') \n + ele.ele('tag:div') - 等同于 ele.ele('t:div') \n + ele.ele('text:some_text') - 等同于 ele.ele('tx:some_text') \n + ele.ele('text=some_text') - 等同于 ele.ele('tx=some_text') :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param mode: 'single' 或 'all',对应查找一个或全部 :param timeout: 查找元素超时时间 @@ -244,7 +260,7 @@ class DriverElement(DrissionElement): """返回当前元素下级所有符合条件的子元素、属性或节点文本 \n 示例: \n - 用loc元组查找: \n - ele.eles((By.CLASS_NAME, 'ele_class')) - 返回所有class为ele_class的子元素 \n + ele.eles((By.CLASS_NAME, 'ele_class')) - 返回所有class为ele_class的子元素 \n - 用查询字符串查找: \n 查找方式:属性、tag name和属性、文本、xpath、css selector、id、class \n @表示属性,.表示class,#表示id,=表示精确匹配,:表示模糊匹配,无控制字符串时默认搜索该字符串 \n @@ -265,6 +281,12 @@ class DriverElement(DrissionElement): ele.eles('text=some_text') - 返回所有文本等于some_text的子元素 \n ele.eles('xpath://div[@class="ele_class"]') - 返回所有符合xpath的子元素 \n ele.eles('css:div.ele_class') - 返回所有符合css selector的子元素 \n + - 查询字符串还有最精简模式,用x代替xpath、c代替css、t代替tag、tx代替text: \n + ele.eles('xpath://div[@class="ele_class"]') - 等同于 ele.eles('x://div[@class="ele_class"]') \n + ele.eles('css:div.ele_class') - 等同于 ele.eles('c:div.ele_class') \n + ele.eles('tag:div') - 等同于 ele.eles('t:div') \n + ele.eles('text:some_text') - 等同于 ele.eles('tx:some_text') \n + ele.eles('text=some_text') - 等同于 ele.eles('tx=some_text') :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param timeout: 查找元素超时时间 :return: DriverElement对象组成的列表 @@ -574,7 +596,7 @@ def execute_driver_find(page_or_ele, :return: 返回DriverElement元素或它们组成的列表 """ mode = mode or 'single' - if mode not in ['single', 'all']: + if mode not in ('single', 'all'): raise ValueError(f"Argument mode can only be 'single' or 'all', not '{mode}'.") if isinstance(page_or_ele, DrissionElement): @@ -674,7 +696,7 @@ class ElementsByXpath(object): else: driver, the_node = ele_or_driver.parent, ele_or_driver - # 把lxml元素对象包装成DriverElement对象并按需要返回第一个或全部 + # 把lxml元素对象包装成DriverElement对象并按需要返回第一个或全部 if self.mode == 'single': try: e = get_nodes(the_node, xpath_txt=self.xpath, type_txt='9') diff --git a/DrissionPage/session_element.py b/DrissionPage/session_element.py index 7d402ff..04ec7ef 100644 --- a/DrissionPage/session_element.py +++ b/DrissionPage/session_element.py @@ -36,9 +36,8 @@ class SessionElement(DrissionElement): @property def html(self) -> str: """返回元素outerHTML文本""" - # tostring()会把跟紧元素的文本节点也带上,因此要去掉 html = format_html(tostring(self._inner_ele, method="html").decode()) - return format_html(html[:html.rfind('>') + 1]) + return html[:html.rfind('>') + 1] # tostring()会把跟紧元素的文本节点也带上,因此要去掉 @property def inner_html(self) -> str: @@ -50,26 +49,35 @@ class SessionElement(DrissionElement): def text(self) -> str: """返回元素内所有文本""" + # re_str = str(self._inner_ele.text_content()) + # # re_str = re.sub(r'
', '\n', re_str) + # re_str = re.sub(r'\n{2,}', '\n', re_str) + # re_str = re.sub(r' {2,}', ' ', re_str) + # return format_html(re_str.strip('\n ')) + # # return format_html(re_str) + # 为尽量保证与浏览器结果一致,弄得比较复杂 def get_node(ele): str_list = [] for el in ele.eles('xpath:./node()'): if isinstance(el, str): if el.replace(' ', '').replace('\n', '') != '': - str_list.append(el.replace('\xa0', ' ').replace('\n', ' ').strip()) + # str_list.append(el.replace('\xa0', ' ').replace('\n', ' ').strip()) + str_list.append(el.replace('\n', ' ').strip(' ')) elif '\n' in el: str_list.append('\n') else: str_list.append(' ') else: str_list.extend(get_node(el)) + if el.tag in ('br', 'p',): + str_list.append('\n') return str_list re_str = ''.join(get_node(self)) re_str = re.sub(r'\n{2,}', '\n', re_str) re_str = re.sub(r' {2,}', ' ', re_str) - return format_html(re_str.strip('\n ')) @property @@ -176,7 +184,7 @@ class SessionElement(DrissionElement): elif attr == 'src': return self._make_absolute(self.inner_ele.get('src')) - elif attr in ['text', 'innerText']: + elif attr in ('text', 'innerText'): return self.text elif attr == 'outerHTML': @@ -189,10 +197,10 @@ class SessionElement(DrissionElement): return self.inner_ele.get(attr) def ele(self, loc_or_str: Union[Tuple[str, str], str], mode: str = None): - """返回当前元素下级符合条件的子元素、属性或节点文本,默认返回第一个 \n + """返回当前元素下级符合条件的子元素、属性或节点文本,默认返回第一个 \n 示例: \n - 用loc元组查找: \n - ele.ele((By.CLASS_NAME, 'ele_class')) - 返回第一个class为ele_class的子元素 \n + ele.ele((By.CLASS_NAME, 'ele_class')) - 返回第一个class为ele_class的子元素 \n - 用查询字符串查找: \n 查找方式:属性、tag name和属性、文本、xpath、css selector、id、class \n @表示属性,.表示class,#表示id,=表示精确匹配,:表示模糊匹配,无控制字符串时默认搜索该字符串 \n @@ -213,6 +221,12 @@ class SessionElement(DrissionElement): ele.ele('text=some_text') - 返回第一个文本等于some_text的子元素 \n ele.ele('xpath://div[@class="ele_class"]') - 返回第一个符合xpath的子元素 \n ele.ele('css:div.ele_class') - 返回第一个符合css selector的子元素 \n + - 查询字符串还有最精简模式,用x代替xpath、c代替css、t代替tag、tx代替text: \n + ele.ele('xpath://div[@class="ele_class"]') - 等同于 ele.ele('x://div[@class="ele_class"]') \n + ele.ele('css:div.ele_class') - 等同于 ele.ele('c:div.ele_class') \n + ele.ele('tag:div') - 等同于 ele.ele('t:div') \n + ele.ele('text:some_text') - 等同于 ele.ele('tx:some_text') \n + ele.ele('text=some_text') - 等同于 ele.ele('tx=some_text') :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param mode: 'single' 或 'all‘,对应查找一个或全部 :return: SessionElement对象 @@ -246,7 +260,7 @@ class SessionElement(DrissionElement): """返回当前元素下级所有符合条件的子元素、属性或节点文本 \n 示例: \n - 用loc元组查找: \n - ele.eles((By.CLASS_NAME, 'ele_class')) - 返回所有class为ele_class的子元素 \n + ele.eles((By.CLASS_NAME, 'ele_class')) - 返回所有class为ele_class的子元素 \n - 用查询字符串查找: \n 查找方式:属性、tag name和属性、文本、xpath、css selector、id、class \n @表示属性,.表示class,#表示id,=表示精确匹配,:表示模糊匹配,无控制字符串时默认搜索该字符串 \n @@ -267,6 +281,12 @@ class SessionElement(DrissionElement): ele.eles('text=some_text') - 返回所有文本等于some_text的子元素 \n ele.eles('xpath://div[@class="ele_class"]') - 返回所有符合xpath的子元素 \n ele.eles('css:div.ele_class') - 返回所有符合css selector的子元素 \n + - 查询字符串还有最精简模式,用x代替xpath、c代替css、t代替tag、tx代替text: \n + ele.eles('xpath://div[@class="ele_class"]') - 等同于 ele.eles('x://div[@class="ele_class"]') \n + ele.eles('css:div.ele_class') - 等同于 ele.eles('c:div.ele_class') \n + ele.eles('tag:div') - 等同于 ele.eles('t:div') \n + ele.eles('text:some_text') - 等同于 ele.eles('tx:some_text') \n + ele.eles('text=some_text') - 等同于 ele.eles('tx=some_text') :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :return: SessionElement对象组成的列表 """ @@ -363,7 +383,7 @@ def execute_session_find(page_or_ele, :return: 返回SessionElement元素或列表 """ mode = mode or 'single' - if mode not in ['single', 'all']: + if mode not in ('single', 'all'): raise ValueError(f"Argument mode can only be 'single' or 'all', not '{mode}'.") # 根据传入对象类型获取页面对象和lxml元素对象 @@ -372,7 +392,6 @@ def execute_session_find(page_or_ele, page_or_ele = page_or_ele.inner_ele else: # 传入的是SessionPage对象 page = page_or_ele - # page_or_ele = fromstring(format_html(page_or_ele.response.text, False)) page_or_ele = fromstring(page_or_ele.response.text) try: