从Google News RSS Feed提取URL失败,原代码因编码变更失效
之前用于解析Google RSS文章跳转URL的代码已无法运行,推测是Google调整了URL的编码规则。示例失效的Google RSS文章URL:
https://news.google.com/rss/articles/CBMiuwFBVV95cUxPNXpRbXdHR3NzWHRlbk40d1I5OFVIajRHUHBXTUFGc3BMd1gxSEU1ZDlocGFWQXZhWEJYakROLUxQcTBZMElHN1VTdlN2eTV2LWFidnhIOHZEVEYwLVhEalpxRFRXeGhXQlZoNEc4d1AzTWR3YUlUZVAybjZWa2c4MU9kLWU2aEtmNlVnRy1OR3ZLcGd1M0NqVjFxeFRaOE9fWExpa1ZxSFpySnRkallHN3dFMm5nU1BIY18w0gHAAUFVX3lxTE5fZDdFSTQwZGVzb3A1eUdIbzNIa0F0RmZlYUFmR3lPVnRZU09QU2hnelFpNngxVXI1aGlYdWE1dzROcTRXSmw3a1dFZ0c1MDNROUU3enYzSFBPaEdpaHZUR0t1V2lpLWt5UEVEY01TbXRvM243U2p4ZTA3MlBtaU9XYmNqMU11QWdUVkFQRmk5RXJRb2Jwa3p0NUptUlpZanpNZVQxaVk2NkJVdU9kRmxlakVPOEx6ZFlIcG9oaWJMaA?oc=5
原失效代码
import re import base64 import functools _ENCODED_URL_PREFIX = "https://news.google.com/rss/articles/" _ENCODED_URL_RE = re.compile(fr"^{re.escape(_ENCODED_URL_PREFIX)}(?P<encoded_url>[^?]+)") _DECODED_URL_RE = re.compile(rb'^\x08\x13".+?(?P<primary_url>http[^\xd2]+)\xd2\x01') class SomeClass: @functools.lru_cache(2048) def _decode_google_news_url(self, url: str) -> str: match = _ENCODED_URL_RE.match(url) encoded_text = match.groupdict()["encoded_url"] # type: ignore encoded_text += "===" # 修复填充问题 decoded_text = base64.urlsafe_b64decode(encoded_text) match = _DECODED_URL_RE.match(decoded_text) primary_url = match.groupdict()["primary_url"] # type: ignore primary_url = primary_url.decode() return primary_url
问题分析
原代码依赖固定的二进制前缀\x08\x13"和分隔符\xd2\x01定位原始URL,但Google修改了编码后的二进制结构,导致正则表达式无法匹配目标内容,最终解码失败。
修复后的代码
import re import base64 import functools _ENCODED_URL_PREFIX = "https://news.google.com/rss/articles/" _ENCODED_URL_RE = re.compile(fr"^{re.escape(_ENCODED_URL_PREFIX)}(?P<encoded_url>[^?]+)") # 调整正则,直接匹配http开头到第一个终止符\x01的内容 _DECODED_URL_RE = re.compile(rb'http[^\x01]+') class SomeClass: @functools.lru_cache(2048) def _decode_google_news_url(self, url: str) -> str: match = _ENCODED_URL_RE.match(url) if not match: raise ValueError("无效的Google新闻RSS URL") encoded_text = match.groupdict()["encoded_url"] # 规范处理base64填充,确保长度为4的倍数 padding = 4 - len(encoded_text) % 4 if padding != 4: encoded_text += "=" * padding decoded_text = base64.urlsafe_b64decode(encoded_text) # 查找并提取第一个有效原始链接 match = _DECODED_URL_RE.search(decoded_text) if not match: raise ValueError("无法从编码内容中提取原始URL") primary_url = match.group().decode() return primary_url
修复说明
- 正则表达式调整:不再依赖固定二进制前缀,改为直接匹配
http开头的内容直到第一个终止符\x01,适配Google新的编码结构。 - Base64填充优化:替换原有的强行添加
===的方式,根据长度计算所需填充数,处理更规范。 - 异常处理增强:添加匹配失败时的异常抛出,避免程序崩溃,同时提供明确的错误信息。
内容的提问来源于stack exchange,提问作者ALTAF HUSSAIN

