wechat-article-parser 0.0.4__tar.gz → 0.0.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wechat-article-parser
3
- Version: 0.0.4
3
+ Version: 0.0.6
4
4
  Summary: WeChat MP article parser - extract metadata and content from WeChat public account articles
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -76,6 +76,7 @@ print(result.article_markdown)
76
76
  - `timeout`:请求超时时间,单位秒,默认 15
77
77
  - `user_agent`:自定义 User-Agent,不传则使用内置默认值
78
78
  - `proxy`:HTTP/HTTPS 代理地址,不传则直连
79
+ - `include_raw_html`:是否在返回结果中带上原始 HTML 网页源代码,默认 `False`
79
80
 
80
81
  ```python
81
82
  result = parse(
@@ -83,6 +84,7 @@ result = parse(
83
84
  timeout=30,
84
85
  user_agent="MyBot/1.0",
85
86
  proxy="http://user:pass@127.0.0.1:7890",
87
+ include_raw_html=True,
86
88
  )
87
89
  ```
88
90
 
@@ -108,6 +110,7 @@ result = parse(
108
110
  | `article_description` | `str` | 文章摘要 |
109
111
  | `article_markdown` | `str` | 文章正文的 Markdown 内容 |
110
112
  | `article_publish_time` | `int` | 发布时间(Unix 时间戳) |
113
+ | `raw_html` | `str` | 原始 HTML 网页源代码(仅在 `include_raw_html=True` 时填充,否则为空字符串) |
111
114
  | `images` | `list[str]` | 文章中提取的所有图片链接 |
112
115
  | `is_valid` | `bool` | 关键字段是否全部解析成功(属性) |
113
116
 
@@ -206,6 +209,12 @@ pytest tests/test_parser.py::test_fetch_all -s --url "https://mp.weixin.qq.com/s
206
209
  pytest tests/test_parser.py::test_fetch_markdown -s --url "https://mp.weixin.qq.com/s/xxxxx"
207
210
  ```
208
211
 
212
+ ### 测试单个链接 - 打印原始 HTML 网页源代码
213
+
214
+ ```bash
215
+ pytest tests/test_parser.py::test_fetch_raw_html -s --url "https://mp.weixin.qq.com/s/xxxxx"
216
+ ```
217
+
209
218
  ### 通过 HTTP 代理运行测试
210
219
 
211
220
  所有测试命令都支持 `--proxy` 参数,传入后所有请求都会经由该代理;不传则直连。常用于 IP 被微信限流时换出口:
@@ -61,6 +61,7 @@ print(result.article_markdown)
61
61
  - `timeout`:请求超时时间,单位秒,默认 15
62
62
  - `user_agent`:自定义 User-Agent,不传则使用内置默认值
63
63
  - `proxy`:HTTP/HTTPS 代理地址,不传则直连
64
+ - `include_raw_html`:是否在返回结果中带上原始 HTML 网页源代码,默认 `False`
64
65
 
65
66
  ```python
66
67
  result = parse(
@@ -68,6 +69,7 @@ result = parse(
68
69
  timeout=30,
69
70
  user_agent="MyBot/1.0",
70
71
  proxy="http://user:pass@127.0.0.1:7890",
72
+ include_raw_html=True,
71
73
  )
72
74
  ```
73
75
 
@@ -93,6 +95,7 @@ result = parse(
93
95
  | `article_description` | `str` | 文章摘要 |
94
96
  | `article_markdown` | `str` | 文章正文的 Markdown 内容 |
95
97
  | `article_publish_time` | `int` | 发布时间(Unix 时间戳) |
98
+ | `raw_html` | `str` | 原始 HTML 网页源代码(仅在 `include_raw_html=True` 时填充,否则为空字符串) |
96
99
  | `images` | `list[str]` | 文章中提取的所有图片链接 |
97
100
  | `is_valid` | `bool` | 关键字段是否全部解析成功(属性) |
98
101
 
@@ -191,6 +194,12 @@ pytest tests/test_parser.py::test_fetch_all -s --url "https://mp.weixin.qq.com/s
191
194
  pytest tests/test_parser.py::test_fetch_markdown -s --url "https://mp.weixin.qq.com/s/xxxxx"
192
195
  ```
193
196
 
197
+ ### 测试单个链接 - 打印原始 HTML 网页源代码
198
+
199
+ ```bash
200
+ pytest tests/test_parser.py::test_fetch_raw_html -s --url "https://mp.weixin.qq.com/s/xxxxx"
201
+ ```
202
+
194
203
  ### 通过 HTTP 代理运行测试
195
204
 
196
205
  所有测试命令都支持 `--proxy` 参数,传入后所有请求都会经由该代理;不传则直连。常用于 IP 被微信限流时换出口:
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "wechat-article-parser"
7
- version = "0.0.4"
7
+ version = "0.0.6"
8
8
  description = "WeChat MP article parser - extract metadata and content from WeChat public account articles"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -26,3 +26,4 @@ packages = ["src/wechat_article_parser"]
26
26
 
27
27
  [tool.pytest.ini_options]
28
28
  asyncio_mode = "auto"
29
+ pythonpath = ["src"]
@@ -46,6 +46,9 @@ class ArticleResult:
46
46
  article_markdown: str = ""
47
47
  article_publish_time: int = 0
48
48
 
49
+ # 原始 HTML 网页源代码(仅在调用方显式要求时才会填充)
50
+ raw_html: str = ""
51
+
49
52
  # 文章中提取的图片列表
50
53
  images: list[str] = field(default_factory=list)
51
54
 
@@ -38,6 +38,15 @@ def _to_markdown(html: str) -> str:
38
38
  return _MarkdownConverter().convert(html)
39
39
 
40
40
 
41
+ def _to_markdown_plain(html: str) -> str:
42
+ """用于纯文本类内容(纯文本/转载/视频/轮播/全屏)的转换。
43
+
44
+ 这些内容由 <br> 承载换行,必须保留;故使用未覆写 convert_p 的基础转换器,
45
+ 避免 _MarkdownConverter 折叠空白时把换行一并吞掉。
46
+ """
47
+ return MarkdownConverter().convert(html)
48
+
49
+
41
50
  # ---------------------------------------------------------------------------
42
51
  # 文本解码辅助函数
43
52
  # ---------------------------------------------------------------------------
@@ -317,7 +326,7 @@ def _extract_repost_content(content_tag: Tag, result: ArticleResult) -> None:
317
326
  if href:
318
327
  html_content += f'<p><a href="{href}">查看原文</a></p>'
319
328
 
320
- result.article_markdown = _to_markdown(html_content)
329
+ result.article_markdown = _to_markdown_plain(html_content)
321
330
 
322
331
 
323
332
  def _extract_plain_text_content(soup: BeautifulSoup, result: ArticleResult) -> None:
@@ -331,7 +340,7 @@ def _extract_plain_text_content(soup: BeautifulSoup, result: ArticleResult) -> N
331
340
  if m:
332
341
  text = _decode_text(m.group(1), preserve_newlines=True)
333
342
  text = unquote(text)
334
- result.article_markdown = _to_markdown(f"<p>{text}</p>")
343
+ result.article_markdown = _to_markdown_plain(f"<p>{text}</p>")
335
344
 
336
345
 
337
346
  def _extract_swiper_content(soup: BeautifulSoup, result: ArticleResult) -> None:
@@ -350,7 +359,7 @@ def _extract_swiper_content(soup: BeautifulSoup, result: ArticleResult) -> None:
350
359
  html_parts.append(f"<p>{text}</p>")
351
360
 
352
361
  if html_parts:
353
- result.article_markdown = _to_markdown("".join(html_parts))
362
+ result.article_markdown = _to_markdown_plain("".join(html_parts))
354
363
 
355
364
 
356
365
  def _extract_fullscreen_content(soup: BeautifulSoup, result: ArticleResult) -> None:
@@ -371,9 +380,10 @@ def _extract_fullscreen_content(soup: BeautifulSoup, result: ArticleResult) -> N
371
380
  html_parts.append(f'<img src="{img}" /><br>')
372
381
 
373
382
  # 从 text_page_info.content_noencode 或 content 中提取文本
383
+ # 文本可能被 JsDecode('...') 包裹,也可能是裸的单引号字符串,两种都要兼容
374
384
  for field in ("content_noencode", "content"):
375
385
  m = re.search(
376
- rf"{field}:\s*JsDecode\('(.*?)'\)",
386
+ rf"{field}:\s*(?:JsDecode\(\s*)?'(.*?)'",
377
387
  script.text,
378
388
  re.DOTALL,
379
389
  )
@@ -384,7 +394,7 @@ def _extract_fullscreen_content(soup: BeautifulSoup, result: ArticleResult) -> N
384
394
  break
385
395
 
386
396
  if html_parts:
387
- result.article_markdown = _to_markdown("".join(html_parts))
397
+ result.article_markdown = _to_markdown_plain("".join(html_parts))
388
398
  return
389
399
 
390
400
 
@@ -482,6 +492,7 @@ def parse(
482
492
  timeout: int = _TIMEOUT,
483
493
  user_agent: str | None = None,
484
494
  proxy: str | None = None,
495
+ include_raw_html: bool = False,
485
496
  ) -> ArticleResult:
486
497
  """抓取并解析微信公众号文章(同步方式)。
487
498
 
@@ -490,6 +501,7 @@ def parse(
490
501
  timeout: 请求超时时间(秒)。
491
502
  user_agent: 自定义 User-Agent,不传则使用内置默认值。
492
503
  proxy: HTTP/HTTPS 代理地址,例如 "http://user:pass@host:port",不传则直连。
504
+ include_raw_html: 是否在结果中返回原始 HTML 网页源代码,默认不返回。
493
505
 
494
506
  Returns:
495
507
  包含解析数据的 ArticleResult。
@@ -503,7 +515,10 @@ def parse(
503
515
  ) as client:
504
516
  response = client.get(url)
505
517
  response.raise_for_status()
506
- return _parse_html(url, response.text)
518
+ result = _parse_html(url, response.text)
519
+ if include_raw_html:
520
+ result.raw_html = response.text
521
+ return result
507
522
 
508
523
 
509
524
  async def parse_async(
@@ -512,6 +527,7 @@ async def parse_async(
512
527
  timeout: int = _TIMEOUT,
513
528
  user_agent: str | None = None,
514
529
  proxy: str | None = None,
530
+ include_raw_html: bool = False,
515
531
  ) -> ArticleResult:
516
532
  """抓取并解析微信公众号文章(异步方式)。
517
533
 
@@ -520,6 +536,7 @@ async def parse_async(
520
536
  timeout: 请求超时时间(秒)。
521
537
  user_agent: 自定义 User-Agent,不传则使用内置默认值。
522
538
  proxy: HTTP/HTTPS 代理地址,例如 "http://user:pass@host:port",不传则直连。
539
+ include_raw_html: 是否在结果中返回原始 HTML 网页源代码,默认不返回。
523
540
 
524
541
  Returns:
525
542
  包含解析数据的 ArticleResult。
@@ -533,4 +550,7 @@ async def parse_async(
533
550
  ) as client:
534
551
  response = await client.get(url)
535
552
  response.raise_for_status()
536
- return _parse_html(url, response.text)
553
+ result = _parse_html(url, response.text)
554
+ if include_raw_html:
555
+ result.raw_html = response.text
556
+ return result
@@ -14,6 +14,7 @@ TEST_URLS = [
14
14
  "https://mp.weixin.qq.com/s/sfwGsafriO9sm6PfbQVXhw",
15
15
  "https://mp.weixin.qq.com/s/h8E6riExCaH2Znmnprj-WQ",
16
16
  "https://mp.weixin.qq.com/s/ySQdtsRlRmAl_skdc5HQ-A",
17
+ "https://mp.weixin.qq.com/s/MnkArbYQNp3tF29gujMUnQ",
17
18
  ]
18
19
 
19
20
 
@@ -127,3 +128,17 @@ def test_fetch_markdown(url: str, proxy: str | None) -> None:
127
128
  """
128
129
  result = parse(url, proxy=proxy)
129
130
  print(f"\n{result.article_markdown}")
131
+
132
+
133
+ def test_fetch_raw_html(url: str, proxy: str | None) -> None:
134
+ """抓取指定 URL 并打印原始 HTML 网页源代码。
135
+
136
+ 用法: pytest tests/test_parser.py::test_fetch_raw_html -s --url <URL> [--proxy <PROXY>]
137
+ """
138
+ result = parse(url, proxy=proxy, include_raw_html=True)
139
+ assert result.raw_html, "raw_html should not be empty when include_raw_html=True"
140
+ print(f"\n{'='*60}")
141
+ print(f"URL: {url}")
142
+ print(f"raw_html长度: {len(result.raw_html)}")
143
+ print(f"{'='*60}")
144
+ print(result.raw_html)