Skip to content

Commit 555f755

Browse files
authored
Merge pull request #526 from ccprocessor/dev
v3.2.2-released
2 parents 9e03b51 + d5a7f8f commit 555f755

13 files changed

Lines changed: 157 additions & 29 deletions

File tree

llm_web_kit/extractor/html/recognizer/cc_math/render/mathjax.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,42 @@ def _detect_ascii_math(self, tree: HtmlElement) -> bool:
533533
return processascii
534534

535535

536+
class MathJaxRenderMock(MathJaxRender):
537+
"""虚拟的MathJax渲染器,用于没有MathJax配置但需要使用MathJax解析逻辑的情况.
538+
539+
这个类主要用于处理以下场景:
540+
1. 网页中没有显式的MathJax配置(如<script type="text/x-mathjax-config">)
541+
2. 但在HTML解析过程中检测到了数学公式元素(如<math>标签、公式相关的class等)
542+
3. 需要使用MathJax渲染器方案扫一遍所有内容,防止漏抽取公式
543+
544+
与普通MathJaxRender的区别:
545+
- MathJaxRender:会解析HTML中的MathJax配置,使用自定义的分隔符和选项
546+
- MathJaxRenderMock:直接使用默认的MathJax配置,不解析HTML配置
547+
"""
548+
549+
def __init__(self):
550+
"""初始化虚拟MathJax渲染器."""
551+
super().__init__()
552+
self.render_type = MathRenderType.MATHJAX_MOCK
553+
# 使用默认的MathJax选项
554+
self.options = MATHJAX_OPTIONS.copy()
555+
556+
def get_options(self, html: str) -> Dict[str, Any]:
557+
"""虚拟渲染器直接返回默认选项,不解析HTML配置.
558+
559+
Args:
560+
html: HTML字符串(忽略)
561+
562+
Returns:
563+
Dict[str, Any]: 默认MathJax选项字典
564+
"""
565+
return self.options
566+
567+
def is_customized_options(self) -> bool:
568+
"""虚拟渲染器始终返回False,表示使用默认配置."""
569+
return False
570+
571+
536572
# 使用示例
537573
if __name__ == '__main__':
538574
# MathJax示例

llm_web_kit/extractor/html/recognizer/cc_math/render/render.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
class MathRenderType:
1212
"""数学公式渲染器类型."""
1313
MATHJAX = 'mathjax'
14+
MATHJAX_MOCK = 'mathjax_mock' # 虚拟的mathjax渲染器
1415
MATHJAX_CUSTOMIZED = 'mathjax_customized' # 临时增加这个type,未来区分走自定义解析的数据
1516
KATEX = 'katex'
1617

