452 lines
18 KiB
C#
452 lines
18 KiB
C#
|
using Api.Framework.Cps;
|
|||
|
using Api.Framework.Model;
|
|||
|
using CsharpHttpHelper;
|
|||
|
using CsharpHttpHelper.Enum;
|
|||
|
using Newtonsoft.Json;
|
|||
|
using Newtonsoft.Json.Linq;
|
|||
|
using System;
|
|||
|
using System.Collections;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Linq;
|
|||
|
using System.Text;
|
|||
|
using System.Text.RegularExpressions;
|
|||
|
using System.Threading;
|
|||
|
using System.Threading.Tasks;
|
|||
|
|
|||
|
namespace Api.Framework.Tools
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// 拼多多解析助手
|
|||
|
/// </summary>
|
|||
|
public class PDDHelper
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// 随机对象
|
|||
|
/// </summary>
|
|||
|
private static Random random = new Random(Guid.NewGuid().GetHashCode());
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 获取随机公共拼多多查询商品信息账号
|
|||
|
/// </summary>
|
|||
|
/// <returns></returns>
|
|||
|
public static fl_cps_member RandomGuidePddMembers()
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
//return CpsClient.GuideMembers[0];
|
|||
|
return CpsClient.GuideMembers[random.Next(0, CpsClient.GuideMembers.Count)];
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{ }
|
|||
|
return CpsClient.GuideMembers.FirstOrDefault();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 多多进宝app中获取高佣商品的招商id
|
|||
|
/// </summary>
|
|||
|
/// <param name="goodsid">商id</param>
|
|||
|
/// <returns></returns>
|
|||
|
public static string FindGoodsSign_H5(string goodsid)
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
if (GoodsCacheDic.ContainsKey(goodsid))
|
|||
|
{
|
|||
|
var GoodsCache = GoodsCacheDic[goodsid];
|
|||
|
if (GoodsCache.GetTime.AddHours(1) > DateTime.Now)
|
|||
|
return GoodsCache.GoodsSign;
|
|||
|
else
|
|||
|
GoodsCacheDic.Remove(goodsid);
|
|||
|
}
|
|||
|
}
|
|||
|
catch (Exception)
|
|||
|
{ }
|
|||
|
|
|||
|
for (int i = 0; i < 3; i++)
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
HttpHelper http = new HttpHelper();
|
|||
|
HttpItem item = new HttpItem()
|
|||
|
{
|
|||
|
URL = "https://jinbaoh5.pinduoduo.com/network_h5/weak_auth/goods/query_goodslist",
|
|||
|
Method = "POST",
|
|||
|
Timeout = 100000,
|
|||
|
ReadWriteTimeout = 30000,
|
|||
|
IsToLower = false,
|
|||
|
Cookie = "",
|
|||
|
UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:18.0) Gecko/20100101 Firefox/18.0",
|
|||
|
Accept = "text/html, application/xhtml+xml, */*",
|
|||
|
ContentType = "application/json",
|
|||
|
Referer = "",
|
|||
|
Allowautoredirect = false,
|
|||
|
AutoRedirectCookie = false,
|
|||
|
Postdata = @"{""hasCoupon"": false,""pageNumber"": 1,""sortType"": 0,""listId"": """",""pageSize"":10,""keyword"":""" + goodsid + @"""}",
|
|||
|
ResultType = ResultType.String
|
|||
|
};
|
|||
|
HttpResult result = http.GetHtml(item);
|
|||
|
string html = result.Html;
|
|||
|
|
|||
|
var jobj = JObject.Parse(html);
|
|||
|
if (!(bool)jobj["success"])
|
|||
|
continue;
|
|||
|
else
|
|||
|
{
|
|||
|
var goodsList = (JArray)jobj["result"]["goodsList"];
|
|||
|
for (int z = 0; z < goodsList.Count; z++)
|
|||
|
{
|
|||
|
//var zsDuoId = (int)goodsList[0]["zsDuoId"];
|
|||
|
//if (zsDuoId == 0) return string.Empty;
|
|||
|
//return zsDuoId.ToString();
|
|||
|
var goodsSign = goodsList[0]["goodsSign"].ToString();
|
|||
|
if (!string.IsNullOrWhiteSpace(goodsSign))
|
|||
|
{
|
|||
|
return goodsSign;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{ }
|
|||
|
finally
|
|||
|
{
|
|||
|
Thread.Sleep(200);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{ }
|
|||
|
return string.Empty;
|
|||
|
}
|
|||
|
|
|||
|
private static Dictionary<string, GoodsCahce> GoodsCacheDic = new Dictionary<string, GoodsCahce>();
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 清理缓存时间
|
|||
|
/// </summary>
|
|||
|
private static DateTime remove_cache_time = DateTime.Now;
|
|||
|
|
|||
|
#region 临时类
|
|||
|
class GoodsCahce
|
|||
|
{
|
|||
|
public string GoodsSign { get; set; }
|
|||
|
public DateTime GetTime { get; set; }
|
|||
|
}
|
|||
|
#endregion
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 缓存异常Cps集合
|
|||
|
/// </summary>
|
|||
|
private static Dictionary<string, string> CacheErrCpsDic = new Dictionary<string, string>();
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 获取商品高佣信息
|
|||
|
/// </summary>
|
|||
|
/// <param name="api">PinduoduoApi对象</param>
|
|||
|
/// <param name="pid">推广位pid</param>
|
|||
|
/// <param name="goods_id">商品id</param>
|
|||
|
/// <param name="isBind">是否授权绑定</param>
|
|||
|
/// <returns></returns>
|
|||
|
public static ArrayList FindSendGuideGoodsInfo(PinduoduoApi api, string pid, string goods_id, bool isBind = false)
|
|||
|
{
|
|||
|
#region 清理缓存
|
|||
|
if (remove_cache_time.AddHours(5) < DateTime.Now)
|
|||
|
{
|
|||
|
remove_cache_time = DateTime.Now;
|
|||
|
Task.Run(() =>
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
var tmps = GoodsCacheDic.Where(f => f.Value.GetTime.AddHours(1) < DateTime.Now).ToList();
|
|||
|
if (tmps == null || tmps.Count == 0) return;
|
|||
|
for (int i = 0; i < tmps.Count; i++)
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
GoodsCacheDic.Remove(tmps[i].Key);
|
|||
|
}
|
|||
|
catch (Exception)
|
|||
|
{ }
|
|||
|
}
|
|||
|
}
|
|||
|
catch (Exception)
|
|||
|
{ }
|
|||
|
});
|
|||
|
}
|
|||
|
#endregion
|
|||
|
|
|||
|
//获取高佣H5信息
|
|||
|
var goodsSign = FindGoodsSign_H5(goods_id);
|
|||
|
if (!string.IsNullOrWhiteSpace(goodsSign))
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
if (!GoodsCacheDic.ContainsKey(goods_id))
|
|||
|
GoodsCacheDic[goods_id] = new GoodsCahce() { GoodsSign = goodsSign, GetTime = DateTime.Now };
|
|||
|
}
|
|||
|
catch (Exception)
|
|||
|
{ }
|
|||
|
if (!isBind)//true-已绑定;false-未绑定
|
|||
|
{ //公用的账号
|
|||
|
var guideCps = RandomGuidePddMembers();
|
|||
|
if (guideCps == null) throw new Exception("公共账号异常");
|
|||
|
var guideApi = CpsClient.CreatePinduoduoRequest(guideCps);
|
|||
|
if (guideApi == null) throw new Exception("创建公共请求对象异常");
|
|||
|
var fgInfo = FindGoodsInfo(guideApi, goodsSign, guideCps.cookies);
|
|||
|
if (fgInfo != null)
|
|||
|
return fgInfo;
|
|||
|
}
|
|||
|
var goodsInfo = FindGoodsInfo(api, goodsSign, pid);
|
|||
|
return goodsInfo;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
PinduoduoApi apiTmp = null;
|
|||
|
var pidTmp = pid;
|
|||
|
var cpsUsername = api.Member.username;
|
|||
|
//无法获取高佣这里获取低佣
|
|||
|
for (int i = 0; i < 2; i++)
|
|||
|
{
|
|||
|
Dictionary<string, object> obj_ = null;
|
|||
|
//if (!isBind)//true-已绑定;false-未绑定
|
|||
|
if (CacheErrCpsDic.ContainsKey(cpsUsername) || !isBind)//true-已绑定;false-未绑定
|
|||
|
{ //公用的账号
|
|||
|
var guideCps = RandomGuidePddMembers();
|
|||
|
if (guideCps == null) throw new Exception("公共账号异常");
|
|||
|
var guideApi = CpsClient.CreatePinduoduoRequest(guideCps);
|
|||
|
if (guideApi == null) throw new Exception("创建公共请求对象异常");
|
|||
|
apiTmp = guideApi;
|
|||
|
pidTmp = guideCps.cookies;
|
|||
|
}
|
|||
|
else
|
|||
|
apiTmp = api;
|
|||
|
if (apiTmp == null) continue;
|
|||
|
try
|
|||
|
{
|
|||
|
obj_ = apiTmp.SendPinduoduo("pdd.ddk.oauth.goods.search", new
|
|||
|
{
|
|||
|
keyword = goods_id,
|
|||
|
pid = pidTmp,
|
|||
|
//custom_parameters = "{\"uid\":\"602172712\",\"sid\":\"jdds\"}"
|
|||
|
//page = 1,
|
|||
|
//page_size = 10,
|
|||
|
//with_coupon = true
|
|||
|
});
|
|||
|
|
|||
|
if (obj_ == null) throw new Exception("创建拼多多API请求失败3");
|
|||
|
|
|||
|
var goods_detail_response = obj_["goods_search_response"] as Dictionary<string, object>;
|
|||
|
var goods_list = goods_detail_response["goods_list"] as ArrayList;
|
|||
|
|
|||
|
if (goods_list != null && goods_list.Count != 0)
|
|||
|
{
|
|||
|
var tmp = goods_list[0] as Dictionary<string, object>;
|
|||
|
if (tmp != null && tmp.Count == 2)
|
|||
|
return FindGoodsInfo(api, tmp["goods_sign"].ToString(), pid);
|
|||
|
}
|
|||
|
return goods_list;
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
if (ex.Message.Contains("涉嫌爬虫或本地缓存商品信息"))
|
|||
|
{
|
|||
|
CacheErrCpsDic[apiTmp.Member.username] = apiTmp.Member.username;
|
|||
|
cpsUsername = apiTmp.Member.username;
|
|||
|
}
|
|||
|
EventClient.OnEvent(null, $"拼多多:public -> {apiTmp.Member.username} 异常:{ex.Message} - {ex.StackTrace} - {(obj_ == null ? string.Empty : JsonConvert.SerializeObject(obj_))}");
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
Thread.Sleep(200);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 获取商品高佣信息
|
|||
|
/// </summary>
|
|||
|
/// <param name="api">PinduoduoApi对象</param>
|
|||
|
/// <param name="data">参数</param>
|
|||
|
/// <returns></returns>
|
|||
|
public static ArrayList FindSendGuideGoodsInfo(PinduoduoApi api, object data)
|
|||
|
{
|
|||
|
//公用的账号
|
|||
|
var guideCps = RandomGuidePddMembers();
|
|||
|
if (guideCps == null) throw new Exception("公共账号异常");
|
|||
|
var guideApi = CpsClient.CreatePinduoduoRequest(guideCps);
|
|||
|
if (guideApi == null) throw new Exception("创建公共请求对象异常");
|
|||
|
var obj_ = guideApi.SendPinduoduo("pdd.ddk.oauth.goods.search", data);
|
|||
|
if (obj_ == null) throw new Exception("创建拼多多API请求失败2");
|
|||
|
//解析商品,获取商品的信息
|
|||
|
var goods_detail_response = obj_["goods_search_response"] as Dictionary<string, object>;
|
|||
|
return goods_detail_response["goods_list"] as ArrayList;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 查询商品信息(自己的账号,以保佣金比例的正确性)
|
|||
|
/// </summary>
|
|||
|
/// <param name="api">PinduoduoApi对象</param>
|
|||
|
/// <param name="goods_sign">商品sign</param>
|
|||
|
/// <param name="pid">推广位pid</param>
|
|||
|
/// <returns></returns>
|
|||
|
public static ArrayList FindGoodsInfo(PinduoduoApi api, string goods_sign, string pid)
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
var obj_ = api.SendPinduoduo("pdd.ddk.oauth.goods.search", new { goods_sign_list = $"[\"{goods_sign}\"]", pid = pid });
|
|||
|
|
|||
|
if (obj_ == null) throw new Exception("创建拼多多API请求失败2");
|
|||
|
//解析商品,获取商品的信息
|
|||
|
var goods_detail_response = obj_["goods_search_response"] as Dictionary<string, object>;
|
|||
|
return goods_detail_response["goods_list"] as ArrayList;
|
|||
|
|
|||
|
|
|||
|
//var obj_ = api.SendPinduoduo("pdd.ddk.goods.detail", new { goods_sign = goods_sign, pid = pid });
|
|||
|
|
|||
|
//if (obj_ == null) throw new Exception("创建拼多多API请求失败2");
|
|||
|
////解析商品,获取商品的信息
|
|||
|
//var goods_detail_response = obj_["goods_detail_response"] as Dictionary<string, object>;
|
|||
|
//return goods_detail_response["goods_details"] as ArrayList;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{ }
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 获取拼多多链接中的商品ID
|
|||
|
/// </summary>
|
|||
|
/// <param name="url">拼多多商品连接</param>
|
|||
|
/// <returns></returns>
|
|||
|
public static string GetPddGoodsID(string url)
|
|||
|
{
|
|||
|
bool flag = false;
|
|||
|
if (url.Contains("goods_id")) flag = true;
|
|||
|
|
|||
|
var _url = HttpExtend.RegexMatchUrl(url);
|
|||
|
if (!flag && !string.IsNullOrWhiteSpace(_url))//有链接的情况
|
|||
|
{
|
|||
|
//TODO 这里好像还有短连接 目前忘记了短连接的格式
|
|||
|
|
|||
|
url = _url;
|
|||
|
if (Regex.IsMatch(url, "(http[s]?://url.cn|http[s]?://a.toutiaonanren.com|http[s]?://p.pinduoduo.com)", RegexOptions.IgnoreCase))
|
|||
|
{
|
|||
|
//可能为短网址//跳转换成真实地址
|
|||
|
url = HttpHelper.URLDecode(GetLocation(url));
|
|||
|
if (url == null || !url.Contains("goods_id")) return string.Empty;
|
|||
|
else flag = true;
|
|||
|
}
|
|||
|
else
|
|||
|
flag = true;
|
|||
|
}
|
|||
|
if (flag)
|
|||
|
{
|
|||
|
var reg = Regex.Match(url, @"goods_id=(?<good_id>\d{4,})");
|
|||
|
if (reg.Success)
|
|||
|
return reg.Groups["good_id"].ToString();
|
|||
|
reg = Regex.Match(HttpHelper.URLDecode(url), @"goods_id=(?<good_id>\d{4,})");
|
|||
|
if (reg.Success)
|
|||
|
return reg.Groups["good_id"].ToString();
|
|||
|
reg = Regex.Match(HttpHelper.URLDecode(url), @"pinduoduo(?:.*)?goodsId=(?<good_id>\d{4,})", RegexOptions.IgnoreCase);
|
|||
|
if (reg.Success)
|
|||
|
return reg.Groups["good_id"].ToString();
|
|||
|
}
|
|||
|
return string.Empty;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 获取页面的真实Location的地址(比如:获取短连接的真实地址)
|
|||
|
/// </summary>
|
|||
|
/// <param name="url">链接</param>
|
|||
|
/// <returns></returns>
|
|||
|
private static string GetLocation(string url)
|
|||
|
{
|
|||
|
HttpHelper http = new HttpHelper();
|
|||
|
HttpItem item = new HttpItem()
|
|||
|
{
|
|||
|
URL = url,
|
|||
|
Method = "GET",
|
|||
|
Timeout = 100000,
|
|||
|
ReadWriteTimeout = 30000,
|
|||
|
IsToLower = false,
|
|||
|
Cookie = "",
|
|||
|
UserAgent = "Opera/9.27 (Windows NT 5.2; U; zh-cn)",
|
|||
|
Accept = "text/html, application/xhtml+xml, */*",
|
|||
|
ContentType = "text/html",
|
|||
|
Referer = "",
|
|||
|
Postdata = "",
|
|||
|
};
|
|||
|
HttpResult result = http.GetHtml(item);
|
|||
|
return result.Header.Get("Location");
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 获取单人团 / 多人团地址
|
|||
|
/// </summary>
|
|||
|
/// <param name="api">PinduoduoApi对象</param>
|
|||
|
/// <param name="obj">接口的必要参数</param>
|
|||
|
/// <returns></returns>
|
|||
|
public string GetShoppingUrl(PinduoduoApi api, object obj)
|
|||
|
{
|
|||
|
for (int i = 0; i < 3; i++)
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
var goods_promotion_url_generate_response = api.SendPinduoduo("pdd.ddk.oauth.goods.prom.url.generate", obj)["goods_promotion_url_generate_response"] as Dictionary<string, object>;
|
|||
|
|
|||
|
var goods_promotion_url_list = goods_promotion_url_generate_response["goods_promotion_url_list"] as ArrayList;
|
|||
|
string url = string.Empty;
|
|||
|
foreach (var _url in goods_promotion_url_list)
|
|||
|
{
|
|||
|
//return (_url as Dictionary<string, object>)["short_url"].ToString();
|
|||
|
return (_url as Dictionary<string, object>)["mobile_short_url"].ToString();
|
|||
|
}
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{ }
|
|||
|
Thread.Sleep(100);
|
|||
|
}
|
|||
|
return string.Empty;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
///
|
|||
|
/// </summary>
|
|||
|
/// <param name="api">PinduoduoApi对象</param>
|
|||
|
/// <param name="obj">参数</param>
|
|||
|
/// <returns></returns>
|
|||
|
public Dictionary<string, object> GetShoppingUrls(PinduoduoApi api, object obj)
|
|||
|
{
|
|||
|
for (int i = 0; i < 2; i++)
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
var goods_promotion_url_generate_response = api.SendPinduoduo("pdd.ddk.oauth.goods.prom.url.generate", obj)["goods_promotion_url_generate_response"] as Dictionary<string, object>;
|
|||
|
|
|||
|
var goods_promotion_url_list = goods_promotion_url_generate_response["goods_promotion_url_list"] as ArrayList;
|
|||
|
string url = string.Empty;
|
|||
|
foreach (var _url in goods_promotion_url_list)
|
|||
|
{
|
|||
|
return (_url as Dictionary<string, object>);
|
|||
|
}
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
EventClient.OnEvent(this, $"生成拼多多推广连接失败:{ex.Message},{ex.StackTrace}");
|
|||
|
}
|
|||
|
Thread.Sleep(100);
|
|||
|
}
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
}
|