将采集到的商品保存到数据库中并在后台系统展示,接下来讲解后端和前端代码。
后端
mapper类:
package com.learn.reptile.mapper;
import org.apache.ibatis.annotations.Mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.learn.reptile.entity.po.Item;
@Mapper
public interface ItemMapper extends BaseMapper<Item> {
}
controller类增加两个方法,craw抓取并保存商品,items分页展示商品数据:
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.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
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.mapper.ItemMapper;
import com.learn.reptile.utils.ItemCraw;
@RequestMapping("/item")
@RestController
public class ItemController {
@Resource
ItemMapper itemMapper;
@GetMapping
public R<IPage<Item>> items(Page<Item> page) {
return R.ok(itemMapper.selectPage(page, new QueryWrapper<>()));
}
/**
* 前端指定匹配商品的网址、正则匹配方法,抓取并保存商品
* @param itemWebsite
* @return
*/
@PostMapping("craw")
public R craw(ItemWebsite itemWebsite) {
List<Item> items = ItemCraw.parseItemsFromUrl(itemWebsite.getUrl(), itemWebsite.getRegexpStr(), itemWebsite.getStartStr(), itemWebsite.getEndStr());
for(Item item: items) {
//根据淘宝id作重复校验
Item pre = itemMapper.selectOne(new QueryWrapper<Item>().eq("item_id", item.getItemId()));
item.setSource(itemWebsite.getCode());
if(pre==null) {
itemMapper.insert(item);
} else {
item.setId(pre.getId());
itemMapper.updateById(item);
}
}
return R.ok();
}
/**
* 前端指定匹配商品的网址、正则匹配方法,仅作抓取测试
* @param itemWebsite
* @return
*/
@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:
{
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,
},
{
path: '',
name: 'item',
component: () => import('@/views/item/index.vue'),
meta: {
title: '商品',
},
},
],
},
/src/views/item/index.vue,首先写列表部分代码:
html部分
<template>
<div>
<el-table
:data="list"
v-loading="loading"
element-loading-text="Loading"
highlight-current-row
border
fit
>
<el-table-column prop="sourceName" label="采集网站"></el-table-column>
<el-table-column prop="itemId" label="淘宝ID"></el-table-column>
<el-table-column prop="title" label="标题"></el-table-column>
<el-table-column prop="pic" label="图片">
<template #default="scope">
<img :src="scope.row.pic" class="item-img" />
</template>
</el-table-column>
<el-table-column prop="price" label="价格"></el-table-column>
<el-table-column prop="prePrice" label="原价"></el-table-column>
<el-table-column prop="createTime" label="采集时间"></el-table-column>
</el-table>
<el-pagination
:current-page="searchForm.current"
:page-sizes="[20, 45, 80]"
:page-size="searchForm.size"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
:hide-on-single-page="true"
@size-change="pageSizeChange"
@current-change="pageNoChange"
/>
</div>
</template>
javascript部分
import { getCurrentInstance, reactive, toRefs, ref, onMounted } from 'vue'
import { list, craw } from '@/api/item'
import { list as websites } from '@/api/itemWebsite'
export default {
setup() {
const { proxy: ctx } = getCurrentInstance()
const state = reactive({
loading: false,
searchForm: {
current: 1,
size: 20,
},
total: 0,
list: [],
getList() {
ctx.loading = true
list(ctx.searchForm).then(res => {
ctx.list = res.data.records
ctx.list.forEach(item => {
ctx.websites.forEach(website => {
if (item.source == website.code) {
item.sourceName = website.name
}
})
})
ctx.total = res.data.total
ctx.loading = false
})
},
pageSizeChange(pageSize) {
ctx.searchForm.size = pageSize
ctx.getList()
},
pageNoChange(pageNo) {
ctx.searchForm.current = pageNo
ctx.getList()
},
})
onMounted(() => {
websites().then(res => {
ctx.websites = res.data
ctx.getList()
})
})
return {
...toRefs(state),
}
},
}
增加采集按钮和javascript代码:
<el-button type="primary" @click="showCraw">采集</el-button>
<el-dialog
v-model="crawDialogVisible"
title="商品采集"
width="40%"
@close="crawDialogVisible = false"
>
<el-form :model="crawForm" label-position="left">
<el-form-item prop="id" label="采集网站">
<el-select v-model="crawForm.id" clearable placeholder="自定义">
<el-option value="">自定义</el-option>
<el-option
v-for="website in websites"
:key="website.id"
:value="website.id"
:label="website.name"
@click="crawForm = website"
>
{{ website.name }}
</el-option>
</el-select>
</el-form-item>
<el-form-item prop="code" label="网站名称" v-if="!crawForm.id">
<el-input v-model="crawForm.code"></el-input>
</el-form-item>
<el-form-item prop="url" label="URL" v-if="!crawForm.id">
<el-input v-model="crawForm.url"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="craw">采集</el-button>
<el-button type="warning" @click="crawDialogVisible = false">
取消
</el-button>
</el-form-item>
</el-form>
</el-dialog>
javascript部分增加craw和showCraw方法,增加后的完整代码如下:
import { getCurrentInstance, reactive, toRefs, ref, onMounted } from 'vue'
import { list, craw } from '@/api/item'
import { list as websites } from '@/api/itemWebsite'
export default {
setup() {
const { proxy: ctx } = getCurrentInstance()
const state = reactive({
loading: false,
searchForm: {
current: 1,
size: 20,
},
total: 0,
list: [],
websites: [],
crawForm: {
id: '',
code: '',
name: '',
url: '',
},
crawDialogVisible: false,
getList() {
ctx.loading = true
list(ctx.searchForm).then(res => {
ctx.list = res.data.records
ctx.list.forEach(item => {
ctx.websites.forEach(website => {
if (item.source == website.code) {
item.sourceName = website.name
}
})
})
ctx.total = res.data.total
ctx.loading = false
})
},
pageSizeChange(pageSize) {
ctx.searchForm.size = pageSize
ctx.getList()
},
pageNoChange(pageNo) {
ctx.searchForm.current = pageNo
ctx.getList()
},
showCraw() {
ctx.crawDialogVisible = true
},
craw() {
craw(ctx.crawForm).then(res => {
ctx.$message.success('采集完成')
ctx.crawDialogVisible = false
ctx.getList()
})
},
})
onMounted(() => {
websites().then(res => {
ctx.websites = res.data
ctx.getList()
})
})
return {
...toRefs(state),
}
},
}
代码及演示网站见:正则采集器之一——需求说明-CSDN博客