正则采集器之五——商品匹配规则
需求设计 + 实现分析
系统通过访问URL得到html代码,通过正则表达式匹配html,通过反向引用来得到商品的标题、图片、价格、原价、id,这部分逻辑在java中实现。
匹配商品的正则做成可视化编辑,因为不同网站的结构不同,同一个网站的结构会随时间发生变化,为方便修改,做成可视化编辑。以九块邮为例分析匹配商品的正则:

由此图可见一个正则由多个单元项组成,每个单元项都是一个单独的正则(包括匹配商品的字段项和字段项前后的标志字符串),比如匹配价格的[\d\.]+,价格前面的html >¥ 。最终组合成的正则需要能够正确解析出一个个商品的标题、图片、价格、原价和id字段。
后端代码
匹配代码
package com.learn.reptile.utils;import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.learn.reptile.entity.po.Item;import cn.hutool.http.HttpUtil;public class ItemCraw {/*** 通过url获取html,然后解析出出商品* @param url* @param regexpStr 商品匹配正则表达式* @param startStr 开始匹配字符串* @param endStr 结束匹配字符串* @return*/public static List<Item> parseItemsFromUrl(String url, String regexpStr, String startStr, String endStr) {String html = HttpUtil.get(url);if(StringUtils.isNotBlank(endStr)) {html = html.substring(html.indexOf(startStr), html.lastIndexOf(endStr));} else {html = html.substring(html.indexOf(startStr));}List<Item> items = new ArrayList<>();Pattern pattern = Pattern.compile(regexpStr);Matcher matcher = pattern.matcher(html);// 每一个匹配整体while(matcher.find()) {Item item = new Item();item.setItemId(matcher.group("id"));item.setPic(matcher.group("pic"));item.setTitle(matcher.group("title"));item.setPrice(Double.parseDouble(matcher.group("price")));item.setPrePrice(Double.parseDouble(matcher.group("prePrice")));items.add(item);}return items;}}
匹配结果实体类
package com.learn.reptile.entity.po;import java.util.Date;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;import lombok.Data;@Data
public class Item {@TableId(type = IdType.AUTO)private Long id;// 淘宝商品idprivate String itemId;// 来源,匹配网站的编码private String source;private String title;private String pic;private double price;private double prePrice;// 采集时间private Date createTime;
}
controller类
package com.learn.reptile.web.controller;import java.util.List;import javax.annotation.Resource;import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import com.learn.reptile.entity.po.Item;
import com.learn.reptile.entity.po.ItemWebsite;
import com.learn.reptile.entity.vo.R;
import com.learn.reptile.utils.ItemCraw;@RequestMapping("/item")
@RestController
public class ItemController {@PostMapping("test")public R<List<Item>> test(@RequestBody ItemWebsite itemWebsite) {return R.ok(ItemCraw.parseItemsFromUrl(itemWebsite.getUrl(), itemWebsite.getRegexpStr(), itemWebsite.getStartStr(), itemWebsite.getEndStr()));}
}
前端代码
添加router,位置:src/router/modules/home.js。router的path中增加了参数:id,即网站的id。
{path: '/item',component: Layout,name: 'item',meta: {title: '商品',},icon: 'icon-home',children: [{path: 'itemWebsite',name: 'itemWebiste',component: () => import('@/views/item_website/index.vue'),meta: {title: '网站',},},{path: 'itemRegexp/:id',name: 'itemRegexp',component: () => import('@/views/item_website/regexp.vue'),meta: {title: '商品匹配正则',},hidden: true,},],},
位置src/views/item_website/regexp.vue
分析
用regexpItems表示正则单元项列表,每个regexpItem包含三个字段:type 表示匹配商品的某个字段还是仅仅是分隔部分,matchType 表示该部分的正则模式,matchStr 表示该正则模式需要用到的字符串。
type 数据
| key | value |
| id | 商品id |
| title | 标题 |
| pic | 图片 |
| price | 价格 |
| prePrice | 原价 |
| 其他 |
matchType 数据
| key | value |
| all | 任意字符串 |
| exclude | 不包含 某些字符串 的字符串 |
| fix | 固定字符串 |
| number | 价格,[\d\.]+ |
正则单元项html:
<divclass="regexp_item"v-for="(regexpItem, index) in regexpItems":key="index">{{ index + 1 }}<el-icon @click="regexpItems.splice(index, 1)"><CloseBold /></el-icon><div class="line"><div class="label">类型</div><div class="field"><el-selectv-model="regexpItem.type"@change="changeType(regexpItem)"><el-optionv-for="(name, code) in types":key="code":value="code":label="name">{{ name }}</el-option></el-select></div></div><div class="line"><div class="label">匹配类型</div><div class="field"><el-radio-group v-model="regexpItem.matchType"><el-radio value="number" label="number">数值</el-radio><el-radio value="all" label="all">任意字符</el-radio><el-radio value="exclude" label="exclude">除</el-radio><el-inputclass="match_input"v-if="regexpItem.matchType == 'exclude'"v-model="regexpItem.matchStr"/><el-radio value="fix" label="fix">固定</el-radio><el-inputv-if="regexpItem.matchType == 'fix'"v-model="regexpItem.matchStr"/></el-radio-group></div></div></div>
页面整体布局为左中右结构,左侧是正则单元项列表,中间是操作按钮,右边是测试匹配结果,完整html部分代码如下:
<template><div style="margin: 10px;">{{ itemWebsite.name }}匹配规则</div><div style="display: flex;"><div style="width: 60%"><div class="form"><div class="form_label">匹配开始字符串</div><div class="form_field"><el-input v-model="itemWebsite.startStr"></el-input></div><div class="form_label">匹配结束字符串</div><div class="form_field"><el-input v-model="itemWebsite.endStr"></el-input></div></div><divclass="regexp_item"v-for="(regexpItem, index) in regexpItems":key="index">{{ index + 1 }}<el-icon @click="regexpItems.splice(index, 1)"><CloseBold /></el-icon><div class="line"><div class="label">类型</div><div class="field"><el-selectv-model="regexpItem.type"@change="changeType(regexpItem)"><el-optionv-for="(name, code) in types":key="code":value="code":label="name">{{ name }}</el-option></el-select></div></div><div class="line"><div class="label">匹配类型</div><div class="field"><el-radio-group v-model="regexpItem.matchType"><el-radio value="number" label="number">数值</el-radio><el-radio value="all" label="all">任意字符</el-radio><el-radio value="exclude" label="exclude">除</el-radio><el-inputclass="match_input"v-if="regexpItem.matchType == 'exclude'"v-model="regexpItem.matchStr"/><el-radio value="fix" label="fix">固定</el-radio><el-inputv-if="regexpItem.matchType == 'fix'"v-model="regexpItem.matchStr"/></el-radio-group></div></div></div></div><div style="width: 180px; text-align: center;"><div style="margin-bottom: 10px;"><el-button round type="primary" @click="add">增加匹配项</el-button></div><div style="margin-bottom: 10px;"><el-button type="primary" round @click="save">保存</el-button></div><el-button type="primary" round @click="test">测试</el-button></div><div style="width: 40%;"><pre>{{ resultItems }}</pre></div></div>
</template>
javascript部分:
import {getCurrentInstance,reactive,toRefs,ref,computed,watch,onMounted,
} from 'vue'
import { getById, update } from '@/api/itemWebsite'
import { test } from '@/api/item'
import { ElMessageBox } from 'element-plus'export default {setup() {const { proxy: ctx } = getCurrentInstance()const state = reactive({id: '',itemWebsite: {},regexpItems: [],types: {title: '标题',pic: '图片',id: '商品id',price: '价格',prePrice: '原价','': '其他',},resultItems: '',add() {ElMessageBox.prompt('请输入添加的位置下标', '添加匹配项', {inputPattern: /\d+/,inputErrorMessage: '下标必须为正整数',}).then(({ value }) => {const index = parseInt(value)ctx.regexpItems.splice(index - 1, 0, {type: '',matchType: '',matchStr: '',})})},changeType(regexpItem) {switch (regexpItem.type) {case 'price':case 'prePrice':regexpItem.matchType = 'number'breakcase 'pic':case 'itemId':regexpItem.matchType = 'exclude'regexpItem.matchStr = '"'breakcase 'title':regexpItem.matchType = 'exclude'regexpItem.matchStr = '<'}},save() {var regexpStr = '' // 通过正则单元项列表生成正则字符串ctx.regexpItems.forEach(item => {var str = ''if (item.matchType == 'all') {str = '.+?'} else if (item.matchType == 'exclude') {str = '[^' + item.matchStr + ']+'} else if (item.matchType == 'fix') {str = item.matchStr} else if (item.matchType == 'number') {str = '[\\d\\.]+'}if (item.type) {regexpStr += '(?<' + item.type + '>' + str + ')'} else {regexpStr += str}})update({startStr: ctx.itemWebsite.startStr,endStr: ctx.itemWebsite.endStr,regexpContents: JSON.stringify(ctx.regexpItems), // 正则单元项列表以json字符串保存regexpStr: regexpStr,id: ctx.id,}).then(res => {ctx.$message.success('保存成功')})},test() {var regexpStr = ''ctx.regexpItems.forEach(item => {var str = ''if (item.matchType == 'all') {str = '.+?'} else if (item.matchType == 'exclude') {str = '[^' + item.matchStr + ']+'} else if (item.matchType == 'fix') {str = item.matchStr} else if (item.matchType == 'number') {str = '[\\d\\.]+'}if (item.type) {regexpStr += '(?<' + item.type + '>' + str + ')'} else {regexpStr += str}})test({url: ctx.itemWebsite.url,startStr: ctx.itemWebsite.startStr,endStr: ctx.itemWebsite.endStr,regexpStr: regexpStr,}).then(res => {ctx.$message.success('测试成功')ctx.resultItems = JSON.stringify(res.data,['itemId', 'title', 'pic', 'price', 'prePrice'],'\t')})},})onMounted(() => {ctx.id = ctx.$route.params.idgetById(ctx.id).then(res => {ctx.itemWebsite = res.dataif (ctx.itemWebsite.regexpContents) {ctx.regexpItems = eval('(' + ctx.itemWebsite.regexpContents + ')')}})})return {...toRefs(state),}},
}
样式部分:
<style>
.regexp_item {margin: 10px;border-top: 1px solid gray;border-right: 1px solid gray;position: relative;width: 100%;
}
.regexp_item .el-icon {position: absolute;right: -5px;top: -5px;color: red;cursor: pointer;
}
.line {display: flex;
}
.line > div {border-bottom: 1px solid gray;border-left: 1px solid gray;padding: 5px;
}
.label {width: 30%;
}
.field {width: 70%;
}
.match_input {width: 100px;margin-right: 15px;
}
.form {display: flex;align-items: center;margin: 10px;width: 100%;
}
.form_label {width: 20%;margin-left: 20px;
}
.form_field {width: 30%;
}
</style>
代码及演示网站见:正则采集器之一——需求说明-CSDN博客
相关文章:
正则采集器之五——商品匹配规则
需求设计 实现分析 系统通过访问URL得到html代码,通过正则表达式匹配html,通过反向引用来得到商品的标题、图片、价格、原价、id,这部分逻辑在java中实现。 匹配商品的正则做成可视化编辑,因为不同网站的结构不同,同…...
一键切换阿里yum源(包括其他系统repo镜像查找方法)
一键切换阿里yum源 示例命令其他系统repo镜像GitHub文档 示例命令 # 备份旧源 mv CentOS-Base.repo CentOS-Base.repo.bak # 添加新源(阿里镜像源) wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo其他系统repo镜像 这里的示例是用…...
Fiddler学习笔记
目录 前言 简介 原理 界面 前言 测试可以使用fiddler工具,通过抓包的方式修改前端参数和模拟后端返回,快速定位缺陷。 简介 Fiddler是HTTP协议调试代理工具,可以记录并检查所有客户端和服务器之间的HTTP和HTTPS请求,允许监视…...
【Vue3】watch 监视多种类型数据
【Vue3】watch 监视多种类型数据 背景简介开发环境开发步骤及源码 背景 随着年龄的增长,很多曾经烂熟于心的技术原理已被岁月摩擦得愈发模糊起来,技术出身的人总是很难放下一些执念,遂将这些知识整理成文,以纪念曾经努力学习奋斗…...
【C++入门】虚函数与多态
文章目录 前言虚函数是什么?如何使用虚函数? 纯虚函数是什么?虚函数与普通函数的区别虚表虚表是什么?含有虚表的类内存结构图如何找到虚表的地址?示例代码代码解释 多态是什么?如何使用多态?为什…...
wpf中轮询显示图片
本文的需求是,在一个文件夹中,放一堆图片的集合,然后在wpf程序中,按照定时的方式,循序显示照片。 全部代码 1.声明一个PictureInfo类 namespace WpfApp1 {public class PictureInfo{public string? FileName { get; …...
CSA笔记9-磁盘管理(2)
分区挂载 挂载:将该文件系统中的内容与指定的目录关联起来,使得你可以通过该目录来访问文件系统中的文件和目录。 mount 命令用来挂载文件系统 #挂载/dev/sda1和/dev/sda2 [rootlocalhost ~]# mkdir test{1..2} [rootlocalhost ~]# ll test1 te…...
Python入门第三课
# 入门第三课 # 关键字 if and or in not in ! car g print(car g) print(car dd) if car ! hh:print("wlcome to here ") age 33 print(age 33) print(age 44) age1 44 if age >0 and age1 > 0:print("nihao") if age >0 or age1 > …...
java计算器,输入公式和对应变量的值
目标:最近想写个东西,本质就是一个计算器,我们可以输入公式(例如:ab),然后把公式的值(a:10,b:20)也输入进去。最后得到结果。核心:这个想法核心部分就是给一个…...
加密货币赋能跨境电商:PayPal供应链金融服务如何引领行业新趋势
跨境电商行业近年来呈现出爆发式增长,随着全球化贸易壁垒的降低和数字经济的快速发展,越来越多的商家和消费者跨越国界进行交易。根据eMarketer的数据,全球跨境电商交易额在2023年已超过4万亿美元,并预计在未来几年内仍将保持两位…...
redis面试(二)List链表数据
list 列表 我们总是说List为列表,其实在真正的数据结构来说,redis是自己基于c语言来实现的双向链表数据结构,主要的逻辑就是每个节点都可以指向下一个节点,这个结构就属于链表数组结构。 每个节点中的属性如下: type…...
SpringDataJPA(三):多表操作,复杂查询
一、Specifications动态查询 有时我们在查询某个实体的时候,给定的条件是不固定的,这时就需要动态构建相应的查询语句,在Spring Data JPA中可以通过JpaSpecificationExecutor接口查询。相比JPQL,其优势是类型安全,更加的面向对象。 import …...
嵌入式硬件面试题集萃:从基础到进阶
基础问题 问题: 解释什么是微控制器,以及它与微处理器的区别。 答案: 微控制器是具有集成内存和输入/输出外设的微型计算机。与通用微处理器相比,微控制器通常用于控制特定应用,而不是执行通用计算任务。 问题: 什么是数字逻辑门,…...
easyui-datebox 只显示月份选择,默认开启月份,隐藏日期选择框
如果你使用 easyui-datebox 并希望隐藏日期选择框,只显示月份选择,可以通过一些自定义代码来实现。虽然 EasyUI 没有直接提供这种功能,但可以通过自定义 formatter 和 parser 方法,以及修改 onShowPanel 事件来实现这个功能。 以下…...
【数据结构】队列(链表实现 + 力扣 + 详解 + 数组实现循环队列 )
Hi~!这里是奋斗的明志,很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~~ 🌱🌱个人主页:奋斗的明志 🌱🌱所属专栏:数据结构 📚本系列文章为个人学…...
02 Go语言操作MySQL基础教程_20240729 课程笔记
概述 如果您没有Golang的基础,应该学习如下前置课程。 Golang零基础入门Golang面向对象编程Go Web 基础Go语言开发REST API接口_20240728 基础不好的同学每节课的代码最好配合视频进行阅读和学习,如果基础比较扎实,则阅读本教程巩固一下相…...
相交链表 - 力扣(LeetCode)C语言
160. 相交链表 - 力扣(LeetCode) (点击前面链接即可查看题目) 一、题目 给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。 图示两个链表在节点 c1 开始…...
【Python】基础学习技能提升代码样例3:JSON文本处理
对json的处理,无非是编码和解码两部分 编码:将python数据结构转换为json字符串解码: 将json字符串转换为python数据结构 另外,还有.json文件的读写 一、编码 json.dumps(obj, *, skipkeysFalse, ensure_asciiTrue, check_circularTrue, a…...
最新Yiso智云搜索引擎系统源码/开源PHP源码/修复版
源码简介: 最新Yiso智云搜索引擎系统源码/开源PHP源码/修复版。Yiso 是一个性能非常好的搜索引擎,不仅免费开源,还能当作收录网址的平台来用呢!只需要输入关键词,就能轻松找到相关的搜索结果内容。 1、Yiso 用的是自…...
Anconda 快速常用命令简洁版
目的:简单清楚的使用基本的conda 命令 可能需求 查看项目中的虚拟环境及依赖是否满足需求操作新环境来满足项目或者论文的实现 Anconda 常用命令 conda 查看基础命令1. 进入Anaconda 环境2. 查看版本3.查看有哪些虚拟环境4.激活虚拟环境5. 进入虚拟环境查看6. 退出…...
多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...
【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...
管理学院权限管理系统开发总结
文章目录 🎓 管理学院权限管理系统开发总结 - 现代化Web应用实践之路📝 项目概述🏗️ 技术架构设计后端技术栈前端技术栈 💡 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 🗄️ 数据库设…...
C++.OpenGL (14/64)多光源(Multiple Lights)
多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...
逻辑回归暴力训练预测金融欺诈
简述 「使用逻辑回归暴力预测金融欺诈,并不断增加特征维度持续测试」的做法,体现了一种逐步建模与迭代验证的实验思路,在金融欺诈检测中非常有价值,本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能
1. 开发环境准备 安装DevEco Studio 3.1: 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK 项目配置: // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...