llm_web_kit/extractor/html/recognizer/cc_math/tag_script.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,16 @@ def modify_tree(cm: CCMATH, math_render: str, o_html: str, node: HtmlElement, pa
1414
try:
1515
text = node.text
1616
if text and text_strip(text):
17+
# 先处理非script标签和style标签的节点:即class为math/katex的节点
18+
# 例子:<div class="math">f(x) \sim x^2, \quad x\to\infty</div>
1719
if node.tag not in ['script', 'style']:
1820
new_span = create_new_span([(CCMATH_INLINE,MathType.LATEX)], cm.wrap_math_md(text), node, math_render, o_html)
19-
node.addnext(new_span)
21+
# node.addnext(new_span)
22+
replace_element(node, new_span) # 替换节点,而不是添加
23+
24+
# 下面是katex逻辑
2025
else:
26+
# 例子:<script type = "e44e-text/javascript">katex.render("f(a,b,c) = (a^2+b^2+c^2)^3", mykatex);</script>
2127
katex_pattern = re.compile(r'katex.render')
2228
node_text = text_strip(text)
2329
if katex_pattern.findall(node_text):
@@ -28,8 +34,17 @@ def modify_tree(cm: CCMATH, math_render: str, o_html: str, node: HtmlElement, pa
2834
target_element = target_elements[0]
2935
o_html = element_to_html(target_element)
3036
target_element.text = None
31-
new_span = create_new_span([(CCMATH_INLINE,MathType.LATEX)], cm.wrap_math_md(formula_content), target_element, math_render, o_html)
37+
wrapped_formula = cm.wrap_math_md(formula_content)
38+
# 转化为ccmath,例子:
39+
# <ccmath-inline type="latex" by="katex" html='...'>f(a,b,c) = (a^2+b^2+c^2)^3</ccmath-inline>
40+
new_span = create_new_span([(CCMATH_INLINE, MathType.LATEX)], wrapped_formula,
41+
target_element, math_render, o_html)
42+
# 插入到span标签内,例子:
43+
# <span id="mykatex"><ccmath-inline ... </ccmath-inline></span>
3244
target_element.insert(0, new_span)
45+
46+
# 处理sript且type为math/tex的节点
47+
# 例子:<html><head><script type="math/tex">x^2 + y^2 = z^2</script></head></html>
3348
elif node.get('type') and 'math/tex' in node.get('type'):
3449
tag_math_type_list = cm.get_equation_type(o_html)
3550
if not tag_math_type_list:

llm_web_kit/extractor/html/recognizer/ccmath.py

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class MathRecognizer(BaseHTMLElementRecognizer):
2424
def __init__(self):
2525
super().__init__()
2626
self.cm = CCMATH()
27+
self.mathjax_detected = False # 添加检测标记
2728

2829
@override
2930
def recognize(self, base_url: str, main_html_lst: List[Tuple[HtmlElement, HtmlElement]], raw_html: str, language:str = 'en') -> List[Tuple[HtmlElement, HtmlElement]]:
@@ -122,8 +123,9 @@ def process_ccmath_html(self, cc_html: str, o_html: str, math_render: BaseMathRe
122123
self.cm.url = base_url
123124
tree = cc_html
124125
math_render_type = math_render.get_render_type()
125-
# 打印遍历node次数
126-
# count = 0
126+
self.mathjax_detected = False # 重置标记
127+
128+
# process1: node循环逻辑
127129
for node in iter_node(tree):
128130
assert isinstance(node, HtmlElement)
129131
original_html = self._element_to_html(node)
@@ -134,9 +136,11 @@ def process_ccmath_html(self, cc_html: str, o_html: str, math_render: BaseMathRe
134136
node.tag == 'span' and
135137
node.get('class') in [CSDN.INLINE, CSDN.DISPLAY]):
136138
tag_script.process_katex_mathml(self.cm, math_render_type, node)
139+
self.mathjax_detected = True
137140

138141
if ZHIHU.DOMAIN in self.cm.url and node.tag == 'span' and node.get('class') == ZHIHU.MATH:
139142
tag_script.process_zhihu_custom_tag(self.cm, math_render_type, node)
143+
self.mathjax_detected = True
140144

141145
# tag = span, class 为 math-containerm, 或者 mathjax 或者 wp-katex-eq
142146
if node.tag == 'span' and node.get('class') and (
@@ -147,44 +151,50 @@ def process_ccmath_html(self, cc_html: str, o_html: str, math_render: BaseMathRe
147151
'tex' in node.get('class')
148152
):
149153
tag_common_modify.modify_tree(self.cm, math_render_type, original_html, node, parent)
150-
151-
# script[type="math/tex"]
152-
# if node.tag == 'script' and node.get('type') and 'math/tex' in node.get('type'):
153-
# print('匹配到script标签: ', node.get('type'))
154-
# tag_common_modify.modify_tree(cm, math_render_type, original_html, node, parent)
154+
self.mathjax_detected = True
155155

156156
# math tags
157157
if node.tag == 'math' or node.tag.endswith(':math'):
158158
# print(f"匹配到数学标签: {node.tag}")
159159
# print(f"标签内容: {original_html}")
160160
tag_math.modify_tree(self.cm, math_render_type, original_html, node, parent)
161+
self.mathjax_detected = True
161162

162163
if node.tag == 'mjx-container':
163164
tag_mjx.modify_tree(self.cm, math_render, original_html, node)
165+
self.mathjax_detected = True
164166

165167
# img中的latex
166168
if node.tag == 'img':
167169
tag_img.modify_tree(self.cm, math_render_type, original_html, node, parent)
170+
self.mathjax_detected = True
168171

169172
# span.katex
170173
if node.tag == 'script' or 'math' == node.get('class') or 'katex' == node.get('class'):
171174
# print('匹配到script/math/katex标签: ', original_html)
172175
tag_script.modify_tree(self.cm, math_render_type, original_html, node, parent)
176+
self.mathjax_detected = True
173177
# 只有有渲染器的网站才会走下面文本匹配逻辑
174178
if math_render_type:
175179
# 14. 只处理只有一层的p标签
176180
if node.tag == 'p' and len(node.getchildren()) == 0:
177181
# print('匹配到p标签: ', original_html)
178182
tag_common_modify.modify_tree(self.cm, math_render_type, original_html, node, parent)
183+
self.mathjax_detected = True
179184

180-
# 修改:传入tree节点,mathjax方案作为process2,不参与上面process1节点的遍历
181-
if math_render_type:
182-
try:
183-
if math_render_type == MathRenderType.MATHJAX:
184-
math_render.find_math(tree)
185-
except Exception as e:
186-
raise HtmlMathMathjaxRenderRecognizerException(f'处理MathjaxRender数学公式失败: {e}')
187-
185+
# procsee2: mathjax渲染器逻辑
186+
try:
187+
# case1:有mathjax配置
188+
if math_render_type == MathRenderType.MATHJAX:
189+
math_render.find_math(tree)
190+
# case2:无Mathjax配置但是开启Mathjax逻辑开关(node循环抽到公式的情况)
191+
elif math_render_type is None and self.mathjax_detected:
192+
from llm_web_kit.extractor.html.recognizer.cc_math.render.mathjax import \
193+
MathJaxRenderMock
194+
math_render = MathJaxRenderMock()
195+
math_render.find_math(tree)
196+
except Exception as e:
197+
raise HtmlMathMathjaxRenderRecognizerException(f'处理MathjaxRender数学公式失败: {e}')
188198
# 保存处理后的html
189199
# with open('test20250702_result.html', 'w', encoding='utf-8') as f:
190200
# f.write(self._element_to_html(tree))

llm_web_kit/extractor/html/recognizer/list.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -199,13 +199,14 @@ def __extract_list_item_text_recusive(el: HtmlElement):
199199
# item['c'].strip(): 会导致前面处理br标签,添加的\n\n失效
200200
result['c'] = ' '.join(normalize_text_segment(item['c'].strip()) for item in paragraph)
201201
return result
202-
list_item_tags = ('li', 'dd', 'dt', 'ul', 'div', 'p', 'span')
203-
if child.tag in list_item_tags:
204-
paragraph = __extract_list_item_text_recusive(child)
205-
if len(paragraph) > 0:
206-
tem_json = json.dumps(paragraph).replace('$br$\"}', '\"}')
207-
new_paragraph = json.loads(tem_json)
208-
text_paragraph.append(new_paragraph)
202+
# list_item_tags = ('li', 'dd', 'dt', 'ul', 'div', 'p', 'span')
203+
# if child.tag in list_item_tags:
204+
# 去掉if限制条件,允许非标准结构的列表通过
205+
paragraph = __extract_list_item_text_recusive(child)
206+
if len(paragraph) > 0:
207+
tem_json = json.dumps(paragraph).replace('$br$\"}', '\"}')
208+
new_paragraph = json.loads(tem_json)
209+
text_paragraph.append(new_paragraph)
209210

210211
for n, item in enumerate(text_paragraph):
211212
tem_json = json.dumps(item).replace('$br$', '\\n\\n')

tests/llm_web_kit/extractor/assets/extractor_chain_input/good_data/html/math_mathjax_mock.html

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

tests/llm_web_kit/extractor/assets/extractor_chain_input/good_data/html_data_input.jsonl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,4 +100,5 @@
100100
{"track_id": "test_mjx_container", "dataset_name": "test_mjx_container", "url": "https://test.com","data_source_category": "HTML", "path":"testmathjax.html", "file_bytes": 1000, "page_layout_type":"artical", "meta_info": {"input_datetime": "2020-01-01 00:00:00"}}
101101
{"track_id": "test_word_press", "dataset_name": "test_word_press", "url": "https://test.com","data_source_category": "HTML", "path":"word_press.html", "file_bytes": 1000, "page_layout_type":"artical", "meta_info": {"input_datetime": "2020-01-01 00:00:00"}}
102102
{"track_id": "test_ascii_delimiter", "dataset_name": "test_ascii_delimiter", "url": "https://montalk.net/notes/342/tuning-forks-and-megalithic-technology","data_source_category": "HTML", "path":"math_test_ascii_delimiter.html", "file_bytes": 1000, "page_layout_type":"artical", "meta_info": {"input_datetime": "2020-01-01 00:00:00"}}
103-
{"track_id": "test_htmlmath_sub_sup", "dataset_name": "test_htmlmath_sub_sup", "url": "https://cccbdb.nist.gov/compvibs3.asp?casno=123911&charge=0&method=42&basis=0","data_source_category": "HTML", "path":"math_table_title_htmlmath_sub_sup.html", "file_bytes": 1000, "page_layout_type":"artical", "meta_info": {"input_datetime": "2020-01-01 00:00:00"}}
103+
{"track_id": "test_htmlmath_sub_sup", "dataset_name": "test_htmlmath_sub_sup", "url": "https://cccbdb.nist.gov/compvibs3.asp?casno=123911&charge=0&method=42&basis=0","data_source_category": "HTML", "path":"math_table_title_htmlmath_sub_sup.html", "file_bytes": 1000, "page_layout_type":"artical", "meta_info": {"input_datetime": "2020-01-01 00:00:00"}}
104+
{"track_id": "test_mathjax_mock", "dataset_name": "test_mathjax_mock", "url": "http://mathonline.wikidot.com/monotone-sequences-of-real-numbers","data_source_category": "HTML", "path":"math_mathjax_mock.html", "file_bytes": 1000, "page_layout_type":"artical", "meta_info": {"input_datetime": "2020-01-01 00:00:00"}}

tests/llm_web_kit/extractor/html/recognizer/assets/ccmath/math_class_math.html

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

tests/llm_web_kit/extractor/html/recognizer/assets/ccmath/math_class_math_1.html

Whitespace-only changes.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
1 yr = 525600 min
2+
1 yr → 525600 min
3+
4.7 yr → T
4+
T
5+
T
6+
4.7 yr → 2470320 min
7+
4.7 years = 2470320 minutes
8+
4.7 yr ≅ 2470320 min

0 commit comments

Comments
 (0)