在电商爆款素材整理里,一个很常见的需求是把商品页面里的商品 ID、主图原图批量提取出来,再统一生成 9:16 的封面图。很多人一上来就搜爬虫教程,但如果你仔细分析会发现,真正要处理的页面并不是要靠程序去自动抓取的,而是浏览器里已经正常打开过的页面。把页面保存到本地后再做解析,整个流程不涉及自动请求第三方服务器,这就是“本地解析非爬虫”的含义。这篇文章围绕多多平台爆款商品页的素材整理场景,把完整链路拆开讲:从手动保存 HTML,到用正则和 BeautifulSoup 提取商品 ID,到还原原图 URL 并批量下载,最后用 Pillow 或 Playwright 生成 1080x1920 的 9:16 精准截图。文中的代码可以直接复制到本地跑通,实际页面结构有差异时再调整选择器和参数即可。
1. 先理解本地解析:不是爬虫,而是处理已打开的页面
1.1 本地解析和网络爬虫的边界
网络爬虫的定义是:按照一定规则,由程序主动向目标服务器发起 HTTP 请求,批量获取网页并抽取数据的自动化程序。它的关键特征是“主动请求”,程序拿着 URL 列表去访问服务器,因此会带来访问频率、服务器压力、反爬策略等一系列问题。
本地解析则完全不同。它的输入是用户在自己浏览器里已经打开过的页面,通过“另存为”或复制渲染后 DOM 的方式保存成本地 HTML 文件。解析程序只读取本地文件,不主动去请求目标网站的 URL。也就是说,网络请求这一步发生在浏览器手工操作阶段,之后的分析和提取全部在本地完成。
很多人搜爬虫教程,实际上要解决的需求只是把已经打开的页面里的信息整理出来。这类需求用本地解析更合适:不需要处理验证码、不需要担心 IP 被封、不依赖网络波动,解析过程反复运行都不会给对方服务器造成压力。
| 对比维度 | 网络爬虫 | 本地解析 |
|---|---|---|
| 输入 | URL 列表 | 本地 HTML 文件 |
| 请求方式 | 程序自动发起 HTTP 请求 | 浏览器手动打开后保存 |
| 触发频率 | 可能高频访问目标服务器 | 不产生额外网络请求 |
| 合规风险 | 较高,容易被平台认定为爬虫 | 相对低,但仍需遵守数据使用边界 |
| 稳定性 | 受反爬、验证码、封 IP 影响 | 稳定,不受对方服务器限制 |
1.2 这个工具要解决的三个功能点
第一个功能点是批量提取商品 ID。商品 ID 是后续做商品比对、库存核对、素材关联的关键字段,它在页面里可能出现在链接参数、元素 data 属性、内嵌 JSON 数据等多个位置,人工一个个复制效率很低。
第二个功能点是提取原图 URL。页面里展示给用户看的图片往往是 CDN 动态压缩后的缩略图,URL 里带了一堆尺寸和处理参数。直接保存缩略图,生成封面时画质不够。因此需要把缩略图 URL 还原成原图 URL,再批量下载。
第三个功能点是生成 9:16 精准截图。现在很多电商素材要用于短视频平台和内容卡片,宽高比要求 9:16,标准尺寸是 1080x1920。直接拿商品图缩放会变形或出现黑边,需要用图像处理方式做背景填充或页面截图。
1.3 整体流程与技术选型
完整流程可以拆成六步:浏览器打开商品页面,手动保存 HTML 文件到 pages 目录,本地脚本读取所有 HTML,用正则或 DOM 解析提取商品 ID 和图片 URL,下载原图到 images 目录,最后生成 9:16 封面到 covers 目录。
技术选型使用 Python 3,原因是生态成熟、代码量少。解析层用 BeautifulSoup 加 lxml,BeautifulSoup 容错性好,lxml 解析速度快;图片下载用 requests,处理简单直接;图像合成用 Pillow,能满足大部分封面生成需求;如果需要对页面本身截图,则用 Playwright 做无头浏览器渲染。
2. 环境准备与页面素材获取:依赖、目录和手动保存
2.1 Python 版本与依赖安装
建议使用 Python 3.9 及以上版本。先创建虚拟环境,避免依赖冲突:
python -m venv .venvWindows 下激活虚拟环境:
.venv\Scripts\activatemacOS 或 Linux 下激活:
source .venv/bin/activate安装基础依赖:
pip install beautifulsoup4 lxml requests pillow如果后面需要做页面截图,再安装 Playwright:
pip install playwright playwright install chromium各依赖的用途如下表所示:
| 包 | 版本建议 | 用途 |
|---|---|---|
| beautifulsoup4 | 4.12+ | HTML 解析与 DOM 遍历 |
| lxml | 4.9+ | 底层解析器,性能更好 |
| requests | 2.31+ | 下载原图 |
| pillow | 10.x | 图像缩放、模糊、合成 |
| playwright | 1.x | 无头浏览器页面截图 |
安装完成后,可以用下面的命令确认环境正常:
python -c "import bs4, lxml, requests, PIL; print('ok')"2.2 项目目录结构规划
建议按输入、中间产物、输出分开存放,避免处理大量文件时目录混乱:
product_parser/ ├── pages/ # 手动保存的 HTML 文件 ├── images/ # 下载的原图 ├── covers/ # 生成的 9:16 封面图 ├── output/ # 导出的表格和日志 └── parse_products.py创建目录:
mkdir pages images covers output代码脚本 parse_products.py 放在项目根目录,页面文件全部放进 pages,后续脚本只扫描这个目录,逻辑清晰。
2.3 手动保存 HTML 的正确姿势
在浏览器中打开商品页面后,在页面空白处右键选择“另存为”,保存类型可以选“网页,仅 HTML”或“网页,全部”。只要 HTML 里包含商品 ID 和图片地址,选“仅 HTML”就够用;如果图片是懒加载模式,真实地址往往在>import re def extract_ids_by_regex(html: str) -> set: """从 HTML 文本中提取商品 ID,返回去重后的集合""" patterns = [ r"(?:goods_id|goodsId|productId|item_id)[\"':= ]+(\d{10,20})", r"goods_id[=/](\d{10,20})", r"goodsId[\"']?\s*:\s*[\"']?(\d{10,20})", ] ids = set() for pattern in patterns: ids.update(re.findall(pattern, html)) return ids
第一行正则里的[\"':= ]+表示属性名和值之间可以有冒号、引号、等号或空格,这样能兼容goods_id=123、"goodsId":"123"、goods_id: 123多种写法。第二、第三行是针对 URL 路径形式和 JSON 键值形式的补充。
3.3 用 BeautifulSoup 按 DOM 结构提取
正则的缺点是理解起来不直观,而且如果 HTML 结构不规范,匹配容易出错。DOM 方式更适合处理嵌套结构,也更利于后续维护:
from bs4 import BeautifulSoup def extract_ids_by_dom(html: str) -> set: """根据 DOM 结构和元素属性提取商品 ID""" soup = BeautifulSoup(html, "lxml") ids = set() # 从 a 标签的 href 参数中提取 for a in soup.find_all("a", href=True): href = a["href"] if "goods" in href.lower() or "product" in href.lower(): match = re.search(r"(?:goods_id|goodsId|productId|item_id)=(\d{10,20})", href) if match: ids.add(match.group(1)) # 从 data 属性中提取 for selector in ["[data-goods-id]", "[data-goods_id]", "[data-product-id]"]: for elem in soup.select(selector): value = elem.get("data-goods-id") or elem.get("data-goods_id") or elem.get("data-product-id") if value and value.isdigit() and len(value) >= 10: ids.add(value) return ids这段代码里有两个值得注意的点。第一,遍历 a 标签时先判断链接里是否包含 goods 或 product 关键字,再提取数字,能避免把站内其他链接的参数也捞进来。第二,data 属性选择器写全了多种命名风格,实际页面用下划线还是驼峰,都能命中。
3.4 去重、校验与导出 CSV
一个 HTML 文件里可能多次出现同一个商品 ID,多个 HTML 文件之间也可能重复。把结果放进 set 集合里,天然去重。导出 CSV 时用 utf-8-sig 编码,是为了让 Excel 直接打开不乱码:
import csv from pathlib import Path def load_html(file_path: Path) -> str: """读取 HTML 文件,按 UTF-8、GBK 顺序尝试解码""" data = file_path.read_bytes() for encoding in ("utf-8", "gbk", "gb18030"): try: return data.decode(encoding) except UnicodeDecodeError: continue return data.decode("utf-8", errors="ignore") def main(): all_ids = set() for html_file in Path("pages").glob("*.html"): html = load_html(html_file) ids = extract_ids_by_regex(html) | extract_ids_by_dom(html) print(f"{html_file.name}: {len(ids)} 个商品 ID") all_ids.update(ids) print(f"总计: {len(all_ids)} 个去重后的商品 ID") with open("output/product_ids.csv", "w", newline="", encoding="utf-8-sig") as f: writer = csv.writer(f) writer.writerow(["商品 ID"]) for pid in sorted(all_ids): writer.writerow([pid]) if __name__ == "__main__": main()这里用两个提取函数取并集:正则能扫到 script 里的 JSON,DOM 能处理结构清晰的标签属性,两者互补。运行后检查 output/product_ids.csv,如果一行一个 ID、数量合理,说明解析成功。
4. 原图 URL 还原与批量下载:从缩略图找回高清图
4.1 页面里的图片为什么不是原图
电商平台的图片 URL 通常由 CDN 动态生成。页面为了加载速度,会把图片压缩成指定尺寸,URL 里会带?x-oss-process=image/resize,w_400这样的处理参数,或者文件名里带_400x400之类的尺寸后缀。如果直接把这种图保存下来,画质偏低,放到 9:16 封面里会明显发糊。
原图还原的本质,是理解 CDN 的 URL 规则:哪些参数和尺寸后缀是缩略图特有的,去掉之后是否还能访问到原始图片。不同平台的规则不一样,但思路是通用的。
4.2 还原原图 URL 的通用规律
from urllib