ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Python本地网页解析实战:批量提取商品ID与原图并生成9:16竖版素材

Python本地网页解析实战:批量提取商品ID与原图并生成9:16竖版素材 1. 为什么要做“多多爆款”本地网页解析工具最近一段时间不少做电商选品、素材整理、内容运营的朋友都在找一类工具把商品详情页里的商品 ID、标题、原图批量提取出来再配合 9:16 竖版截图整理成素材库。市面上的思路主要分两种一种是直接写爬虫脚本去抓平台数据另一种是解析浏览器保存下来的本地网页文件。本文要讲的是第二种——本地解析方式。相比爬虫本地解析有几个明显优势不主动请求服务器只是读取你浏览器已经打开过的页面源码行为边界更清晰不需要处理登录态、验证码、反爬机制稳定性高只处理自己授权保存的数据合规风险更低通过浏览器“另存为”或开发者工具导出的 HTML本身包含了商品结构、图片链接、价格等关键信息。适用人群也很明确电商运营、自媒体素材整理者、前端开发初学者、对本地文件批处理感兴趣的人。读完本文你将掌握用 Python BeautifulSoup 批量解析本地 HTML 文件从 URL 中批量提取商品 ID提取商品原图地址并处理常见的图片尺寸后缀用 Playwright 实现 9:16 精准截图用 Pillow 做图片比例裁剪得到规范 9:16 素材避开本地解析中的常见坑。2. 环境准备与工具选型2.1 运行环境本文示例以 Windows 10/11 为主macOS 和 Linux 操作逻辑完全一致只是路径写法不同。需要准备以下环境工具版本说明用途Python3.9 及以上运行解析脚本BeautifulSoup44.10解析 HTML 结构lxml 或 html.parser随 bs4 使用HTML 解析器Playwright1.40控制浏览器截图Pillow9.0图片比例裁剪如果版本不确定安装最新稳定版即可。以下命令在命令行中执行pip install beautifulsoup4 lxml playwright pillow playwright install chromium注意Playwright 首次使用需要下载 Chromium 浏览器内核这一步会占用一定网络资源和磁盘空间请耐心等待。2.2 目录结构规划建议按下面的目录结构管理文件goods_parser/ ├── html/ # 存放本地保存的商品页 HTML ├── output/ # 输出 csv / 图片 │ ├── csv/ │ └── screenshots/ ├── parse_goods.py # 批量提取商品ID与原图 ├── screenshot_916.py # 9:16 精准截图 └── crop_916.py # 图片比例裁剪把本地网页文件统一放在html目录下脚本只需要遍历这个目录代码会清晰很多。3. 核心原理解析商品ID、原图与9:16截图3.1 商品ID长在哪里电商商品页的 HTML 中商品 ID 一般有三个位置位置一URL 参数这是最常见的形式例如https://example.com/goods.html?goods_id123456789fromsearch位置二页面 JSON 数据很多页面会把商品信息写在一个script标签里类似window.__INITIAL_STATE__ {goodsId: 123456789, goodsName: xxx}位置三HTML 标签属性例如div classgoods-item>https://img.example.com/abc/123.jpg_400x400.jpg这种地址前一段是原图后一段_400x400.jpg是缩略图后缀。提取原图时需要把尺寸后缀去掉否则拿到的不是清晰大图。常见处理思路是用正则匹配_数字x数字.jpg、_数字x数字.png这类模式并移除。import re def clean_image_url(url: str) - str: url re.sub(r_\dx\d\.(jpg|jpeg|png|webp)$, r.\1, url) return url这个正则会把http://xxx.com/a.jpg_400x400.jpg还原成http://xxx.com/a.jpg。3.3 9:16精准截图的本质9:16 是短视频平台常见的竖版比例。精准截图有两种含义按视口比例截图控制浏览器窗口宽高比为 9:16然后对页面内容截图按图片比例裁剪把已经下载的图片裁剪成 9:16 比例。两种方式本文都会讲到。第一种用 Playwright 控制浏览器第二种用 Pillow 处理图片。4. 完整实战批量提取商品ID与原图4.1 准备一个示例HTML文件为了演示我们手动构造一个非常接近真实结构的 HTML 文件放入html/demo1.html!DOCTYPE html html langzh-CN head meta charsetUTF-8 title示例商品页/title /head body div classgoods-info>import re import csv import os import json from bs4 import BeautifulSoup from urllib.parse import urlparse, parse_qs HTML_DIR html OUTPUT_CSV os.path.join(output, csv, goods_info.csv) def extract_goods_id(soup, html_text): 按优先级从不同位置提取商品ID # 1. 从链接 URL 中提取 for a in soup.find_all(a, hrefTrue): href a[href] parsed urlparse(href) query parse_qs(parsed.query) if goods_id in query: return query[goods_id][0] # 兼容 /goods/123456 这类路径 match re.search(r/goods/(\d), href) if match: return match.group(1) # 2. 从 data 属性提取 goods_box soup.find([data-goods-id]) if goods_box: gid str(goods_box.get(data-goods-id)).strip() if gid: return gid # 3. 从页面 JSON 中提取 scripts soup.find_all(script) for script in scripts: script_text script.string or # 这里只做最简单的匹配实际页面可能需要更精细的 JSON 提取 match re.search(rgoodsId\s*:\s*(\d), script_text) if match: return match.group(1) return None def extract_images(soup): 提取图片地址优先取>goods_box soup.find(lambda tag: tag.get(data-goods-id) is not None)extract_images 函数这里处理了两个关键点优先取>python parse_goods.py预期输出类似正在解析: demo1.html 解析完成共处理 1 个文件输出: output/csv/goods_info.csv打开output/csv/goods_info.csv内容应如下source_file,goods_id,title,images demo1.html,987654321,【热卖】便携榨汁杯 家用小型榨汁机,https://img.example.com/goods/1001.jpg|https://img.example.com/goods/1002.jpg|https://img.example.com/goods/1003.jpg到这里批量提取商品 ID 和原图地址的流程就通了。5. 完整实战9:16精准截图5.1 方案一Playwright 按 9:16 视口截图如果你的需求是“按 9:16 比例截取页面某一块区域”推荐用 Playwright。新建screenshot_916.pyimport os from playwright.sync_api import sync_playwright SCREENSHOT_DIR os.path.join(output, screenshots) os.makedirs(SCREENSHOT_DIR, exist_okTrue) # 9:16 视口的常见分辨率 VIEWPORT_WIDTH 900 VIEWPORT_HEIGHT 1600 URLS_DICT { demo1: file:///绝对路径/html/demo1.html, # 可以继续添加更多页面 } def take_916_screenshot(): with sync_playwright() as p: browser p.chromium.launch(headlessTrue) context browser.new_context( viewport{width: VIEWPORT_WIDTH, height: VIEWPORT_HEIGHT}, device_scale_factor1, ) page context.new_page() for name, url in URLS_DICT.items(): print(f正在截图: {name}) page.goto(url, wait_untilload) # 等待图片懒加载完成 page.wait_for_timeout(2000) # 方式1截图整个可视区域 page.screenshot( pathos.path.join(SCREENSHOT_DIR, f{name}_viewport.png), full_pageFalse, ) # 方式2如果只想截取某个元素区域用 element.screenshot goods_box page.query_selector(.goods-info) if goods_box: goods_box.screenshot( pathos.path.join(SCREENSHOT_DIR, f{name}_goodsbox.png) ) browser.close() print(f截图完成输出目录: {SCREENSHOT_DIR}) if __name__ __main__: take_916_screenshot()运行前注意把URLS_DICT中的file:///绝对路径/html/demo1.html换成你自己机器上的绝对路径。运行python screenshot_916.py这里有几个说明viewport{width: 900, height: 1600}正好是 9:16 比例headlessTrue表示无头模式不弹出浏览器窗口wait_for_timeout(2000)是为了等懒加载图片显示具体等待时间按页面情况调整element.screenshot()可以只截取指定的 DOM 元素适合截商品主图、详情模块等局部区域。如果你的 HTML 页面使用了字体、弹窗、动态渲染等特性Playwright 都能正确处理因为它是一个真实的浏览器内核。5.2 方案二Pillow 把普通图片裁剪成 9:16有时候我们已经下载好了商品原图只需要把图片处理成 9:16 竖版用于短视频封面或信息流素材。这时可以直接用 Pillow。新建crop_916.pyimport os from PIL import Image INPUT_DIR input_images OUTPUT_DIR output/crop_916 TARGET_RATIO 9 / 16 def crop_to_9_16(image_path, output_path): img Image.open(image_path) w, h img.size current_ratio w / h if current_ratio TARGET_RATIO: # 图片偏宽以高度为基准裁剪宽度 new_h h new_w int(h * TARGET_RATIO) left (w - new_w) // 2 top 0 else: # 图片偏高以宽度为基准裁剪高度 new_w w new_h int(w / TARGET_RATIO) left 0 top (h - new_h) // 2 box (left, top, left new_w, top new_h) cropped img.crop(box) # 保存时设置质量 cropped.save(output_path, quality95) print(f裁剪完成: {image_path} - {output_path}) print(f原尺寸: {w}x{h}, 新尺寸: {new_w}x{new_h}) def main(): os.makedirs(OUTPUT_DIR, exist_okTrue) if not os.path.exists(INPUT_DIR): print(请先创建 input_images 目录并放入商品图片) return for filename in os.listdir(INPUT_DIR): ext os.path.splitext(filename)[1].lower() if ext not in (.jpg, .jpeg, .png, .webp): continue input_path os.path.join(INPUT_DIR, filename) output_path os.path.join(OUTPUT_DIR, filename) crop_to_9_16(input_path, output_path) if __name__ __main__: main()使用方法创建input_images目录放入需要裁剪的商品图片运行python crop_916.py。裁剪逻辑的核心是先判断原图比例是偏宽还是偏高然后按目标比例裁剪掉多余部分。焦点保持在图片中心区域这是最常见的商品图构图方式。5.3 两种方案怎么选场景推荐方案需要截取页面效果、商品详情模块、动态渲染后的样子Playwright已经有图片文件只需裁剪比例Pillow批量处理几百张图片Pillow 更快页面内容很长需要滚动截取完整详情Playwright 滚动截图如果两种需求都有可以先用 Playwright 截图再用 Pillow 做二次裁剪形成一套完整流水线。6. 本地 HTML 获取的两种安全姿势前面脚本都假设我们已经有了本地 HTML 文件。那么在合规的前提下怎么获取这些本地文件呢6.1 浏览器“另存为”打开商品页按Ctrl S选择“网页仅 HTML”保存到本地即可。这种方式适合少量页面优点是简单直接缺点是页面里的 JS、CSS 可能缺失导致本地打开时样式错乱。不过对于解析商品 ID 和图片地址来说HTML 主体结构通常已足够。6.2 开发者工具复制元素如果你只需要某个商品模块的数据可以这样做在商品页右键 →“检查”在 Elements 面板中找到目标模块右键节点 → Copy → Copy OuterHTML保存到一个.html或.txt文件中。这种方式的优点是数据干净没有大量冗余标签解析速度更快。注意无论哪种方式都只处理自己有权查看、有权保存的页面数据。不要把他人的页面源码用于商业爬取、倒卖等场景。7. 常见问题与排查思路7.1 解析结果商品ID为空问题现象常见原因解决思路goods_id 为 None页面ID不在URL中也没有 data 属性检查__INITIAL_STATE__JSON调整正则匹配goods_id 提取成 0正则匹配到了其他数字加上前后边界例如goods_id(\d)中文乱码HTML 编码不为 utf-8用chardet检测编码后读取如果页面编码不确定建议改装文件读取逻辑import chardet def read_html(file_path): with open(file_path, rb) as f: raw f.read() result chardet.detect(raw) encoding result.get(encoding) or utf-8 return raw.decode(encoding, errorsignore)chardet需要单独安装pip install chardet7.2 图片提取不全很多电商页面图片是 JS 动态渲染的如果本地保存的 HTML 只有静态源码图片地址可能不在其中。排查思路用开发者工具查看真实图片标签是否在 HTML 源码中确认图片地址是在src还是在># 推荐写法 import pathlib abs_path pathlib.Path(html/demo1.html).resolve().as_uri() URLS_DICT {demo1: abs_path}原因二页面资源未加载完如果页面依赖外部 CSS、JS本地打开时可能出现空白。可以替换为wait_untilnetworkidle或加更多等待时间。原因三懒加载导致图片未显示可以模拟滚动页面触发懒加载page.evaluate(() { window.scrollTo(0, document.body.scrollHeight); }) page.wait_for_timeout(1000)7.4 Pillow 裁剪后比例不对裁剪后图片比例不对通常是目标比例计算有误。建议在裁剪后打印尺寸做一次校验output_img cropped.copy() print(f实际比例: {output_img.width / output_img.height:.4f})如果偏差明显检查TARGET_RATIO 9 / 16在 Python 3 中这是浮点数计算没问题但如果手滑写成了9 // 16结果会是 0比例直接就错了。8. 最佳实践与工程建议8.1 用配置管理路径和参数不要把所有路径硬编码在脚本开头。推荐用一个config.py统一管理# config.py HTML_DIR html OUTPUT_DIR output VIEWPORT_WIDTH 900 VIEWPORT_HEIGHT 1600 TARGET_RATIO_9_16 (9, 16)脚本中直接import config后续改路径、改分辨率都只需要动一个文件。8.2 增加断点续跑和错误隔离批量处理几十上百个页面时单个文件解析失败不应导致整个程序崩溃。def parse_single_file(file_path): try: # 原有解析逻辑 return row except Exception as e: print(f解析失败: {file_path}, 错误: {e}) return None在main中过滤空结果即可。8.3 日志输出要能看到进度批量任务最怕跑一半不知道进度。建议每处理一个文件都输出“当前序号/总数”total len(files) for index, filename in enumerate(files, 1): print(f[{index}/{total}] 正在处理: {filename})8.4 数据写入前做去重同一个商品可能在多个 HTML 文件中重复出现。写入 CSV 前用goods_id做一次去重seen_ids set() unique_results [] for row in results: if row[goods_id] in seen_ids: continue seen_ids.add(row[goods_id]) unique_results.append(row)8.5 合规边界要清晰最后强调一下本文定位是本地网页解析不是爬虫。核心区别在于解析对象是已经保存到本地的 HTML 文件不是通过脚本主动抓取网络资源处理的数据来自用户有权访问的页面不涉及绕过登录、验证码、反爬等操作。在实际工作中仍然建议遵守以下原则仅处理自己有权限查看的数据不将提取结果用于批量倒卖、侵权发布等场景如果要做大规模数据采集先评估平台规则和适用法律涉及自动化工具操作第三方系统时先确认是否获得授权。8.6 注意图片资源的归属从页面中提取的图片链接其版权通常属于原始权利方。整理本地素材库可以但不建议直接二次发布、转售、批量下载后用于商业广告投放除非你已获得授权。9. 后续可以怎么拓展本文的流程已经覆盖了“本地解析 → 商品ID提取 → 原图提取 → 9:16 截图”这条完整链路。如果你还想继续深入可以考虑这几个方向增加自动下载图片用 Pythonrequests下载提取到的原图按商品 ID 分目录存放对接表格或数据库把 CSV 结果导入 Excel、数据库方便后续筛选做成本地 GUI 小工具用 PyQt 或 Tkinter 把解析脚本包装成可视化界面运营同学也能直接用加入批量重命名按“商品ID_序号”规则重命名图片方便素材归档。记住掌握解析思路比抄代码更重要。把脚本跑通之后花点时间理解 HTML 结构和字符串处理逻辑你就能应对各种不同结构的页面。
返回列表