4040 '☁' # 云符号
4141]
4242
43+ # 其他标点符
44+ other_symbols = [
45+ '“' ,
46+ '‘' ,
47+ '[' ,
48+ '(' ,
49+ '”' ,
50+ '’' ,
51+ '。' ,
52+ ','
53+ ]
54+
4355PARAGRAPH_SEPARATOR = '\n \n '
4456
4557# 需要保留的html实体,例如:'>' 直接在markdown中无法渲染,需要替换为html实体
5062 'a' , 'abbr' , 'acronym' , 'b' , 'bdo' , 'big' , 'br' , 'button' , 'cite' , 'code' ,
5163 'dfn' , 'em' , 'i' , 'img' , 'input' , 'kbd' , 'label' , 'map' , 'object' , 'q' ,
5264 'samp' , 'script' , 'select' , 'small' , 'span' , 'strong' , 'sub' , 'sup' ,
53- 'textarea' , 'time' , 'var' , 'u' , 's' , 'code' , 'cccode-inline' , 'ccmath-inline' ,
54- 'marked-tail' , 'marked-text' ,'math' ,'mspace'
65+ 'textarea' , 'time' , 'var' , 'u' , 's' , 'cccode-inline' , 'ccmath-inline' ,
66+ 'marked-tail' , 'marked-text' , 'math' ,'mspace' , 'font' , 'nobr' , 'bdi' ,
67+ 'mjx-container' , 'mjx-assistive-mml' , 'strike' , 'wbr' , 'ins'
5568}
5669
5770
@@ -93,6 +106,7 @@ def recognize(self, base_url:str, main_html_lst: List[Tuple[HtmlElement | str, H
93106 new_html_lst = []
94107 for html_element , raw_html_element in main_html_lst :
95108 # 如果是字符串则转换为 HtmlElement
109+
96110 if self .is_cc_html (html_element ):
97111 new_html_lst .append ((html_element , raw_html_element ))
98112 else :
@@ -108,7 +122,9 @@ def __to_cctext_lst(self, lst: List[Tuple[HtmlElement | str, HtmlElement | str]]
108122 lst: List[Tuple[HtmlElement | str, HtmlElement | str]]: Element和raw_html组成的列表
109123 """
110124 new_lst = []
125+
111126 for el , raw_html in lst :
127+
112128 # 如果是字符串则转换为 HtmlElement
113129 el_element = html_to_element (el ) if isinstance (el , str ) else el
114130 raw_html_element = html_to_element (raw_html ) if isinstance (raw_html , str ) else raw_html
@@ -120,20 +136,45 @@ def __to_cctext_lst(self, lst: List[Tuple[HtmlElement | str, HtmlElement | str]]
120136 return new_lst
121137
122138 def replace_entities (self , text , entities_map ):
123- """使用正则表达式同时替换文本中的多个特定字符为其对应的HTML实体 。
139+ """替换文本中指定字符为对应的HTML实体,但跳过HTML标签内的字符 。
124140
125141 :param text: 需要处理的文本。
126- :param entities_map: 一个字典,键是需要替换的字符 ,值是对应的HTML实体名
142+ :param entities_map: 字典,键是要替换的字符 ,值是对应的HTML实体名。
127143 :return: 替换后的文本。
128144 """
129- # 创建正则表达式模式,匹配所有需要替换的字符
130- rx = re .compile ('|' .join (re .escape (str (key )) for key in entities_map .keys ()))
145+ if not entities_map :
146+ return text # 如果字典为空,直接返回原文本
147+
148+ # 构建匹配需要替换字符的正则表达式
149+ entities_pattern = '|' .join (re .escape (str (key )) for key in entities_map .keys ())
150+ rx_entity = re .compile (entities_pattern )
151+
152+ # 构建匹配HTML标签的正则表达式
153+ rx_tag = re .compile (r'<[^>]*>' )
131154
132- def one_xlat (match ):
133- """回调函数,用于将匹配到的字符替换为对应的HTML实体。"""
134- return f'&{ entities_map [match .group (0 )]} ;'
155+ result = []
156+ last_pos = 0
135157
136- return rx .sub (one_xlat , text )
158+ # 遍历所有HTML标签
159+ for tag_match in rx_tag .finditer (text ):
160+ start , end = tag_match .start (), tag_match .end ()
161+
162+ # 提取非标签部分并进行替换
163+ non_tag_part = text [last_pos :start ]
164+ replaced = rx_entity .sub (lambda m : f'&{ entities_map [m .group (0 )]} ;' , non_tag_part )
165+ result .append (replaced )
166+
167+ # 保留HTML标签不变
168+ result .append (text [start :end ])
169+
170+ last_pos = end
171+
172+ # 处理最后剩余的非标签部分
173+ non_tag_part = text [last_pos :]
174+ replaced = rx_entity .sub (lambda m : f'&{ entities_map [m .group (0 )]} ;' , non_tag_part )
175+ result .append (replaced )
176+
177+ return '' .join (result )
137178
138179 def __combine_text (self , text1 :str , text2 :str , lang = 'en' ) -> str :
139180 """将两段文本合并,中间加空格.
@@ -149,7 +190,11 @@ def __combine_text(self, text1:str, text2:str, lang='en') -> str:
149190 txt = text1 + text2
150191 return self .replace_entities (txt .strip (), entities_map )
151192 else :
152- words_sep = '' if text2 [0 ] in string .punctuation or text2 [0 ] in special_symbols else ' '
193+ # 根据text1的最后一个字符和text2的第一个字符判断两个text之间的连接
194+ if (text2 [0 ] in string .punctuation ) or (text2 [0 ] in special_symbols ) or (text2 [0 ] in other_symbols ) or (text1 and text1 [- 1 ] in other_symbols ):
195+ words_sep = ''
196+ else :
197+ words_sep = ' '
153198 txt = text1 + words_sep + text2
154199 return self .replace_entities (txt .strip (), entities_map )
155200
@@ -169,7 +214,6 @@ def __get_paragraph_text(self, root: HtmlElement) -> List[dict]:
169214 para_text = []
170215
171216 def __get_paragraph_text_recusive (el : HtmlElement , text : str ) -> str :
172-
173217 # 标记当前元素是否是sub或sup类型
174218 is_sub_sup = el .tag == 'sub' or el .tag == 'sup'
175219
@@ -187,6 +231,8 @@ def __get_paragraph_text_recusive(el: HtmlElement, text: str) -> str:
187231 text += PARAGRAPH_SEPARATOR # TODO 这个地方直接加换行是错误点做法,需要利用数据结构来保证段落。
188232 elif el .tag == 'sub' or el .tag == 'sup' :
189233 text = process_sub_sup_tags (el , text , recursive = False )
234+ elif el .tag == 'audio' : # 避免audio被识别为paragraph
235+ pass
190236 else :
191237 if el .text and el .text .strip ():
192238 text = self .__combine_text (text , el .text .strip ())
0 commit comments