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(); Response response = await dio.get(url); // Logger.debug(response.data.toString()); Map json = Map.from(response.data); List images = List(); try { Map data = json['data']; List list = json['data']['wdescContent']['pages']; for (var item in list) { Logger.debug(item); RegExp exp = new RegExp("(?<=)(.*?)(?=)"); String image = exp.stringMatch(item.toString()); if (image != null && image != '') { if (image.startsWith('//')) { images.add('http:' + image); } else if (image.startsWith('https://') || image.startsWith('http://')) { images.add(image); } } } } catch (err) { 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) { if (pic.startsWith("http")) { mPicList.add(pic); } else { 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) { if (s.startsWith("http")) { mPicList.add(s); } else { 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); } }