跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

网页内容变更检测与精准差异定位教程

本文介绍如何通过行级差异比对替代全页哈希校验,实现对目标网页(如新闻列表页)新增文章的精准检测,避免因页眉、时间戳等动态元素导致的误报,并提供可落地的 Python 实现方案。 本文介绍如何通过行级差异比对替代全页哈希校验,实现对目标网页(如新闻列表页)新增文章的初步检测,避免因页眉、时间戳等动态元素导致的误报,并提供可落地的 python 实现方案。 在网页变更监控场景中,直接对整页 HTML 进行 SHA224 哈希比对虽实现简单,但极易产生大量误报——例如页面嵌入的实时时间戳、广告位、统计脚本、CDN 缓存标识或响应头注入的动态元信息,都会导致每次请求返回的 HTML 字符串不同,即使核心内容(如文章列表)未变。根本问题不在于“子域名 vs 高层域名”,而在于 缺乏语义感知的变更过滤能力 。 更稳健的思路是:将 HTML 视为文本序列,逐行比对历史快照与当前快照,识别实际发生变动的行号范围,再结合网页结构特征(如
区块、特定 class 的
容器)聚焦分析关键区域。Python 标准库 difflib 正是为此类行级差异分析而生。 以下是一个精简、可扩展的监控脚本示例,已优化原始逻辑缺陷(如重复请求、无异常兜底、缺少内容聚焦):
import difflib import time from urllib.request import urlopen, Request import logging # 配置日志 logging.basicConfig( level=logging.INFO, format='%(asctime)s [%(levelname)s] %(message)s', datefmt='%Y-%m-%d %H:%M:%S' ) def fetch_html(url: str) -> str: """安全获取网页HTML(含UA伪装与异常处理)""" try: req = Request(url, headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'}) with urlopen(req, timeout=10) as response: return response.read().decode('utf-8') except Exception as e: logging.error(f"Failed to fetch {url}: {e}") return "" def extract_content_region(html: str, start_tag: str = " str: """ 粗粒度过滤:提取主体内容区域(如
或ID为'content'的区块) 实际使用时建议用BeautifulSoup精准定位,此处为简化演示 """ start_idx = html.find(start_tag) if start_idx == -1: return html # 退化为全页比对 end_idx = html.find(end_tag, start_idx) return html[start_idx:end_idx + len(end_tag)] if end_idx != -1 else html[start_idx:] def detect_line_changes(old_html: str, new_html: str) -> list: """返回发生变化的行号列表(基于context_diff)""" old_lines = old_html.splitlines(keepends=True) new_lines = new_html.splitlines(keepends=True) diff = difflib.context_diff( old_lines, new_lines, fromfile='old', tofile='new', lineterm='', n=0 # n=0 表示显示所有差异行,不省略上下文 ) changed_lines = set() for line in diff: # 匹配形如 "*** 470,475 ****" 或 "--- 575,580 ----" 的行号标记 if line.startswith('*** ') or line.startswith('--- '): parts = line.strip().split() if len(parts) >= 2 and ',' in parts[1]: try: line_range = parts[1].split(',')[0] changed_lines.add(int(line_range)) except (ValueError, IndexError): continue return sorted(changed_lines) # ===== 主监控逻辑 ===== URL = "https://example-news-site.com/articles/" # 替换为目标URL CHECK_INTERVAL = 60 # 检测间隔(秒),生产环境请勿低于30秒 MAX_RUNTIME = 3600 # 最大运行时长(秒),防无限循环 logging.info(f"Starting monitor for {URL}, interval={CHECK_INTERVAL}s") old_content = fetch_html(URL) if not old_content: logging.error("Initial fetch failed. Exiting.") exit(1) start_time = time.time() while time.time() - start_time < MAX_RUNTIME: time.sleep(CHECK_INTERVAL) new_content = fetch_html(URL) if not new_content: continue # 【关键改进】聚焦主体内容区域,排除页眉页脚干扰 old_main = extract_content_region(old_content) new_main = extract_content_region(new_content) changed_lines = detect_line_changes(old_main, new_main) if changed_lines: logging.info(f"Change detected at lines: {changed_lines}") # 进阶提示:此处可触发BeautifulSoup解析,定位新增
节点 # 例如:soup = BeautifulSoup(new_content, 'html.parser') # new_articles = soup.select('article:not([data-checked])') # ... 标记并通知 else: logging.debug("No content change detected.") old_content = new_content # 更新基准快照
⚠️ 重要注意事项 遵守 robots.txt 与网站条款 :运行前务必检查 https://yoursite.com/robots.txt,确认 User-Agent 是否被允许爬取,且频率符合 Crawl-delay 要求。 合理设置请求间隔 :示例中 CHECK_INTERVAL=60 是最低推荐值;高频轮询(如每秒)极可能被封禁 IP 或触发 WAF。 内容区域需定制化提取 :extract_content_region() 函数仅为示意,真实项目中应使用 BeautifulSoup 精准定位
或 section[data-testid="articles"] 等稳定容器,大幅提升准确率。 增量式优化路径 : 先用行号差异锁定变化范围; 再用 BeautifulSoup 解析该范围内的 DOM,筛选新增的
  • ; 最终提取标题、链接、发布时间,生成结构化变更报告。 此方法将监控粒度从“整页二进制”下沉至“语义区块文本行”,兼顾实现简易性与工程实用性,是构建轻量级网页更新通知系统的核心基础。
  • 相关文章