|
|
@@ -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<List<String>> loadImages(String id) async { |
|
|
|
return _loadImagesNew(id); |
|
|
|
} |
|
|
|
|
|
|
|
static Future<List<String>> _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<List<String>> _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<String, dynamic> 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<Cookie> 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<String> 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<String, dynamic> 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<String> images = List(); |
|
|
|
if (null != model.data.images && model.data.images.length > 0) { |
|
|
|
/** |
|
|
|
* images字段的数据,在前面拼接好https:就可以作为商品详情的图片链接使用了 |
|
|
|
*/ |
|
|
|
List<String> Images = model.data.images; |
|
|
|
List<String> 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字段的数据,数组中可能有多张图片的链接数据,以<img></img>标签框起来的 |
|
|
|
* 先去掉前面的<img>标签,再以</img>标签分割各个图片链接数据, |
|
|
|
* 最后每个数据拼接上https:就可以作为商品详情图片链接返回了 |
|
|
|
* 经测试images字段和pages字段的数据是相同的,所以取一个字段的数据返回就可以了 |
|
|
|
*/ |
|
|
|
List<String> pages = model.data.pages; |
|
|
|
List<String> mPicList = []; |
|
|
|
for (String pic in pages) { |
|
|
|
String pic1 = pic.replaceAll("<img>", ""); |
|
|
|
List<String> pic2 = pic1.split("</img>"); |
|
|
|
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); |
|
|
|
} |
|
|
|
} |