应提取正文区域(如或.content)的清洗后纯文本再哈希,而非整页html;用beautifulsoup定位关键节点、get_text()清洗空白、sqlite3存哈希值,并设随机延迟与user-agent防反爬。

为什么直接比对HTML字符串容易误报
网页里常有动态插入的时间戳、广告位ID、埋点脚本等随机内容,哪怕正文没变,html.replace() 也救不了——整页字符串一差就标为“更新”。更糟的是,有些站点用CDN返回带毫秒级 ETag 或 Last-Modified 头,但实际内容压根没动。
真正该比的,是「人眼可见的正文稳定性」。做法是:先清洗(去掉 script、style、注释、空白折叠),再提取纯文本或关键DOM节点的文本摘要,最后算 hashlib.sha256()。这样即使广告位换了位置,只要正文段落没增删,哈希值就不变。
如何用requests + BeautifulSoup做稳定内容提取
别用 response.text 原样哈希。要定位到关键词最可能所在的区域,比如新闻页的 <article></article>、博客正文的 class="content"、或通过XPath匹配含关键词的父容器。
- 用
BeautifulSoup(response.content, "lxml")解析,避免编码错乱;response.text可能被自动解码损坏二进制字符 - 优先选
soup.select("main article, .post-content, #content")[0]而不是soup.body—— 减少干扰块 - 清洗后调用
.get_text(strip=True, separator=" "),再用正则re.sub(r"\s+", " ", text)合并空白,防因换行缩进导致哈希不同 - 若页面含大量用户评论(易变),加条件过滤:
[t for t in texts if not t.startswith("发表评论") and len(t) > 10]
Hash存储与增量对比怎么不丢数据
本地存哈希不能只写个txt——并发爬取或程序崩溃时容易写坏。推荐用轻量方案:
- 用
sqlite3建表:CREATE TABLE IF NOT EXISTS page_hash (url TEXT PRIMARY KEY, hash TEXT, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP) - 每次请求前先查库:
SELECT hash FROM page_hash WHERE url = ?,有结果才比对;无则视为首次抓取,直接存入 - 更新时用
INSERT OR REPLACE INTO,避免手动写UPDATE+INSERT分支逻辑出错 - 别把哈希存成 base64——直接存十六进制字符串(
hash_obj.hexdigest()),可读、可索引、无编码开销
监控频率和反爬绕过要注意什么
高频请求不等于高实效。很多网站内容几小时才更新一次,每分钟刷一次只会触发 429 Too Many Requests 或 IP 封禁。
- 首次运行后,记录
updated_at时间戳,下次只对超过 2 小时未更新的 URL 发起请求 - 加
time.sleep(random.uniform(5, 15)),别用固定间隔——规律性请求最容易被识别 - 必须设
headers={"User-Agent": "Mozilla/5.0 (X11; Linux x86_64)..."},否则多数站点直接返回403 - 如果目标站用 JavaScript 渲染正文(如 React SPA),
requests拿不到真实内容,得换playwright或selenium,但哈希逻辑不变——只是输入源从response.content换成page.content()
真正难的不是算哈希,是判断「哪段 HTML 才算有效内容」。同一套代码在知乎专栏和政府公报页上表现可能完全不同——得为每个目标站点单独调试选择器,这点没法偷懒。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











