提交 e768cea5 authored 作者: 梁业锦's avatar 梁业锦 💬

添加了基于本项目的爬虫文档;已完成Zara与优衣库的爬虫格式化;优衣库存在缺陷,

暂且不可用
上级 d2f65e7c
......@@ -8,6 +8,11 @@
* 自定义的一些配置,比如邮箱、stripe账号、腾讯ai相关的配置等等
3. logback-spring.xml
* 日志配置文件
# 爬虫规范及文档
[爬虫文档链接](../doc/SpiderSpecification.md)
# 项目层次结构
项目大致分为两个包和相关的资源文件,两个包分别是com.diaoyun.zion.master和com.diaoyun.zion.chinafrica。
1. master这个包下面放的是项目通用的类,比如项目模块配置类、异常处理、spring security相关的东西。
......
# 爬虫规范
## 添加新爬虫至项目规范
- 1.实现爬虫接口并重写 **captureItem()** 方法
- [IItemSpider.java](../src/main/java/com/diaoyun/zion/chinafrica/bis/IItemSpider.java)
- 2.声明为 Spring 的组件
- 如"Nike"的```@Component("nikeItemSpider")```
- 3.在**爬虫枚举**中声明该平台的枚举
- [PlatformEnum.java](../src/main/java/com/diaoyun/zion/chinafrica/enums/PlatformEnum.java)
- 如"Nike"的```NIKE("NIKE", "nike")```
- 4.在**商品爬虫工厂类**中声明该平台的实现类
- [ItemSpiderFactory.java](../src/main/java/com/diaoyun/zion/chinafrica/factory/ItemSpiderFactory.java)
- 5.在**商品爬虫Service类**中声明该平台的爬虫和商品的详情路径
- [SpiderServiceImpl.java](../src/main/java/com/diaoyun/zion/chinafrica/service/impl/SpiderServiceImpl.java)
- 6.在**Json工具类**中编写获取爬虫主要的商品数据
- [JsoupUtil.java](../src/main/java/com/diaoyun/zion/master/util/JsoupUtil.java)
- 7.在**爬虫工具类**中编写爬虫返回json数据规范
- [SpiderServiceImpl.java](../src/main/java/com/diaoyun/zion/master/util/SpiderUtil.java)
- 爬虫数据封装类:[ProductResponse.java](../src/main/java/com/diaoyun/zion/chinafrica/vo/ProductResponse.java)
- 8.调用**翻译方法**翻译爬取回来的数据
- [translateProductResponse(JSONObject resultObj)](../src/main/java/com/diaoyun/zion/master/util/TranslateHelper.java)
> 注意爬虫的命名
## 需要爬取的购物网站
### Pull and Bear
- 主页:https://www.pullandbear.cn/cn/%E5%A5%B3%E5%A3%AB-c1030204574.html
- 命名:pullandbear
- 爬虫进度:存在缺陷
- 有反爬机制,有时会直接失效,不稳定
### Gap
- 主页:https://www.gap.cn/
- 命名:gap
- 爬虫进度:已完成
### Zara
- 主页:https://www.zara.cn/cn
- 命名:zara
- 爬虫进度:已完成
### Uniqlo
- 主页:https://www.uniqlo.cn/UNIQLO_U19FW_MEN.html
- 命名:uniqlo
- 爬虫进度:存在缺陷--
- 图片:会直接下载
- 无法获取折扣后的价格
### Nike
- 主页:https://www.nike.com/cn
- 命名:nike
- 爬虫进度:已完成
### Adidas
- 主页:https://www.adidas.com.cn/
- 命名:adidas
- 爬虫进度:
### H&M
- 主页:https://www2.hm.com/zh_cn/
- 命名:hm
- 爬虫进度:
### LiLy
- 主页:http://www.lily.sh.cn/webapp/wcs/stores/servlet/lilystore
- 命名:lily
- 爬虫进度:
### Eifini
- 主页:https://eifini.tmall.com/
- 命名:eifini
- 爬虫进度:
### Urban Revivo
- 主页:http://www.ur.cn/index.html
- 命名:ur
- 爬虫进度:
### 待选爬虫网站:
- 10, ( UR, the most famous fashion brand in china)
- 11,https://www.abercrombie.cn/zh_CN/home (A&F, America brand)
- 12,https://www.underarmour.cn/ (America famous sport brand)
- 13,https://www.converse.com.cn/ (Famous American Canvas Shoes Brand)
- 14,http://www.ochirly.com.cn/SALE/list.shtml (one of the best fashion brand in china)
- 15,https://www.esprit.cn/ (America casual fashion brand)
- 16,https://www.levi.com.cn/sale#page=3 (levis)
- 17,https://www.moco.com/moco/zh/c/BS_DISCOUNT (one of the best fashion brand in china)
- 18,https://www.massimodutti.cn/cn/男装/季末折扣/休闲西装-c1745921.html ( 西班牙品牌)
- 19,https://china.coach.com/women.html
- 20,https://www.revolve.com/wrangler/br/57f1a1/?utm_source=baidu&utm_medium=cpc&utm_campaign=intl_P_cn-d-Wrangler (美国轻奢品牌集合网站)
- 21,https://vans.com.cn/gallery-index---0---36.html
- 22,https://zarahome.tmall.com/?spm=a1z10.3-b-s.1997427721.d4918089.7b872e00zWrHhi
- 23,https://oysho.tmall.com/ (SPORT WEAR)
- 24,https://stradivarius.tmall.com/ (ZARA GROUP )
- 25,https://www.maje.cn/home?utm_campaign=maje-competitor-u&utm_content=competitor-ph&utm_medium=cpc&utm_source=baidu&utm_term=marc%e6%98%af%e4%bb%80%e4%b9%88%e7%89%8c%e5%ad%90
- 26,https://www.gucci.cn/zh/?utm_source=baiducpc_cn&utm_medium=cpc&utm_term=Title2&utm_content=URL_E-commerce_HomePage&utm_campaign=BD_PC_URL_E-commerce&src=Baidu&medium=PPC&Network=1&kw=133669705534&ad=31701433238&ag_kwid=23329-4-575c9bdeeef5d28f.9143bf3bdcd3718c
- 27,https://cn.burberry.com/?utm_source=Baidu&utm_medium=cpc&utm_campaign=cn_brandzone_pc_aw19_aw_20190822&utm_term=brandzone&utm_content=title&gclid=CPf87uqgwOQCFQ4XvAodUzMEkA&gclsrc=ds
- 28,https://www.prada.com/cn/zh.html?utm_source=baidu&utm_medium=pcbrandzone&utm_campaign=2017-12-19_e-commerce
- 29,https://www.fendi.cn/?utm_source=Baidu&utm_medium=PC&utm_campaign=NewBrand%20Pure&utm_content=B_Site
- 30,https://www.vmall.com/huawei?cid=78140 (huawei)
- 31,http://inside.chanel.com/zh/chanel-goes-west
- 32,https://www.apple.com/cn/shop/buy-iphone/iphone-xr
- 33,https://www.louisvuitton.cn/zhs-cn/homepage?campaign=sem_CN_ZHS_BA_EC_BZON_PC_Valuable_H1_homepage
## 爬虫Json数据返回规范
#### 按照 Nike 爬虫返回的数据进行封装
│── data 数据<br>
│ │── dynStock 库存<br>
│ ││─── productSkuStockList 库存数量的集合<br>
│ │││──── sellableQuantity 库存数量<br>
│ │││──── skuStr 库存对应尺码(或尺码)<br>
│ ││─── sellableQuantity 库存数量<br>
│ │── itemInfo 基本信息<br>
│ ││─── itemId 商品id<br>
│ ││─── pic 图片<br>
│ ││─── shopName 平台名称<br>
│ ││─── shopUrl 商品路径<br>
│ ││─── title 标题<br>
│ │── originalPriceList 原始价<br>
│ ││─── price 价格<br>
│ │── 自定义属性名<br>
│ │── platform 枚举类型<br>
│ │── 自定义属性名<br>
│ ││─── image 图片<br>
│ ││─── propId 属性id<br>
│ ││─── propName 属性名<br>
│ ││─── translate 翻译结果<br>
│ │── price 价格<br>
│ │── productPropSet 属性<br>
│ │── promotionFlag 是否包含促销价格<br>
│ │── promotionList 若促销价格为 true 则添加促销价格<br>
│ │── propFlag 是否包含商品商品属性<br>
│ │── salePrice 促销一口价<br>
│ │── stockFlag 是否包含库存信息<br>
│── code 1 <br>
│── message "商品规格信息" <br>
```json
{
"data": {
"dynStock": { // 库存
"productSkuStockList": [ // 库存的集合
{
"sellableQuantity": 999, // 库存数量
"skuStr": ";AO4568-600;customizeId_39EU;" // 商品型号(或对应尺码)
},
{
"sellableQuantity": 999,
"skuStr": ";AO4568-600;customizeId_40EU;"
},
{
"sellableQuantity": 999,
"skuStr": ";AO4568-600;customizeId_44.5EU;"
},
{
"sellableQuantity": 999,
"skuStr": ";AO4568-600;customizeId_45EU;"
},
{
"sellableQuantity": 999,
"skuStr": ";AO4568-600;customizeId_46EU;"
},
{
"sellableQuantity": 999,
"skuStr": ";AO4568-300;customizeId_36EU;"
},
{
"sellableQuantity": 999,
"skuStr": ";AO4568-300;customizeId_36.5EU;"
},
{
"sellableQuantity": 999,
"skuStr": ";AO4568-300;customizeId_37.5EU;"
},
{
"sellableQuantity": 999,
"skuStr": ";AO4568-300;customizeId_38EU;"
},
{
"sellableQuantity": 999,
"skuStr": ";AO4568-300;customizeId_38.5EU;"
},
{
"sellableQuantity": 999,
"skuStr": ";AO4568-300;customizeId_39EU;"
},
{
"sellableQuantity": 999,
"skuStr": ";AO4568-300;customizeId_44EU;"
},
{
"sellableQuantity": 999,
"skuStr": ";AO4568-300;customizeId_45EU;"
},
{
"sellableQuantity": 999,
"skuStr": ";AO4568-300;customizeId_46EU;"
}
],
"sellableQuantity": 9999
},
"itemInfo": { // 基本信息
"itemId": "", // 商品信息
"pic": "https://c.static-nike.com/a/images/t_default/oswylvgs8k4lipvwtxsz/zoomx-vaporfly-next-男-女跑步鞋-SJldS6.jpg", // 商品图片
"shopName": "NIKE", // 商店名称
"shopUrl": "https://www.nike.com/cn/", // 商品路径
"title": "Nike ZoomX Vaporfly NEXT% 男/女跑步鞋-耐克(Nike)中国官网" // 商品的标题
},
"originalPriceList": [ // 原始价
{
"price": "296.06", // 价格
"skuStr": ";AO4568-600;customizeId_36EU;" // 对应尺码
},
{
"price": "296.06",
"skuStr": ";AO4568-600;customizeId_36.5EU;"
},
{
"price": "296.06",
"skuStr": ";AO4568-600;customizeId_37.5EU;"
},
{
"price": "296.06",
"skuStr": ";AO4568-600;customizeId_38EU;"
},
{
"price": "296.06",
"skuStr": ";AO4568-600;customizeId_38.5EU;"
},
{
"price": "296.06",
"skuStr": ";AO4568-600;customizeId_39EU;"
},
{
"price": "296.06",
"skuStr": ";AO4568-600;customizeId_40EU;"
},
{
"price": "296.06",
"skuStr": ";AO4568-600;customizeId_40.5EU;"
},
{
"price": "296.06",
"skuStr": ";AO4568-600;customizeId_41EU;"
},
{
"price": "296.06",
"skuStr": ";AO4568-600;customizeId_42EU;"
},
{
"price": "296.06",
"skuStr": ";AO4568-600;customizeId_42.5EU;"
},
{
"price": "296.06",
"skuStr": ";AO4568-600;customizeId_43EU;"
},
{
"price": "296.06",
"skuStr": ";AO4568-600;customizeId_44EU;"
},
{
"price": "296.06",
"skuStr": ";AO4568-600;customizeId_44.5EU;"
},
{
"price": "296.06",
"skuStr": ";AO4568-600;customizeId_45EU;"
},
{
"price": "296.06",
"skuStr": ";AO4568-600;customizeId_46EU;"
},
{
"price": "296.06",
"skuStr": ";AO4568-300;customizeId_36EU;"
},
{
"price": "296.06",
"skuStr": ";AO4568-300;customizeId_36.5EU;"
},
{
"price": "296.06",
"skuStr": ";AO4568-300;customizeId_37.5EU;"
},
{
"price": "296.06",
"skuStr": ";AO4568-300;customizeId_38EU;"
},
{
"price": "296.06",
"skuStr": ";AO4568-300;customizeId_38.5EU;"
},
{
"price": "296.06",
"skuStr": ";AO4568-300;customizeId_39EU;"
},
{
"price": "296.06",
"skuStr": ";AO4568-300;customizeId_40EU;"
},
{
"price": "296.06",
"skuStr": ";AO4568-300;customizeId_40.5EU;"
},
{
"price": "296.06",
"skuStr": ";AO4568-300;customizeId_41EU;"
},
{
"price": "296.06",
"skuStr": ";AO4568-300;customizeId_42EU;"
},
{
"price": "296.06",
"skuStr": ";AO4568-300;customizeId_42.5EU;"
},
{
"price": "296.06",
"skuStr": ";AO4568-300;customizeId_43EU;"
},
{
"price": "296.06",
"skuStr": ";AO4568-300;customizeId_44EU;"
},
{
"price": "296.06",
"skuStr": ";AO4568-300;customizeId_44.5EU;"
},
{
"price": "296.06",
"skuStr": ";AO4568-300;customizeId_45EU;"
},
{
"price": "296.06",
"skuStr": ";AO4568-300;customizeId_46EU;"
}
],
"platform": "nike", // 商品来源,对应枚举类
"price": "296.06", // 商品价格
"productPropSet": { // 商品属性,如颜色、尺码
"颜色": [
{
"image": "https://c.static-nike.com/a/images/t_default/og3mqyqi9ckvpz43yp3u/zoomx-vaporfly-next-男-女跑步鞋-SJldS6.jpg", // 图片
"propId": "AO4568-600", // 颜色对应的型号
"propName": "爆炸粉/冰番石榴色/黑", // 颜色名称
"translate": "Explosive powder / pomegranate color / black" // 翻译的结果
},
{
"image": "https://c.static-nike.com/a/images/t_default/oswylvgs8k4lipvwtxsz/zoomx-vaporfly-next-男-女跑步鞋-SJldS6.jpg",
"propId": "AO4568-300",
"propName": "电子绿/冰番石榴色/黑",
"translate": "Electronic green / pomegranate color / black"
}
],
"尺码": [
{
"image": "", // 图片
"propId": "customizeId_40EU", // 尺码值
"propName": "EU 40", // 尺码值
"translate": "EU 40" // 翻译结果
},
{
"image": "",
"propId": "customizeId_42EU",
"propName": "EU 42",
"translate": "EU 42"
},
{
"image": "",
"propId": "customizeId_44.5EU",
"propName": "EU 44.5",
"translate": "EU 44.5"
},
{
"image": "",
"propId": "customizeId_41EU",
"propName": "EU 41",
"translate": "EU 41"
},
{
"image": "",
"propId": "customizeId_37.5EU",
"propName": "EU 37.5",
"translate": "EU 37.5"
},
{
"image": "",
"propId": "customizeId_40.5EU",
"propName": "EU 40.5",
"translate": "EU 40.5"
},
{
"image": "",
"propId": "customizeId_42.5EU",
"propName": "EU 42.5",
"translate": "EU 42.5"
},
{
"image": "",
"propId": "customizeId_44EU",
"propName": "EU 44",
"translate": "EU 44"
},
{
"image": "",
"propId": "customizeId_43EU",
"propName": "EU 43",
"translate": "EU 43"
},
{
"image": "",
"propId": "customizeId_46EU",
"propName": "EU 46",
"translate": "EU 46"
},
{
"image": "",
"propId": "customizeId_45EU",
"propName": "EU 45",
"translate": "EU 45"
},
{
"image": "",
"propId": "customizeId_36EU",
"propName": "EU 36",
"translate": "EU 36"
},
{
"image": "",
"propId": "customizeId_36.5EU",
"propName": "EU 36.5",
"translate": "EU 36.5"
},
{
"image": "",
"propId": "customizeId_38EU",
"propName": "EU 38",
"translate": "EU 38"
},
{
"image": "",
"propId": "customizeId_39EU",
"propName": "EU 39",
"translate": "EU 39"
},
{
"image": "",
"propId": "customizeId_38.5EU",
"propName": "EU 38.5",
"translate": "EU 38.5"
}
]
},
"promotionFlag": false, // 是否包促销价格 true 有促销价格,false\null没有促销价格
"promotionList": [
],
"propFlag": true, // 是否包含商品属性,有些商品没有属性
"salePrice": "", // 促销一口价
"stockFlag": true // 是否包含库存信息 有些商品没有库存信息,可以当作是有货 true 有库存信息,false没有
},
"code": 1,
"message": "商品规格信息"
}
```
\ No newline at end of file
......@@ -21,7 +21,6 @@ public class NikeItemSpider implements IItemSpider {
private static Logger logger = LoggerFactory.getLogger(NikeItemSpider.class);
@Override
public JSONObject captureItem(String targetUrl) throws URISyntaxException, IOException, InterruptedException, ExecutionException, TimeoutException {
JSONObject resultObj;
......
......@@ -17,8 +17,6 @@ import java.util.concurrent.TimeoutException;
/**
* 西班牙年轻时尚品牌-pullandbear 数据爬虫
*
* TODO 图片路径未处理
* 图片路径为:
*/
@Component("pullandbearSpider")
public class PullandbearSpider implements IItemSpider {
......
......@@ -2,7 +2,10 @@ package com.diaoyun.zion.chinafrica.bis.impl;
import com.diaoyun.zion.chinafrica.bis.IItemSpider;
import com.diaoyun.zion.chinafrica.enums.PlatformEnum;
import com.diaoyun.zion.chinafrica.vo.ProductResponse;
import com.diaoyun.zion.master.util.HttpClientUtil;
import com.diaoyun.zion.master.util.SpiderUtil;
import com.diaoyun.zion.master.util.TranslateHelper;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
......@@ -17,8 +20,9 @@ import java.util.concurrent.TimeoutException;
/**
* 优衣库数据爬虫
*
* TODO 图片路径未处理
* 图片路径为:"https://www.uniqlo.cn/hmall/test/" + 商品id + "/sku/40/" + 商品图片id + ".jpg"
* 图片路径为:"https://www.uniqlo.cn/hmall/test/" + 商品id + "/sku/561/" + 商品图片id + ".jpg"
*
* @author 爱酱油不爱醋
*/
@Component("uniqloSpider")
public class UniqloSpider implements IItemSpider {
......@@ -37,18 +41,13 @@ public class UniqloSpider implements IItemSpider {
// 获取网页内容
String content = HttpClientUtil.getContentByUrl(targetUrl, PlatformEnum.UNIQLO.getValue());
// 转换为 json
JSONObject json = JSONObject.fromObject(content);
// 商品id
String pName = json.getJSONObject("summary").getString("name");
// 商品价格
String pPrice = json.getJSONObject("summary").getString("originPrice");
// 格式化数据
JSONArray rowsJson = json.getJSONArray("rows");
JSONObject returnJson = new JSONObject();
returnJson.put("name", pName);
returnJson.put("price", pPrice);
returnJson.elementOpt("data", rowsJson);
return returnJson;
JSONObject resultJson = JSONObject.fromObject(content);
// 按照封装规范封装商品数据
ProductResponse productResponse = SpiderUtil.formatUniqloProductResponse(resultJson, pId);
resultJson = JSONObject.fromObject(productResponse);
// 翻译
TranslateHelper.translateProductResponse(resultJson);
return resultJson;
}
}
......@@ -22,7 +22,8 @@ import java.util.concurrent.TimeoutException;
/**
* Zara西班牙时尚品牌数据爬虫
* TODO 数据未处理完全
*
* @author 爱酱油不爱醋
*/
@Component("zaraSpider")
public class ZaraSpider implements IItemSpider {
......@@ -37,76 +38,14 @@ public class ZaraSpider implements IItemSpider {
// 获取url中的网页内容
String content = HttpClientUtil.getContentByUrl(targetUrl, PlatformEnum.ZARA.getValue());
// 截取主要的 Json 内容
resultObj = JsoupUtil.getZara(content);
ProductResponse productResponse = SpiderUtil.formatNikeProductResponse(resultObj);
resultObj = JsoupUtil.getZaraJsonData(content);
// 按照封装规范封装商品数据
ProductResponse productResponse = SpiderUtil.formatZaraProductResponse(resultObj);
// 将封装数据转换为 json 数据
resultObj = JSONObject.fromObject(productResponse);
//翻译
// 翻译
TranslateHelper.translateProductResponse(resultObj);
return resultObj;
}
/**
* 根据首位字符串内容进行截取
* @param jsonStr
* @param startStr 起始字符串
* @param lastStr 结尾字符串(不包含)
* @return
*/
private static String getDataJson(String jsonStr, String startStr, String lastStr) {
int startIndex = jsonStr.indexOf(startStr);
int lastIndex = jsonStr.lastIndexOf(lastStr);
return jsonStr.substring(startIndex, lastIndex);
}
public static void main(String[] args) throws IOException, URISyntaxException {
// URL链接
String targetUrl = "https://www.zara.cn/cn/zh/%E5%BA%9C%E7%BB%B8%E9%95%BF%E7%89%88%E8%A1%AC%E8%A1%AB-p08053157.html?v1=31979171&v2=1319321";
// 获取网页内容
String content = HttpClientUtil.getContentByUrl(targetUrl, PlatformEnum.ZARA.getValue());
// 截取主要的商品数据
int labelHeadIndex = content.indexOf("dataLayer");
int labelTailIndex = content.lastIndexOf(";window.zara.viewPayload");
String abv = content.substring(labelHeadIndex, labelTailIndex).replace("dataLayer = ", "");
System.err.println(abv);
// 转换为 Json 格式
JSONObject json = JSONObject.fromObject(abv);
// System.err.println(json);
// product 对象节点
JSONObject responseData = json.getJSONObject("product");
// System.err.println(responseData);
// detail 对象节点
JSONObject details = responseData.getJSONObject("detail");
// System.err.println(details);
// colors 数组节点
JSONArray colorsArray = details.getJSONArray("colors");
Map<Integer, JSONObject> colorMap = new HashMap<>();
for (int i = 0; i < colorsArray.size(); i++) {
colorMap.put(i, colorsArray.getJSONObject(i));
}
System.out.println(colorMap);
// TODO 取出颜色属性
// sizes 数组节点
Map<Integer, JSONObject> sizesMap = new HashMap<>();
for(Map.Entry<Integer, JSONObject> entry : colorMap.entrySet()){
// 遍历出每个 colors 对象节点
JSONObject colors = entry.getValue();
JSONArray sizesArray = colors.getJSONArray("sizes");
for (int i = 0; i < sizesArray.size(); i++) {
sizesMap.put(i, sizesArray.getJSONObject(i));
}
}
System.out.println(sizesMap);
// TODO 取出价格和尺码属性
// TODO 取出图片属性(图片实体类未知)
}
}
......@@ -29,10 +29,12 @@ public enum PlatformEnum implements EnumItemable<PlatformEnum> {
this.value = value;
}
@Override
public String getLabel() {
return this.label;
}
@Override
public String getValue() {
return this.value;
}
......
......@@ -6,6 +6,7 @@ import java.util.List;
* 库存
*/
public class DynStock {
//可用总的库存数
private int sellableQuantity;
//sku对应的库存数
......
......@@ -47,11 +47,13 @@ public class ProductProp {
@Override
public boolean equals(Object obj) {
if(obj==null)
if (obj==null) {
return false;
if(this==obj)
}
if (this==obj) {
return true;
if(obj instanceof ProductProp) {
}
if (obj instanceof ProductProp) {
ProductProp productProp =(ProductProp) obj;
if(productProp.propId.equals(this.propId)) {
return true;
......
......@@ -6,30 +6,54 @@ import java.util.Set;
/**
* 爬取数据后,返回页面的商品详情数据
*
* @author G
*/
public class ProductResponse {
//原始价格 有优惠的话还有优惠价
/**
* 原始价格 有优惠的话还有优惠价
*/
private List<OriginalPrice> originalPriceList;
//是否包促销价格 true 有促销价格,false\null没有促销价格
/**
* 是否包促销价格 true 有促销价格,false\null没有促销价格
*/
private boolean promotionFlag;
//促销价格
/**
* 促销价格
*/
private List<ProductPromotion> promotionList;
//原价一口价,就是商品一开始展示的价格,比如多sku多价格的情况下展示 18.80-49.90
/**
* 原价一口价,就是商品一开始展示的价格,比如多sku多价格的情况下展示 18.80-49.90
*/
private String price;
//促销一口价
/**
* 促销一口价
*/
private String salePrice;
//是否包含库存信息 有些商品没有库存信息,可以当作是有货 true 有库存信息,false没有
/**
* 是否包含库存信息 有些商品没有库存信息,可以当作是有货 true 有库存信息,false没有
*/
private boolean stockFlag;
//库存
/**
* 库存
*/
private DynStock dynStock;
//是否包含商品属性,有些商品没有属性
/**
* 是否包含商品属性,有些商品没有属性
*/
private boolean propFlag;
//商品属性 颜色:红色,蓝色;尺码:S,l,M
/**
* 商品属性 颜色:红色,蓝色;尺码:S,l,M
*/
private Map<String, Set<ProductProp>> productPropSet;
//商品信息
/**
* 商品信息
*/
private ItemInfo itemInfo;
//商品来源平台 PlatformEnum
/**
* 商品来源平台 PlatformEnum
*/
private String platform;
......
package com.diaoyun.zion.master.util;
import com.diaoyun.zion.chinafrica.enums.PlatformEnum;
import net.sf.json.JSONObject;
import org.apache.commons.text.StringEscapeUtils;
import org.jsoup.Jsoup;
......@@ -10,6 +11,8 @@ import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
......@@ -18,8 +21,11 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class JsoupUtil {
public static String unknow = "未知";
private static Logger logger = LoggerFactory.getLogger(JsoupUtil.class);
/**
* 获取淘宝商品详情的信息 店铺id 名字 主图 sibUrl 等
*
......@@ -33,7 +39,8 @@ public class JsoupUtil {
String varArr[] = configGroup.split(";");
for (String variable : varArr) {
//获取g_config 变量
Pattern variablePattern = Pattern.compile("(g_config){1,1}\\s+={1,1}[\\s\\S]*"); // Regex for the value of the key
// Regex for the value of the key
Pattern variablePattern = Pattern.compile("(g_config){1,1}\\s+={1,1}[\\s\\S]*");
Matcher varMatcher = variablePattern.matcher(variable);
while (varMatcher.find()) {
String configStr = varMatcher.group();
......@@ -225,11 +232,11 @@ public class JsoupUtil {
}
/**
*
* 获取Zara爬虫的主要数据
* @param content
* @return
*/
public static JSONObject getZara(String content) {
public static JSONObject getZaraJsonData(String content) {
int labelHeadIndex = content.indexOf("dataLayer");
int labelTailIndex = content.lastIndexOf(";window.zara.viewPayload");
String abv = content.substring(labelHeadIndex, labelTailIndex).replace("dataLayer = ", "");
......@@ -237,7 +244,6 @@ public class JsoupUtil {
return dataMap;
}
/**
* 根据script id获取内容
* @param content
......
......@@ -13,6 +13,7 @@ import java.math.BigDecimal;
import java.util.*;
public class SpiderUtil {
private static BigDecimal rate;
static {
......@@ -150,7 +151,7 @@ public class SpiderUtil {
ProductResponse productResponse = new ProductResponse();
//nike 基本是 颜色、尺码属性
Map<String, Set<ProductProp>> productPropSet = new HashMap<>();
Map<String, Set<ProductProp>> productPropSet = new HashMap<>(16);
//原始价
List<OriginalPrice> originalPriceList = new ArrayList<>();
//促销价格
......@@ -181,7 +182,7 @@ public class SpiderUtil {
productResponse.setPrice(currentPrice);
JSONArray skusArr = itemDetail.getJSONArray("skus");
//获取商品尺码属性,同时记录下skuid和尺码关系
Map<String, String> sizeSkuIdMapping = new HashMap<>();
Map<String, String> sizeSkuIdMapping = new HashMap<>(16);
for (int i = 0; i < skusArr.size(); i++) {
String skuId = skusArr.getJSONObject(i).getString("skuId");
/////////////////////////获取商品尺码属性////////////////////
......@@ -290,12 +291,16 @@ public class SpiderUtil {
return productResponse;
}
//转换汇率
/**
* 转换汇率
* @param fullPrice
* @return
*/
private static String exchangeRate(String fullPrice) {
/*获取人民币汇率 1美元换取人民币
*TODO 同步汇率问题
*暂时从后台获取人工设置的汇率
* */
/* 获取人民币汇率 1美元换取人民币
* TODO 同步汇率问题
* 暂时从后台获取人工设置的汇率
*/
return new BigDecimal(fullPrice).divide(rate, 2, BigDecimal.ROUND_UP).toString();
}
......@@ -340,7 +345,7 @@ public class SpiderUtil {
OriginalPrice originalPrice = new OriginalPrice();
String price = variantsArray.getJSONObject(i).getString("price");
BigDecimal priceOld=new BigDecimal(price);
BigDecimal div=new BigDecimal("100");
BigDecimal div = new BigDecimal("100");
BigDecimal priceNew = priceOld.divide(div, 2, BigDecimal.ROUND_HALF_UP);
originalPrice.setPrice(priceNew.toString());
originalPrice.setSkuStr(skuStr);
......@@ -399,4 +404,260 @@ public class SpiderUtil {
productResponse.setPrice(priceNew.toString());
return productResponse;
}
/**
* 格式化 Zara 返回数据
*
* @param dataMap
* @return
*/
public static ProductResponse formatZaraProductResponse(JSONObject dataMap) {
// 声明封装类
ProductResponse productResponse = new ProductResponse();
// 属性:Zara 的商品属性有颜色、尺码
Map<String, Set<ProductProp>> productPropSet = new HashMap<>(16);
// 原始价
List<OriginalPrice> originalPriceList = new ArrayList<>();
// 促销价格
List<ProductPromotion> promotionList = new ArrayList<>();
// 库存
DynStock dynStock = new DynStock();
// 其实数据没有包含确切的库存数,这里默认给足量的库存
dynStock.setSellableQuantity(9999);
//////////////////////////////////// 获取商品基本信息 ////////////////////////////////////////////
ItemInfo itemInfo = new ItemInfo();
itemInfo.setShopName("Zara");
itemInfo.setShopUrl(dataMap.getString("backUrl"));
JSONObject productObj = dataMap.getJSONObject("product");
itemInfo.setItemId(productObj.getString("id"));
itemInfo.setTitle(productObj.getString("name"));
//////////////////////////////////// 获取商品基本信息End(图片下取) ////////////////////////////////////////////
// 取 colors 节点数组
JSONArray colorsArr = productObj.getJSONObject("detail").getJSONArray("colors");
for (int i = 0; i < colorsArr.size(); i++) {
JSONObject colorsObj = colorsArr.getJSONObject(i);
// 取 detailImagesArr 节点数组第一个对象
JSONObject detailImagesObj_0 = colorsObj.getJSONArray("detailImages").getJSONObject(0);
// 处理图片 参考路径:http://static.zara.cn/photos///2019/I/0/1/p/0858/457/800/17/w/1920/0858457800_1_1_1.jpg?ts=1570720340221
String imageUrl = "http://static.zara.cn/photos//"
+ detailImagesObj_0.getString("path")
+ "w/1920/"
+ detailImagesObj_0.getString("name")
+ "_1.jpg?ts="
+ detailImagesObj_0.getString("timestamp");
if (i == 0) {
// 商品基本信息--设置:图片
itemInfo.setPic(imageUrl);
}
//////////////////////////////////// 获取商品颜色属性 ////////////////////////////////////////////
Set<ProductProp> propSet = new HashSet<>(16);
ProductProp productPropColor = new ProductProp();
// 颜色描述
productPropColor.setPropId(colorsObj.getString("productId"));
productPropColor.setPropName(colorsObj.getString("name"));
productPropColor.setImage(imageUrl);
propSet.add(productPropColor);
if (productPropSet.get("颜色") == null) {
productPropSet.put("颜色", propSet);
} else {
Set<ProductProp> oldPropSet = productPropSet.get("颜色");
propSet.addAll(oldPropSet);
productPropSet.put("颜色", propSet);
}
//////////////////////////////////// 获取商品颜色属性 END ////////////////////////////////////////////
// 取 sizes 节点数组
JSONArray sizesArr = colorsArr.getJSONObject(i).getJSONArray("sizes");
for (int j = 0; j < sizesArr.size(); j++) {
JSONObject sizesObj = sizesArr.getJSONObject(j);
// 库存对应的id(Zara 中以颜色id + 尺码id)
String skuStr = ";" + colorsObj.getString("productId") + ";" + sizesObj.getString("sku") + ";";
//////////////////////////////////// 获取库存 ////////////////////////////////////////////
// 设置:商品包含库存信息
productResponse.setStockFlag(true);
List<ProductSkuStock> productSkuStockList = dynStock.getProductSkuStockList();
if (productSkuStockList == null) {
productSkuStockList = new ArrayList<>();
}
ProductSkuStock productSkuStock = new ProductSkuStock();
// 设置:可用库存值,Zara 未有可用的库存数据
productSkuStock.setSellableQuantity(999);
// 设置:库存对应的id
productSkuStock.setSkuStr(skuStr);
productSkuStockList.add(productSkuStock);
dynStock.setProductSkuStockList(productSkuStockList);
//////////////////////////////////// 获取库存 END/////////////////////////////////////////
//////////////////////////////////// 获取原始价 //////////////////////////////////
OriginalPrice originalPrice = new OriginalPrice();
// 获取商品的原始价
String fullPrice = sizesObj.getString("price");
BigDecimal priceOld=new BigDecimal(fullPrice);
BigDecimal div = new BigDecimal("100");
BigDecimal priceNew = priceOld.divide(div, 2, BigDecimal.ROUND_HALF_UP);
// TODO 转换汇率,目前商品单位是人民币
fullPrice= exchangeRate(priceNew.toString());
originalPrice.setPrice(fullPrice);
originalPrice.setSkuStr(skuStr);
originalPriceList.add(originalPrice);
//////////////////////////////////// 获取原始价 END//////////////////////////////////
///////////////////////// 获取商品尺码属性 ////////////////////
Set<ProductProp> sizePropSet = new HashSet<>();
ProductProp productPropSize = new ProductProp();
String size = sizesObj.getString("name");
productPropSize.setPropId(sizesObj.getString("sku"));
productPropSize.setPropName(size);
sizePropSet.add(productPropSize);
if (productPropSet.get("尺码") == null) {
productPropSet.put("尺码", sizePropSet);
} else {
Set<ProductProp> oldPropSet = productPropSet.get("尺码");
sizePropSet.addAll(oldPropSet);
productPropSet.put("尺码", sizePropSet);
}
///////////////////////// 获取商品尺码属性 END////////////////////
}
}
// 按照一下顺序进行 json 数据的填充
productResponse.setPropFlag(true);
productResponse.setProductPropSet(productPropSet);
productResponse.setPlatform(PlatformEnum.ZARA.getValue());
productResponse.setPromotionList(promotionList);
productResponse.setOriginalPriceList(originalPriceList);
productResponse.setItemInfo(itemInfo);
productResponse.setDynStock(dynStock);
return productResponse;
}
/**
* 格式化 Uniqlo优衣库 返回数据
*
* @param dataMap 调用优衣库网页接口接收的主要商品数据
* @param pId 商品链接截取的商品id
* @return
*/
public static ProductResponse formatUniqloProductResponse(JSONObject dataMap, String pId) {
// 声明封装类
ProductResponse productResponse = new ProductResponse();
// 属性:Zara 的商品属性有颜色、尺码
Map<String, Set<ProductProp>> productPropSet = new HashMap<>(16);
// 原始价
List<OriginalPrice> originalPriceList = new ArrayList<>();
// 促销价格
List<ProductPromotion> promotionList = new ArrayList<>();
// 库存
DynStock dynStock = new DynStock();
// 其实数据没有包含确切的库存数,这里默认给足量的库存
dynStock.setSellableQuantity(9999);
// 商品基本信息
ItemInfo itemInfo = new ItemInfo();
// 取 summary 节点对象
JSONObject summaryObj = dataMap.getJSONObject("summary");
//////////////////////////////////// 获取商品基本信息 ////////////////////////////////////////////
itemInfo.setShopName("Uniqlo");
itemInfo.setShopUrl("https://www.uniqlo.cn/product-detail.html");
itemInfo.setItemId(pId);
itemInfo.setTitle(summaryObj.getString("fullName"));
//////////////////////////////////// 获取商品基本信息End(图片下取) ////////////////////////////////////////////
// 取 rows 节点数组
JSONArray rowsArr = dataMap.getJSONArray("rows");
for (int i = 0; i < rowsArr.size(); i++) {
JSONObject rowsObj = rowsArr.getJSONObject(i);
//////////////////////////////////// 获取商品颜色属性 ////////////////////////////////////////////
Set<ProductProp> propSetColor = new HashSet<>(16);
ProductProp productPropColor = new ProductProp();
// 颜色id
String colorNo = rowsObj.getString("colorNo");
productPropColor.setPropId(colorNo);
// 颜色名
String colorName = rowsObj.getString("style");
productPropColor.setPropName(colorName);
// 颜色图片
String imageUrl = "https://www.uniqlo.cn/hmall/test/" + pId + "/sku/561/" + colorNo + ".jpg";
productPropColor.setImage(imageUrl);
if (i == 0) {
itemInfo.setPic(imageUrl);
}
propSetColor.add(productPropColor);
if (productPropSet.get("颜色") == null) {
productPropSet.put("颜色", propSetColor);
} else {
Set<ProductProp> oldPropSet = productPropSet.get("颜色");
propSetColor.addAll(oldPropSet);
productPropSet.put("颜色", propSetColor);
}
//////////////////////////////////// 获取商品颜色属性 END ////////////////////////////////////////////
///////////////////////// 获取商品尺码属性 ////////////////////
Set<ProductProp> sizePropSetSize = new HashSet<>();
ProductProp productPropSize = new ProductProp();
String size = rowsObj.getString("sizeText");
productPropSize.setPropName(size);
String sizeNo = rowsObj.getString("sizeNo");
productPropSize.setPropId(sizeNo + "_" + size);
sizePropSetSize.add(productPropSize);
if (productPropSet.get("尺码") == null) {
productPropSet.put("尺码", sizePropSetSize);
} else {
Set<ProductProp> oldPropSet = productPropSet.get("尺码");
sizePropSetSize.addAll(oldPropSet);
productPropSet.put("尺码", sizePropSetSize);
}
///////////////////////// 获取商品尺码属性 END////////////////////
String skuStr = ";" + colorNo + ";" + sizeNo + ";";
//////////////////////////////////// 获取库存 ////////////////////////////////////////////
// 设置:商品包含库存信息
productResponse.setStockFlag(true);
List<ProductSkuStock> productSkuStockList = dynStock.getProductSkuStockList();
if (productSkuStockList == null) {
productSkuStockList = new ArrayList<>();
}
ProductSkuStock productSkuStock = new ProductSkuStock();
// 设置:可用库存值,Uniqlo 未有可用的库存数据
productSkuStock.setSellableQuantity(999);
// 设置:库存对应的id
productSkuStock.setSkuStr(skuStr);
productSkuStockList.add(productSkuStock);
dynStock.setProductSkuStockList(productSkuStockList);
//////////////////////////////////// 获取库存 END/////////////////////////////////////////
//////////////////////////////////// 获取原始价 //////////////////////////////////
OriginalPrice originalPrice = new OriginalPrice();
// 获取商品的原始价
String fullPrice = summaryObj.getString("originPrice");
// TODO 转换汇率,目前商品单位是人民币
// TODO Uniqlo 无法获取折扣后的价格
fullPrice= exchangeRate(fullPrice);
originalPrice.setPrice(fullPrice);
originalPrice.setSkuStr(skuStr);
originalPriceList.add(originalPrice);
//////////////////////////////////// 获取原始价 END//////////////////////////////////
}
// 按照一下顺序进行 json 数据的填充
productResponse.setPropFlag(true);
productResponse.setProductPropSet(productPropSet);
productResponse.setPlatform(PlatformEnum.UNIQLO.getValue());
productResponse.setPromotionList(promotionList);
productResponse.setOriginalPriceList(originalPriceList);
productResponse.setItemInfo(itemInfo);
productResponse.setDynStock(dynStock);
return productResponse;
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论