diff --git a/lib/util/taobao/goods_pic_model.dart b/lib/util/taobao/goods_pic_model.dart new file mode 100644 index 0000000..b6ec13c --- /dev/null +++ b/lib/util/taobao/goods_pic_model.dart @@ -0,0 +1,45 @@ +class GoodsPicModel { + String api; + Data data; + List ret; + String v; + + GoodsPicModel({this.api, this.data, this.ret, this.v}); + + GoodsPicModel.fromJson(Map json) { + api = json['api']; + data = json['data'] != null ? new Data.fromJson(json['data']) : null; + ret = json['ret'].cast(); + v = json['v']; + } + + Map toJson() { + final Map data = new Map(); + data['api'] = this.api; + if (this.data != null) { + data['data'] = this.data.toJson(); + } + data['ret'] = this.ret; + data['v'] = this.v; + return data; + } +} + +class Data { + List pages; + List images; + + Data({this.pages, this.images}); + + Data.fromJson(Map json) { + pages = json['pages'].cast(); + images = json['images'].cast(); + } + + Map toJson() { + final Map data = new Map(); + data['pages'] = this.pages; + data['images'] = this.images; + return data; + } +} \ No newline at end of file diff --git a/lib/util/taobao/taobao_loader.dart b/lib/util/taobao/taobao_loader.dart index c30c8f9..a318a33 100644 --- a/lib/util/taobao/taobao_loader.dart +++ b/lib/util/taobao/taobao_loader.dart @@ -1,9 +1,22 @@ +import 'dart:convert'; +import 'dart:io'; + import 'package:dio/dio.dart'; +import 'package:dio_cookie_manager/dio_cookie_manager.dart'; +import 'package:zhiying_comm/util/taobao/goods_pic_model.dart'; import 'package:zhiying_comm/zhiying_comm.dart'; +import 'package:cookie_jar/cookie_jar.dart'; +import 'package:convert/convert.dart'; +import 'package:crypto/crypto.dart'; + // 淘宝详情图抓取 class TaobaoLoader { static Future> loadImages(String id) async { + return _loadImagesNew(id); + } + + static Future> _loadImages1(String id) async { String url = 'http://h5api.m.taobao.com/h5/mtop.taobao.detail.getdesc/6.0/?api=mtop.taobao.detail.getdesc&appkey=12574478&data={"type":"0","f":"TB1AsvbfnqWBKNjSZFx8qwpLpla","id":"${id.toString()}"}&dataType=json&jsv=2.4.11&sign=e93a97b7e9a9a459297f4e689051c895&t=1538180732308&type=json'; Dio dio = Dio(); @@ -21,8 +34,7 @@ class TaobaoLoader { if (image != null && image != '') { if (image.startsWith('//')) { images.add('http:' + image); - } else - if (image.startsWith('https://') || image.startsWith('http://')) { + } else if (image.startsWith('https://') || image.startsWith('http://')) { images.add(image); } } @@ -31,7 +43,130 @@ class TaobaoLoader { Logger.error(err); } + return images; + } + + ///方法说明:通过破解淘宝签名接口获取商品详情图片 + ///流程:第一次请求接口拿到cookie数据,拼接签名数据,第二次请求接口拿到图片连接数据 + ///方法地址 + ///https://darktiantian.github.io/%E8%AE%B0%E4%B8%80%E6%AC%A1%E6%B7%98%E5%AE%9D%E6%8E%A5%E5%8F%A3sign%E7%AD%BE%E5%90%8D%E7%A0%B4%E8%A7%A3/ + static Future> _loadImagesNew(String id) async { + String data = "{\"item_num_id\":\"" + id + "\"}"; + String time = DateTime.now().millisecondsSinceEpoch.toString(); + String appkey = "12574478"; //文章中使用的appkey + String url = "https://h5api.m.taobao.com/h5/mtop.wdetail.getitemdescx/4.9/?jsv=2.4.11&appKey=$appkey&t=$time" + "&sign=FAKE_SIGN_WITH_ANYTHING&api=mtop.wdetail.getItemDescx&v=4.9&type=jsonp&dataType=jsonp&callback=mtopjsonp2&data=" + "$data"; + Logger.log("url: " + url); + Dio dio = Dio(BaseOptions(responseType: ResponseType.plain)); + var cookieJar = CookieJar(); + //dio绑定cookie管理器,以便第一次请求后获取cookie数据 + dio.interceptors.add(CookieManager(cookieJar)); + Map headers = new Map(); + headers['User-Agent'] = + "Mozilla/5.0 (iPhone; CPU iPhone OS 9_3_4 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13G35 Safari/601.1"; + Options options = new Options(headers: headers); + //首次请求获取cookie + await dio.get(url, options: options); + + String token = ""; + String _m_h5_tk = ""; + String enc_token = ""; + //根据第一次请求后返回的cookie拿到需要的数据 + List cookieList = cookieJar.loadForRequest(Uri.parse(url)); + for (int i = 0; i < cookieList.length; i++) { + Cookie cookie = cookieList[i]; + Logger.log(cookie.name + " : " + cookie.value); + if ("_m_h5_tk" == cookie.name) { + _m_h5_tk = cookie.value; + List token_with_time = cookie.value.split("_"); + if (token_with_time.length > 0) { + token = token_with_time[0]; + } + } + if ("_m_h5_tk_enc" == cookie.name) { + enc_token = cookie.value; + } + } + + //设置第二次请求的cookie数据 + Map headers2 = new Map(); + headers2['cookie'] = "_m_h5_tk=$_m_h5_tk; _m_h5_tk_enc=$enc_token;" + "User-Agent=Mozilla/5.0 (iPhone; CPU iPhone OS 9_3_4 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13G35 Safari/601.1"; + Options options2 = new Options(headers: headers2); + + //设置签名 + String time2 = DateTime.now().millisecondsSinceEpoch.toString(); + String sign = token + "&" + time2 + "&" + appkey + "&" + data; + String mySign = string2Md5(sign); + + //设置第二次请求的url + String url2 = "https://h5api.m.taobao.com/h5/mtop.wdetail.getitemdescx/4.9/?jsv=2.4.11&appKey=$appkey&t=$time2" + "&sign=$mySign&api=mtop.wdetail.getItemDescx&v=4.9&type=jsonp&dataType=jsonp&callback=mtopjsonp2&data=" + "$data"; + + //进行第二次网络请求拿到图片数据 + Dio dio2 = Dio(BaseOptions(responseType: ResponseType.plain)); + Response response2 = await dio2.get(url2, options: options2); + Logger.log(response2.data); + + //根据返回的数据,拼接图片链接数据 + String result2 = response2.data; + String object = result2.substring(result2.indexOf("(") + 1, result2.length - 1); + Logger.log(object); + var json = jsonDecode(object); + GoodsPicModel model = GoodsPicModel.fromJson(json); + List images = List(); + if (null != model.data.images && model.data.images.length > 0) { + /** + * images字段的数据,在前面拼接好https:就可以作为商品详情的图片链接使用了 + */ + List Images = model.data.images; + List mPicList = []; + for (String pic in Images) { + if (pic.isNotEmpty) { + mPicList.add("https:" + pic); + } + } + Logger.log("mPicList图片:" + mPicList.length.toString() + "\n" + mPicList.toString()); + if (mPicList.length > 0) { + return mPicList; + } + } else { + if (null != model.data.pages && model.data.pages.length > 0) { + /** + * pages字段的数据,数组中可能有多张图片的链接数据,以标签框起来的 + * 先去掉前面的标签,再以标签分割各个图片链接数据, + * 最后每个数据拼接上https:就可以作为商品详情图片链接返回了 + * 经测试images字段和pages字段的数据是相同的,所以取一个字段的数据返回就可以了 + */ + List pages = model.data.pages; + List mPicList = []; + for (String pic in pages) { + String pic1 = pic.replaceAll("", ""); + List pic2 = pic1.split(""); + if (pic2.length > 0) { + for (String s in pic2) { + if (s.isNotEmpty) { + mPicList.add("https:" + s); + } + } + } + } + Logger.log("图片2:" + mPicList.length.toString() + "\n" + mPicList.toString()); + if (mPicList.length > 0) { + return mPicList; + } + } + } return images; } + + // md5 加密 + static String string2Md5(String data) { + var content = new Utf8Encoder().convert(data); + var digest = md5.convert(content); + return hex.encode(digest.bytes); + } } diff --git a/pubspec.yaml b/pubspec.yaml index 39d8f8d..3b9fc13 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -74,6 +74,7 @@ dependencies: #保存文件到相册 save_image: ^1.0.1 + dio_cookie_manager: 1.0.0 dev_dependencies: