4.7 KiB
name, description, version, tags
| name | description | version | tags | ||||
|---|---|---|---|---|---|---|---|
| wechat-article-extraction | Extract full article content from WeChat public account (微信公众号) articles at mp.weixin.qq.com. Plain requests with desktop UA works; all "smarter" tools fail. | 1.0 |
|
WeChat Article Extraction (mp.weixin.qq.com)
Extract full article content from WeChat public account (微信公众号) articles.
What Works
Plain requests with desktop Chrome User-Agent. The full article HTML is server-rendered and included in the initial response — no JS rendering needed.
import requests, re, html
session = requests.Session()
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
}
resp = session.get(url, headers=headers, timeout=20)
content = resp.text
# Title
title = re.search(r'var\s+msg_title\s*=\s*["\'](.+?)["\']', content)
title_text = html.unescape(title.group(1)) if title else None
# Article body
body = re.search(r'id="js_content"[^>]*>(.*?)</div>', content, re.DOTALL)
text = re.sub(r'<[^>]+>', '\n', body.group(1))
text = html.unescape(text)
text = re.sub(r'\n{3,}', '\n\n', text).strip()
What Does NOT Work
| Tool | Result | Reason |
|---|---|---|
Jina Reader (r.jina.ai) |
❌ CAPTCHA block | Detected as bot, returns "环境异常" |
| Crawl4AI (Playwright) | ❌ Timeout | Anti-bot blocks js_content selector from appearing |
| Browser automation | ❌ Sandbox/CAPTCHA | Chrome sandbox issues + verification wall |
| Google Cache | ❌ No cache | Google hasn't cached most WeChat articles |
| Mobile UA (MicroMessenger) | ❌ Empty content | Triggers anti-bot, content hidden behind JS |
| SerpAPI search | ❌ No results | WeChat articles rarely indexed |
Extraction Strategy (Two Patterns)
WeChat articles come in two rendering variants. Try Pattern 1 first, fall back to Pattern 2.
Pattern 1: Server-rendered HTML (js_content div)
Most common. The full article HTML is in <div id="js_content">...</div>.
body = re.search(r'id="js_content"[^>]*>(.*?)</div>', content, re.DOTALL)
if body and body.group(1).strip():
text = re.sub(r'<br\s*/?>', '\n', body.group(1))
text = re.sub(r'<p[^>]*>', '\n', text)
text = re.sub(r'</p>', '', text)
text = re.sub(r'<[^>]+>', '', text)
text = html.unescape(text)
Pattern 2: JS-string embedded (window.msg_title)
Some shorter articles embed the ENTIRE content inside a JS string assignment.
The <div id="js_content"> is empty — content is populated dynamically by JS.
The article text lives in window.msg_title = '...' with \\n for newlines
and \\' for escaped quotes.
# Try Pattern 2 when js_content is empty
title_match = re.search(r"window\.msg_title\s*=\s*window\.title\s*=\s*'(.+?)'\s*\|\|\s*", content, re.S)
if not title_match:
title_match = re.search(r"window\.msg_title\s*=\s*window\.title\s*=\s*'(.+?)';(?:\s*$|\s*\n)", content, re.S)
if title_match:
article = title_match.group(1).replace('\\n', '\n').replace("\\'", "'").replace('\\\\', '\\')
# First line is the title, rest is body
lines = article.strip().split('\n')
title = lines[0]
body = '\n'.join(lines[1:])
The msg_title variable is also the source of the <title> tag — it's a single JS string
that contains the full article text. The regex MUST use ' (single quotes) because WeChat
uses single-quoted JS strings for this value.
Key Patterns
- Title:
var msg_title = "...";— HTML-escaped, usehtml.unescape() - Description:
var msg_desc = "..."; - Body (Pattern 1):
<div id="js_content">...</div>— server-rendered HTML with inline tags - Body (Pattern 2):
window.msg_title = '...\\n...';— JS-embedded,\\nfor newlines - Images:
data-srcattribute (notsrc) — WeChat lazy-loads images - Author:
var nickname = "...";in JS
Pitfalls
- Desktop UA is critical — mobile/bot user agents trigger the verification wall even with
requests - Pattern 2:
js_contentdiv exists but is empty — don't assume failure; checkwindow.msg_titleas fallback - Pattern 2: use single quotes in regex — WeChat uses
'...'not"..."formsg_titleJS strings - Pattern 2:
\\nnot\n— the JS string literal has escaped newlines; use.replace('\\n', '\n') - Image URLs need transformation —
data-srcvalues are relative (//mmbiz.qpic.cn/...), prependhttps: - Don't over-engineer — sophisticated tools (Crawl4AI, CamouFox) are overkill and actually worse for WeChat articles