Tb/Tmall批量获取商品详情

item_get-获得JD商品详情 

onebound.jd.item_get

公共参数

名称类型必须描述
keyString调用key(必须以GET方式拼接在URL中)
secretString调用密钥
api_nameStringAPI接口名称(包括在请求地址中)[item_search,item_get,item_search_shop等]
cacheString[yes,no]默认yes,将调用缓存的数据,速度比较快
result_typeString[json,jsonu,xml,serialize,var_export]返回数据格式,默认为json,jsonu输出的内容中文可以直接阅读
langString[cn,en,ru]翻译语言,默认cn简体中文
versionStringAPI版本

请求参数

请求参数:num_iid=10335871600

参数说明:num_iid:JD商品ID

响应参数

名称类型是否隐私示例值描述
itemsitems[]获得JD商品详情
num_iidBigint29186819959商品ID
titleStringMOCO2018夏季新品时尚V领条纹连衣裙 摩安珂 蓝白条色 S商品标题
desc_shortString商品简介
priceFloat719.0价格
total_priceFloat0
suggestive_priceFloat0
orginal_priceFloat1199.00原价
nickStringMO&Co.官方旗舰店掌柜昵称
numInt999
min_numInt0
detail_urlStringhttp://item.jd.com/29186819959.html商品链接
pic_urlString//img14.360buyimg.com/n0/jfs/t22033/147/1051007175/85125/c44dd0df/5b1f2855Ncbe35858.jpg商品图片
brandString品牌名称
brandIdInt品牌ID
rootCatIdInt1343顶级分类ID
cidInt9719
crumbsMix[]
created_timeString
modified_timeString
delist_timeString
descString
desc_imgMix[]
item_imgsMix[{ "url": "//img14.360buyimg.com/n0/jfs/t22033/147/1051007175/85125/c44dd0df/5b1f2855Ncbe35858.jpg"}]商品图片
item_weightString
item_sizeString
locationString发货地
post_feeFloat6.00物流费用
express_feeFloat6.00快递费用
ems_feeFloat6.00EMS费用
shipping_toString发货至
has_discountBooleanfalse
videoMix[]商品视频
is_virtualString
sample_idString商品风格标识ID
is_promotionBoolean
props_nameString0:0:尺码:S;0:1:尺码:XS;0:2:尺码:M;0:3:尺码:L;0:4:尺码:XL商品属性名
prop_imgsMix{"prop_img": []}商品属性图片列表
property_aliasString0:0:S;0:1:XS;0:2:M;0:3:L;0:4:XL商品属性别名
propsMix[{ "name": "尺码","value": "S XS M L XL" }]商品详情
total_soldInt
skusMix{"sku": [{"price": "719.00", "orginal_price": "1199.00", "properties": "0:0", "properties_name": "0:0:尺码:S", "quantity": 99, "sku_id": 29186819959, "sku_url": "http://item.jd.com/29186819959.html"}]商品规格信息
seller_idInt卖家ID
salesInt销量
shop_idInt店铺ID
props_listMix{"0:0": "尺码:S"}商品属性
seller_infoMix{"level": null, "shop_type": null, "user_num_id": 57467, "cid": null, "delivery_score": null, "item_score": null, "score_p": null, "zhuy": "//moco.jd.com", "search_id": "", "nick": "MO&Co.官方旗舰店", "shop_name": "MO&Co.官方旗舰店", "title": "MO&Co.官方旗舰店" }卖家信息
tmallBooleanfalse是否天猫
errorString错误信息
warningString警告信息
url_logMix[]
props_imgMix[]属性图片
shop_itemMix[]
relate_itemsMix[]

请求示例

	
-- 请求示例 url 默认请求参数已经URL编码处理
curl -i "https://api-gw.onebound.cn/jd/item_get/?key=<您自己的apiKey>&secret=<您自己的apiSecret>&num_iid=10335871600"

响应示例

{
	"item": {
		"num_iid": "10335871600",
		"title": "安踏男鞋休闲运动鞋男士2021春季网面透气轻便板鞋慢跑步鞋子户外训练旅游 -6安踏白 42",
		"desc_short": "",
		"price": "159.00",
		"total_price": 0,
		"suggestive_price": 0,
		"orginal_price": "249.00",
		"nick": "安踏悠购专卖店",
		"num": 999,
		"min_num": 0,
		"detail_url": "https://item.jd.com/10335871600.html",
		"pic_url": "//img10.360buyimg.com/n1/jfs/t1/161425/26/11624/223402/6048750bE7b82f057/24b57f419c846302.jpg",
		"brand": "",
		"brandId": "",
		"rootCatId": 12099,
		"cid": 9756,
		"crumbs": [],
		"created_time": "",
		"modified_time": "",
		"delist_time": "",
		"desc": "<p><br></p><p><img src=\"https://img10.360buyimg.com/imgzone/jfs/t1/38380/20/8577/216030/5cfdcf2eE3d3c15fc/9072ee4986b3f8d4.jpg\"><img src=\"http://img30.360buyimg.com/popWaterMark/jfs/t11302/317/3187030400/282664/5886b268/5ce3c383N6572b1da.jpg\"><br></p><p><img src=\"http://img30.360buyimg.com/popWaterMark/jfs/t29380/248/1583857170/211757/8803264c/5ce3c383Nf658d385.jpg\"><img src=\"http://img30.360buyimg.com/popWaterMark/jfs/t1/109234/40/8433/163092/5e6764a2E4e1a8595/47322c3588d6d0c8.jpg\"><img src=\"http://img30.360buyimg.com/popWaterMark/jfs/t1/89006/11/14706/167699/5e6764a2E68bdab3a/be235af214561ca7.jpg\"><img src=\"http://img30.360buyimg.com/popWaterMark/jfs/t1/109389/11/8312/121412/5e6764a2E0699edcb/74e48d00298808af.jpg\"><img src=\"http://img30.360buyimg.com/popWaterMark/jfs/t1/93399/24/14627/132014/5e6764a3E0f078e8e/8598eb9176d64a6c.jpg\"><img src=\"http://img30.360buyimg.com/popWaterMark/jfs/t1/107443/10/8345/114390/5e6764a3Ea1726cee/45cfa1a0f7a8df0c.jpg\"></p><p><img src=\"http://img30.360buyimg.com/popWaterMark/jfs/t1/96343/40/10991/241473/5e257548Ed6f96ea3/b52ab120eb9f7db0.jpg\" ><img src=\"http://img30.360buyimg.com/popWaterMark/jfs/t1/101185/7/11047/209310/5e257548E8788bdf6/c20d5eb0405dde28.jpg\" ><img src=\"http://img30.360buyimg.com/popWaterMark/jfs/t1/107815/5/4768/276947/5e257548Ee42f500c/0ba8cd264bd603a3.jpg\" ><img src=\"http://img30.360buyimg.com/popWaterMark/jfs/t1/95922/19/10937/254828/5e257549Eaad6ebf6/7d1400f5908cb55e.jpg\" ></p><p><img src=\"https://img30.360buyimg.com/popWaterMark/jfs/t1/18297/2/6243/210375/5c5019eeE98403891/9b5ab2294d1ba205.jpg\"><br></p><p><img src=\"https://img30.360buyimg.com/popWaterMark/jfs/t1/27804/18/6412/176509/5c5019eeE5e96f053/a189a00b486e911f.jpg\"><img src=\"http://img30.360buyimg.com/popWaterMark/jfs/t1/96858/36/10825/132522/5e2575aaE6e19013f/fcb7fb8fd4943401.jpg\" ><img src=\"http://img30.360buyimg.com/popWaterMark/jfs/t1/99976/27/10982/138934/5e2575afEc3a3592a/e17eb9f780781060.jpg\" ><img src=\"http://img30.360buyimg.com/popWaterMark/jfs/t1/96877/26/10975/138715/5e25756dE20b8140b/19ae8dc80c60e881.jpg\" ><img src=\"http://img30.360buyimg.com/popWaterMark/jfs/t1/98449/30/11083/55082/5e25756dE88f3074f/7616bc2265b4432c.jpg\" ><img src=\"http://img30.360buyimg.com/popWaterMark/jfs/t1/95543/21/10832/67134/5e25756eE012754e2/70f82adbaa11adee.jpg\" ><img src=\"http://img30.360buyimg.com/popWaterMark/jfs/t1/94888/33/11004/95067/5e25756eE02e81061/61480afaef4c3b86.jpg\" ><img src=\"http://img30.360buyimg.com/popWaterMark/jfs/t1/108065/10/4797/103705/5e25756eEebecd8f5/65e124bd694bc55b.jpg\" ><img src=\"http://img30.360buyimg.com/popWaterMark/jfs/t1/85920/26/11004/124810/5e25756eEa5644682/8fbaea1d59e5be77.jpg\" ><img src=\"http://img30.360buyimg.com/popWaterMark/jfs/t1/84849/11/11057/55611/5e25756eE9f2527d0/83d5811a3712f5fd.jpg\" ><img src=\"http://img30.360buyimg.com/popWaterMark/jfs/t1/90594/7/11010/137361/5e25756eE5d4d543c/61e96678694e5ba5.jpg\"  class=\"\"><img src=\"http://img30.360buyimg.com/popWaterMark/jfs/t1/85156/23/10917/211412/5e2546caEbf42fae5/5e494f3c805915e6.jpg\" ></p><p><br></p><br/>",
		"desc_img": [
			"https://img10.360buyimg.com/imgzone/jfs/t1/38380/20/8577/216030/5cfdcf2eE3d3c15fc/9072ee4986b3f8d4.jpg",
			"http://img30.360buyimg.com/popWaterMark/jfs/t11302/317/3187030400/282664/5886b268/5ce3c383N6572b1da.jpg",
			"http://img30.360buyimg.com/popWaterMark/jfs/t29380/248/1583857170/211757/8803264c/5ce3c383Nf658d385.jpg",
			"http://img30.360buyimg.com/popWaterMark/jfs/t1/109234/40/8433/163092/5e6764a2E4e1a8595/47322c3588d6d0c8.jpg",
			"http://img30.360buyimg.com/popWaterMark/jfs/t1/89006/11/14706/167699/5e6764a2E68bdab3a/be235af214561ca7.jpg",
			"http://img30.360buyimg.com/popWaterMark/jfs/t1/109389/11/8312/121412/5e6764a2E0699edcb/74e48d00298808af.jpg",
			"http://img30.360buyimg.com/popWaterMark/jfs/t1/93399/24/14627/132014/5e6764a3E0f078e8e/8598eb9176d64a6c.jpg",
			"http://img30.360buyimg.com/popWaterMark/jfs/t1/107443/10/8345/114390/5e6764a3Ea1726cee/45cfa1a0f7a8df0c.jpg",
			"http://img30.360buyimg.com/popWaterMark/jfs/t1/96343/40/10991/241473/5e257548Ed6f96ea3/b52ab120eb9f7db0.jpg",
			"http://img30.360buyimg.com/popWaterMark/jfs/t1/101185/7/11047/209310/5e257548E8788bdf6/c20d5eb0405dde28.jpg",
			"http://img30.360buyimg.com/popWaterMark/jfs/t1/107815/5/4768/276947/5e257548Ee42f500c/0ba8cd264bd603a3.jpg",
			"http://img30.360buyimg.com/popWaterMark/jfs/t1/95922/19/10937/254828/5e257549Eaad6ebf6/7d1400f5908cb55e.jpg",
			"https://img30.360buyimg.com/popWaterMark/jfs/t1/18297/2/6243/210375/5c5019eeE98403891/9b5ab2294d1ba205.jpg",
			"https://img30.360buyimg.com/popWaterMark/jfs/t1/27804/18/6412/176509/5c5019eeE5e96f053/a189a00b486e911f.jpg",
			"http://img30.360buyimg.com/popWaterMark/jfs/t1/96858/36/10825/132522/5e2575aaE6e19013f/fcb7fb8fd4943401.jpg",
			"http://img30.360buyimg.com/popWaterMark/jfs/t1/99976/27/10982/138934/5e2575afEc3a3592a/e17eb9f780781060.jpg",
			"http://img30.360buyimg.com/popWaterMark/jfs/t1/96877/26/10975/138715/5e25756dE20b8140b/19ae8dc80c60e881.jpg",
			"http://img30.360buyimg.com/popWaterMark/jfs/t1/98449/30/11083/55082/5e25756dE88f3074f/7616bc2265b4432c.jpg",
			"http://img30.360buyimg.com/popWaterMark/jfs/t1/95543/21/10832/67134/5e25756eE012754e2/70f82adbaa11adee.jpg",
			"http://img30.360buyimg.com/popWaterMark/jfs/t1/94888/33/11004/95067/5e25756eE02e81061/61480afaef4c3b86.jpg",
			"http://img30.360buyimg.com/popWaterMark/jfs/t1/108065/10/4797/103705/5e25756eEebecd8f5/65e124bd694bc55b.jpg",
			"http://img30.360buyimg.com/popWaterMark/jfs/t1/85920/26/11004/124810/5e25756eEa5644682/8fbaea1d59e5be77.jpg",
			"http://img30.360buyimg.com/popWaterMark/jfs/t1/84849/11/11057/55611/5e25756eE9f2527d0/83d5811a3712f5fd.jpg",
			"http://img30.360buyimg.com/popWaterMark/jfs/t1/90594/7/11010/137361/5e25756eE5d4d543c/61e96678694e5ba5.jpg",
			"http://img30.360buyimg.com/popWaterMark/jfs/t1/85156/23/10917/211412/5e2546caEbf42fae5/5e494f3c805915e6.jpg"
		],
		"item_imgs": [
			{
				"url": "//img10.360buyimg.com/n1/jfs/t1/161425/26/11624/223402/6048750bE7b82f057/24b57f419c846302.jpg"
			},
			{
				"url": "//img10.360buyimg.com/n1/jfs/t1/37130/8/15667/153389/6011201bEb50bd760/f2d1ffe7ebad9436.jpg"
			},
			{
				"url": "//img10.360buyimg.com/n1/jfs/t1/164674/37/4551/170984/6011201bE0e613da2/440557c2c95a91d4.jpg"
			},
			{
				"url": "//img10.360buyimg.com/n1/jfs/t1/154416/30/16507/145178/6011201bEfa34047a/4bf1640b33cf9315.jpg"
			},
			{
				"url": "//img10.360buyimg.com/n1/jfs/t1/153609/29/16131/158270/6011201dEf430dc42/44d9310a57b910db.jpg"
			},
			{
				"url": "//img10.360buyimg.com/n1/jfs/t1/170321/31/4510/73278/6011201dEb0a321ee/4ab66e6bd5d24731.jpg"
			},
			{
				"url": "//img10.360buyimg.com/n1/jfs/t1/160893/36/4563/34735/6011201cEbc4782b3/421e58e83e287d8d.jpg"
			},
			{
				"url": "//img10.360buyimg.com/n1/jfs/t1/155392/21/16258/78051/6011201dEdbefbd4e/eb0bf02a39124ec5.jpg"
			},
			{
				"url": "//img10.360buyimg.com/n1/jfs/t1/85750/15/5753/82208/5def45a5E8bfae67b/5d6983c1a9b2dc81.jpg"
			}
		],
		"item_weight": "",
		"item_size": "",
		"location": "福建泉州市",
		"post_fee": "",
		"express_fee": "",
		"ems_fee": "",
		"shipping_to": "",
		"has_discount": "",
		"video": [],
		"is_virtual": "",
		"sample_id": "",
		"is_promotion": "",
		"props_name": "0:0:尺码:43;0:1:尺码:42;0:2:尺码:39;0:3:尺码:40;0:4:尺码:44.5;0:5:尺码:40.5;0:6:尺码:41;0:7:尺码:42.5;1:0:颜色:-19黑;1:1:颜色:-6安踏白;1:2:颜色:-1黑/大红/安踏白;1:3:颜色:-5黑【推荐】;1:4:颜色:-3黑/安踏白;1:5:颜色:-4二度灰/亚麻灰/安踏白;1:6:颜色:-18黑【皮面】",
		"prop_imgs": {
			"prop_img": [
				{
					"properties": "1:0",
					"url": "//img14.360buyimg.com/n1/jfs/t1/167443/5/11561/189395/60487513E67e1ac89/6f2b0e24a15d2a07.jpg"
				},
				{
					"properties": "1:1",
					"url": "//img10.360buyimg.com/n1/jfs/t1/161425/26/11624/223402/6048750bE7b82f057/24b57f419c846302.jpg"
				},
				{
					"properties": "1:2",
					"url": "//img13.360buyimg.com/n1/jfs/t1/170137/21/11570/221223/6048750eE6f37af3b/485bda2785ac1eeb.jpg"
				},
				{
					"properties": "1:3",
					"url": "//img12.360buyimg.com/n1/jfs/t1/166580/14/11814/282234/60487510E28b9a6a6/7276dcf61be1c212.jpg"
				},
				{
					"properties": "1:4",
					"url": "//img10.360buyimg.com/n1/jfs/t1/166198/6/11018/211129/6048750eEe91f12ae/fc70a6a6c9119278.jpg"
				},
				{
					"properties": "1:5",
					"url": "//img13.360buyimg.com/n1/jfs/t1/159863/10/12700/269705/60487512Ed2c190a7/d3e41f7e3f809632.jpg"
				},
				{
					"properties": "1:6",
					"url": "//img14.360buyimg.com/n1/jfs/t1/170113/38/11563/205655/6048750cEa69d1123/61dfb5929923dd6f.jpg"
				}
			]
		},
		"property_alias": "0:0:43;0:1:42;0:2:39;0:3:40;0:4:44.5;0:5:40.5;0:6:41;0:7:42.5;1:0:-19黑;1:1:-6安踏白;1:2:-1黑/大红/安踏白;1:3:-5黑【推荐】;1:4:-3黑/安踏白;1:5:-4二度灰/亚麻灰/安踏白;1:6:-18黑【皮面】",
		"props": [
			{
				"name": "商品名称",
				"value": "安踏男鞋休闲运动鞋男士2021春季网面透气轻便板鞋慢跑步鞋子户外训练旅游 -6安踏白 42"
			},
			{
				"name": "商品编号",
				"value": "10335871600"
			},
			{
				"name": "店铺",
				"value": "安踏悠购专卖店"
			},
			{
				"name": "商品毛重",
				"value": "400.00g"
			},
			{
				"name": "商品产地",
				"value": "中国大陆"
			},
			{
				"name": "货号",
				"value": "安踏1"
			},
			{
				"name": "鞋面材质",
				"value": "网布"
			},
			{
				"name": "类别",
				"value": "稳定跑鞋,缓冲跑鞋,马拉松跑鞋"
			},
			{
				"name": "运动鞋科技",
				"value": "扭转系统,易弯折功能,强化避震缓冲,透气技术,其它"
			},
			{
				"name": "适用季节",
				"value": "春季"
			},
			{
				"name": "上市时间",
				"value": "2021年春季"
			},
			{
				"name": "选购热点",
				"value": "透气,避震缓冲,经典款"
			},
			{
				"name": "适用人群",
				"value": "男士"
			},
			{
				"name": "鞋底材质",
				"value": "EVA"
			},
			{
				"name": "适合路面",
				"value": "跑道,公路,小道"
			},
			{
				"name": "运动系列",
				"value": "运动生活系列"
			},
			{
				"name": "功能",
				"value": "透气,耐磨,轻便"
			},
			{
				"name": "闭合方式",
				"value": "系带"
			},
			{
				"name": "颜色",
				"value": "白色,红色,灰色,黑色"
			}
		],
		"total_sold": "",
		"skus": {
			"sku": [
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:0;1:0",
					"properties_name": "0:0:尺码:43;1:0:颜色:-19黑",
					"quantity": 99,
					"sku_id": 10335871595,
					"sku_url": "http://item.jd.com/10335871595.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:1;1:0",
					"properties_name": "0:1:尺码:42;1:0:颜色:-19黑",
					"quantity": 99,
					"sku_id": 10335871594,
					"sku_url": "http://item.jd.com/10335871594.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:2;1:0",
					"properties_name": "0:2:尺码:39;1:0:颜色:-19黑",
					"quantity": 99,
					"sku_id": 10335871593,
					"sku_url": "http://item.jd.com/10335871593.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:3;1:0",
					"properties_name": "0:3:尺码:40;1:0:颜色:-19黑",
					"quantity": 99,
					"sku_id": 10335871592,
					"sku_url": "http://item.jd.com/10335871592.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:4;1:0",
					"properties_name": "0:4:尺码:44.5;1:0:颜色:-19黑",
					"quantity": 99,
					"sku_id": 10335871596,
					"sku_url": "http://item.jd.com/10335871596.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:5;1:0",
					"properties_name": "0:5:尺码:40.5;1:0:颜色:-19黑",
					"quantity": 99,
					"sku_id": 11500318990,
					"sku_url": "http://item.jd.com/11500318990.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:6;1:0",
					"properties_name": "0:6:尺码:41;1:0:颜色:-19黑",
					"quantity": 99,
					"sku_id": 10335871591,
					"sku_url": "http://item.jd.com/10335871591.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:7;1:0",
					"properties_name": "0:7:尺码:42.5;1:0:颜色:-19黑",
					"quantity": 99,
					"sku_id": 11488668356,
					"sku_url": "http://item.jd.com/11488668356.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:0;1:1",
					"properties_name": "0:0:尺码:43;1:1:颜色:-6安踏白",
					"quantity": 99,
					"sku_id": 10335876501,
					"sku_url": "http://item.jd.com/10335876501.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:1;1:1",
					"properties_name": "0:1:尺码:42;1:1:颜色:-6安踏白",
					"quantity": 99,
					"sku_id": 10335871600,
					"sku_url": "http://item.jd.com/10335871600.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:2;1:1",
					"properties_name": "0:2:尺码:39;1:1:颜色:-6安踏白",
					"quantity": 99,
					"sku_id": 10335871599,
					"sku_url": "http://item.jd.com/10335871599.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:3;1:1",
					"properties_name": "0:3:尺码:40;1:1:颜色:-6安踏白",
					"quantity": 99,
					"sku_id": 10335871598,
					"sku_url": "http://item.jd.com/10335871598.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:4;1:1",
					"properties_name": "0:4:尺码:44.5;1:1:颜色:-6安踏白",
					"quantity": 99,
					"sku_id": 10335876502,
					"sku_url": "http://item.jd.com/10335876502.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:5;1:1",
					"properties_name": "0:5:尺码:40.5;1:1:颜色:-6安踏白",
					"quantity": 99,
					"sku_id": 11500318989,
					"sku_url": "http://item.jd.com/11500318989.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:6;1:1",
					"properties_name": "0:6:尺码:41;1:1:颜色:-6安踏白",
					"quantity": 99,
					"sku_id": 10335871597,
					"sku_url": "http://item.jd.com/10335871597.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:7;1:1",
					"properties_name": "0:7:尺码:42.5;1:1:颜色:-6安踏白",
					"quantity": 99,
					"sku_id": 11488668357,
					"sku_url": "http://item.jd.com/11488668357.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:0;1:2",
					"properties_name": "0:0:尺码:43;1:2:颜色:-1黑/大红/安踏白",
					"quantity": 99,
					"sku_id": 10335871589,
					"sku_url": "http://item.jd.com/10335871589.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:1;1:2",
					"properties_name": "0:1:尺码:42;1:2:颜色:-1黑/大红/安踏白",
					"quantity": 99,
					"sku_id": 10335871588,
					"sku_url": "http://item.jd.com/10335871588.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:2;1:2",
					"properties_name": "0:2:尺码:39;1:2:颜色:-1黑/大红/安踏白",
					"quantity": 99,
					"sku_id": 10335871587,
					"sku_url": "http://item.jd.com/10335871587.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:3;1:2",
					"properties_name": "0:3:尺码:40;1:2:颜色:-1黑/大红/安踏白",
					"quantity": 99,
					"sku_id": 10335871586,
					"sku_url": "http://item.jd.com/10335871586.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:4;1:2",
					"properties_name": "0:4:尺码:44.5;1:2:颜色:-1黑/大红/安踏白",
					"quantity": 99,
					"sku_id": 10335871590,
					"sku_url": "http://item.jd.com/10335871590.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:5;1:2",
					"properties_name": "0:5:尺码:40.5;1:2:颜色:-1黑/大红/安踏白",
					"quantity": 99,
					"sku_id": 11500318988,
					"sku_url": "http://item.jd.com/11500318988.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:6;1:2",
					"properties_name": "0:6:尺码:41;1:2:颜色:-1黑/大红/安踏白",
					"quantity": 99,
					"sku_id": 10335876503,
					"sku_url": "http://item.jd.com/10335876503.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:7;1:2",
					"properties_name": "0:7:尺码:42.5;1:2:颜色:-1黑/大红/安踏白",
					"quantity": 99,
					"sku_id": 11488668358,
					"sku_url": "http://item.jd.com/11488668358.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:0;1:3",
					"properties_name": "0:0:尺码:43;1:3:颜色:-5黑【推荐】",
					"quantity": 99,
					"sku_id": 43139143226,
					"sku_url": "http://item.jd.com/43139143226.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:1;1:3",
					"properties_name": "0:1:尺码:42;1:3:颜色:-5黑【推荐】",
					"quantity": 99,
					"sku_id": 43139143227,
					"sku_url": "http://item.jd.com/43139143227.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:2;1:3",
					"properties_name": "0:2:尺码:39;1:3:颜色:-5黑【推荐】",
					"quantity": 99,
					"sku_id": 43139143228,
					"sku_url": "http://item.jd.com/43139143228.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:3;1:3",
					"properties_name": "0:3:尺码:40;1:3:颜色:-5黑【推荐】",
					"quantity": 99,
					"sku_id": 43139143229,
					"sku_url": "http://item.jd.com/43139143229.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:4;1:3",
					"properties_name": "0:4:尺码:44.5;1:3:颜色:-5黑【推荐】",
					"quantity": 99,
					"sku_id": 43139143230,
					"sku_url": "http://item.jd.com/43139143230.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:5;1:3",
					"properties_name": "0:5:尺码:40.5;1:3:颜色:-5黑【推荐】",
					"quantity": 99,
					"sku_id": 43139143231,
					"sku_url": "http://item.jd.com/43139143231.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:6;1:3",
					"properties_name": "0:6:尺码:41;1:3:颜色:-5黑【推荐】",
					"quantity": 99,
					"sku_id": 43139143232,
					"sku_url": "http://item.jd.com/43139143232.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:7;1:3",
					"properties_name": "0:7:尺码:42.5;1:3:颜色:-5黑【推荐】",
					"quantity": 99,
					"sku_id": 43139143233,
					"sku_url": "http://item.jd.com/43139143233.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:0;1:4",
					"properties_name": "0:0:尺码:43;1:4:颜色:-3黑/安踏白",
					"quantity": 99,
					"sku_id": 43139143234,
					"sku_url": "http://item.jd.com/43139143234.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:1;1:4",
					"properties_name": "0:1:尺码:42;1:4:颜色:-3黑/安踏白",
					"quantity": 99,
					"sku_id": 43139143235,
					"sku_url": "http://item.jd.com/43139143235.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:2;1:4",
					"properties_name": "0:2:尺码:39;1:4:颜色:-3黑/安踏白",
					"quantity": 99,
					"sku_id": 43139143236,
					"sku_url": "http://item.jd.com/43139143236.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:3;1:4",
					"properties_name": "0:3:尺码:40;1:4:颜色:-3黑/安踏白",
					"quantity": 99,
					"sku_id": 43139143237,
					"sku_url": "http://item.jd.com/43139143237.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:4;1:4",
					"properties_name": "0:4:尺码:44.5;1:4:颜色:-3黑/安踏白",
					"quantity": 99,
					"sku_id": 43139143238,
					"sku_url": "http://item.jd.com/43139143238.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:5;1:4",
					"properties_name": "0:5:尺码:40.5;1:4:颜色:-3黑/安踏白",
					"quantity": 99,
					"sku_id": 43139143239,
					"sku_url": "http://item.jd.com/43139143239.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:6;1:4",
					"properties_name": "0:6:尺码:41;1:4:颜色:-3黑/安踏白",
					"quantity": 99,
					"sku_id": 43139143240,
					"sku_url": "http://item.jd.com/43139143240.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:7;1:4",
					"properties_name": "0:7:尺码:42.5;1:4:颜色:-3黑/安踏白",
					"quantity": 99,
					"sku_id": 43139143241,
					"sku_url": "http://item.jd.com/43139143241.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:0;1:5",
					"properties_name": "0:0:尺码:43;1:5:颜色:-4二度灰/亚麻灰/安踏白",
					"quantity": 99,
					"sku_id": 43139143242,
					"sku_url": "http://item.jd.com/43139143242.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:1;1:5",
					"properties_name": "0:1:尺码:42;1:5:颜色:-4二度灰/亚麻灰/安踏白",
					"quantity": 99,
					"sku_id": 43139143243,
					"sku_url": "http://item.jd.com/43139143243.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:2;1:5",
					"properties_name": "0:2:尺码:39;1:5:颜色:-4二度灰/亚麻灰/安踏白",
					"quantity": 99,
					"sku_id": 43139143244,
					"sku_url": "http://item.jd.com/43139143244.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:3;1:5",
					"properties_name": "0:3:尺码:40;1:5:颜色:-4二度灰/亚麻灰/安踏白",
					"quantity": 99,
					"sku_id": 43139143245,
					"sku_url": "http://item.jd.com/43139143245.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:4;1:5",
					"properties_name": "0:4:尺码:44.5;1:5:颜色:-4二度灰/亚麻灰/安踏白",
					"quantity": 99,
					"sku_id": 43139143246,
					"sku_url": "http://item.jd.com/43139143246.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:5;1:5",
					"properties_name": "0:5:尺码:40.5;1:5:颜色:-4二度灰/亚麻灰/安踏白",
					"quantity": 99,
					"sku_id": 43139143247,
					"sku_url": "http://item.jd.com/43139143247.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:6;1:5",
					"properties_name": "0:6:尺码:41;1:5:颜色:-4二度灰/亚麻灰/安踏白",
					"quantity": 99,
					"sku_id": 43139143248,
					"sku_url": "http://item.jd.com/43139143248.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:7;1:5",
					"properties_name": "0:7:尺码:42.5;1:5:颜色:-4二度灰/亚麻灰/安踏白",
					"quantity": 99,
					"sku_id": 43139143249,
					"sku_url": "http://item.jd.com/43139143249.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:0;1:6",
					"properties_name": "0:0:尺码:43;1:6:颜色:-18黑【皮面】",
					"quantity": 99,
					"sku_id": 17160622595,
					"sku_url": "http://item.jd.com/17160622595.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:1;1:6",
					"properties_name": "0:1:尺码:42;1:6:颜色:-18黑【皮面】",
					"quantity": 99,
					"sku_id": 17160622594,
					"sku_url": "http://item.jd.com/17160622594.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:2;1:6",
					"properties_name": "0:2:尺码:39;1:6:颜色:-18黑【皮面】",
					"quantity": 99,
					"sku_id": 17160622593,
					"sku_url": "http://item.jd.com/17160622593.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:3;1:6",
					"properties_name": "0:3:尺码:40;1:6:颜色:-18黑【皮面】",
					"quantity": 99,
					"sku_id": 17160622600,
					"sku_url": "http://item.jd.com/17160622600.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:4;1:6",
					"properties_name": "0:4:尺码:44.5;1:6:颜色:-18黑【皮面】",
					"quantity": 99,
					"sku_id": 17160622598,
					"sku_url": "http://item.jd.com/17160622598.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:5;1:6",
					"properties_name": "0:5:尺码:40.5;1:6:颜色:-18黑【皮面】",
					"quantity": 99,
					"sku_id": 17160622599,
					"sku_url": "http://item.jd.com/17160622599.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:6;1:6",
					"properties_name": "0:6:尺码:41;1:6:颜色:-18黑【皮面】",
					"quantity": 99,
					"sku_id": 17160622597,
					"sku_url": "http://item.jd.com/17160622597.html"
				},
				{
					"price": "159.00",
					"orginal_price": "249.00",
					"properties": "0:7;1:6",
					"properties_name": "0:7:尺码:42.5;1:6:颜色:-18黑【皮面】",
					"quantity": 99,
					"sku_id": 17160622596,
					"sku_url": "http://item.jd.com/17160622596.html"
				}
			]
		},
		"seller_id": "",
		"sales": "30万+",
		"shop_id": 221247,
		"props_list": {
			"0:0": "尺码:43",
			"0:1": "尺码:42",
			"0:2": "尺码:39",
			"0:3": "尺码:40",
			"0:4": "尺码:44.5",
			"0:5": "尺码:40.5",
			"0:6": "尺码:41",
			"0:7": "尺码:42.5",
			"1:0": "颜色:-19黑",
			"1:1": "颜色:-6安踏白",
			"1:2": "颜色:-1黑/大红/安踏白",
			"1:3": "颜色:-5黑【推荐】",
			"1:4": "颜色:-3黑/安踏白",
			"1:5": "颜色:-4二度灰/亚麻灰/安踏白",
			"1:6": "颜色:-18黑【皮面】"
		},
		"seller_info": {
			"level": null,
			"shop_type": null,
			"user_num_id": 221247,
			"cid": null,
			"delivery_score": null,
			"item_score": null,
			"score_p": null,
			"zhuy": "//mall.jd.com/index-213251.html?from=pc",
			"search_id": "617610",
			"nick": "安踏悠购专卖店",
			"shop_name": "安踏悠购专卖店",
			"title": "安踏悠购专卖店"
		},
		"tmall": "false",
		"error": "",
		"warning": "",
		"url_log": [],
		"props_img": {
			"1:0": "//img14.360buyimg.com/n1/jfs/t1/167443/5/11561/189395/60487513E67e1ac89/6f2b0e24a15d2a07.jpg",
			"1:1": "//img10.360buyimg.com/n1/jfs/t1/161425/26/11624/223402/6048750bE7b82f057/24b57f419c846302.jpg",
			"1:2": "//img13.360buyimg.com/n1/jfs/t1/170137/21/11570/221223/6048750eE6f37af3b/485bda2785ac1eeb.jpg",
			"1:3": "//img12.360buyimg.com/n1/jfs/t1/166580/14/11814/282234/60487510E28b9a6a6/7276dcf61be1c212.jpg",
			"1:4": "//img10.360buyimg.com/n1/jfs/t1/166198/6/11018/211129/6048750eEe91f12ae/fc70a6a6c9119278.jpg",
			"1:5": "//img13.360buyimg.com/n1/jfs/t1/159863/10/12700/269705/60487512Ed2c190a7/d3e41f7e3f809632.jpg",
			"1:6": "//img14.360buyimg.com/n1/jfs/t1/170113/38/11563/205655/6048750cEa69d1123/61dfb5929923dd6f.jpg"
		},
		"shop_item": [],
		"relate_items": []
	},
	"secache": "29126e588509ddfeccfca167642f7bc2",
	"secache_time": 1615362960,
	"secache_date": "2021-03-10 15:56:00",
	"translate_status": "",
	"translate_time": 0,
	"language": {
		"default_lang": "cn",
		"current_lang": "cn"
	},
	"error": "",
	"reason": "",
	"error_code": "0000",
	"cache": 0,
	"api_info": "today:12 max:10000",
	"execution_time": 3.016,
	"server_time": "Beijing/2021-03-10 15:56:00",
	"client_ip": "106.6.35.144",
	"call_args": [
		"10335871600"
	],
	"api_type": "jd",
	"translate_language": "zh-CN",
	"translate_engine": "google_cn",
	"server_memory": "3.26MB",
	"request_id": "gw-3.60487b8d702d6"
}
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 淘宝优惠券批量优惠券获取工具 支持批量处理多个商品URL获取优惠券信息 支持结果导出为JSON、CSV、TXT格式文件 支持收集多个优惠券链接 """ from selenium import webdriver from selenium.common.exceptions import TimeoutException from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.chrome.options import Options import time import re import sys import os import json import csv from datetime import datetime class CouponFinder: def __init__(self): self.driver = None self.wait = None def setup_driver(self): """初始化Chrome浏览器驱动""" try: options = Options() options.add_argument('--no-sandbox') options.add_argument('--disable-dev-shm-usage') options.add_argument('--disable-gpu') options.add_argument('--window-size=1920,1080') # 关闭自动测试状态显示 options.add_experimental_option("excludeSwitches", ['enable-automation']) options.add_experimental_option('useAutomationExtension', False) # 设置用户代理 options.add_argument('--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36') self.driver = webdriver.Chrome(options=options) # 反爬虫检测 self.driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", { "source": """ Object.defineProperty(navigator, 'webdriver', { get: () => undefined }) """ }) self.driver.maximize_window() self.wait = WebDriverWait(self.driver, 20) print("✅ 浏览器驱动初始化成功") return True except Exception as e: print(f"❌ 浏览器驱动初始化失败: {e}") return False def get_product_info(self, product_url): """获取商品基本信息""" try: print(f"🌐 正在访问商品页面: {product_url}") self.driver.get(product_url) time.sleep(3) # 等待页面加载 input("⏳ 请确认商品页面完全加载后按回车继续...") product_info = { 'title': '', 'price': '', 'url': product_url } try: # 获取商品标题 title_selectors = [ 'h1[data-spm="1000983"]', '.tb-detail-hd h1', '[class*="ItemTitle"]', 'h1[class*="title"]', '.tb-main-title' ] for selector in title_selectors: try: title_elem = self.driver.find_element(By.CSS_SELECTOR, selector) if title_elem and title_elem.text.strip(): product_info['title'] = title_elem.text.strip() break except: continue # 获取商品价格 price_selectors = [ '.tb-rmb-num', '.notranslate', '[class*="price"] [class*="num"]', '.tm-price-cur', '.tb-price-cur' ] for selector in price_selectors: try: price_elem = self.driver.find_element(By.CSS_SELECTOR, selector) if price_elem and price_elem.text.strip(): price_text = price_elem.text.strip() # 提取数字 price_match = re.search(r'[\d.]+', price_text) if price_match: product_info['price'] = f"¥{price_match.group()}" break except: continue if not product_info['title']: product_info['title'] = "商品标题获取失败" if not product_info['price']: product_info['price'] = "价格获取失败" return product_info except Exception as e: print(f"⚠️ 获取商品信息失败: {e}") return product_info except Exception as e: print(f"❌ 访问商品页面失败: {e}") return None def extract_coupon_info(self): """从当前页面提取优惠券信息""" try: coupon_info = { 'amount': '无优惠券', 'urls': [], # 改为列表存储多个链接 'primary_url': '', # 主链接 'description': '', 'type': '未知' } # 查找优惠券信息的各种选择器 coupon_selectors = [ "//*[contains(@class, 'coupon') and (contains(text(), '减') or contains(text(), '满减') or contains(text(), '折扣'))]", "//*[contains(@class, 'Coupon') and (contains(text(), '减') or contains(text(), '满减') or contains(text(), '折扣'))]", "//*[contains(text(), '优惠券') and (contains(text(), '减') or contains(text(), '满减') or contains(text(), '折扣'))]", "//*[contains(@class, 'promo') and (contains(text(), '减') or contains(text(), '满减') or contains(text(), '折扣'))]", "//div[contains(@class, 'coupon')]", "//span[contains(@class, 'coupon')]" ] # 查找优惠券金额 for xpath in coupon_selectors: try: coupon_elements = self.driver.find_elements(By.XPATH, xpath) for elem in coupon_elements: text = elem.text.strip() if text and ('减' in text or '满减' in text or '折扣' in text): # 提取优惠券金额 if '满减' in text: match = re.search(r'满(\d+)减(\d+)', text) if match: coupon_info['amount'] = f"满{match.group(1)}减{match.group(2)}元" coupon_info['type'] = '满减' coupon_info['description'] = text break elif '折扣' in text: match = re.search(r'(\d+\.?\d*)折', text) if match: coupon_info['amount'] = f"{match.group(1)}折" coupon_info['type'] = '折扣券' coupon_info['description'] = text break elif '减' in text: match = re.search(r'减(\d+)', text) if match: coupon_info['amount'] = f"减{match.group(1)}元" coupon_info['type'] = '减价券' coupon_info['description'] = text break if coupon_info['amount'] != '无优惠券': break except: continue # 查找优惠券链接 - 修改为收集多个链接 coupon_link_selectors = [ "//a[contains(@href, 'coupon')]", "//a[contains(@href, 'activity')]", "//a[contains(text(), '领券')]", "//button[contains(text(), '领券')]", "//a[contains(@class, 'coupon')]" ] # 初始化一个列表来存储所有找到的优惠券链接 all_coupon_links = [] for xpath in coupon_link_selectors: try: link_elements = self.driver.find_elements(By.XPATH, xpath) # 记录当前选择器找到的链接数量 links_found_with_this_selector = 0 for elem in link_elements: if elem.is_displayed() and elem.is_enabled(): # 尝试获取href属性 href = elem.get_attribute('href') if href and ('coupon' in href.lower() or 'activity' in href.lower()): if not href.startswith('http'): href = 'https:' + href # 添加到链接列表而不是直接赋值 all_coupon_links.append({ 'url': href, 'type': 'direct_link', 'selector': xpath, 'element_text': elem.text.strip() if elem.text else '' }) links_found_with_this_selector += 1 # 如果没有href,可能是按钮,点击获取链接 elif elem.text and '领券' in elem.text: try: # 尝试点击获取弹窗链接 original_url = self.driver.current_url elem.click() time.sleep(2) # 检查是否有新窗口或弹窗 if len(self.driver.window_handles) > 1: self.driver.switch_to.window(self.driver.window_handles[-1]) clicked_url = self.driver.current_url self.driver.close() self.driver.switch_to.window(self.driver.window_handles[0]) # 添加到链接列表 all_coupon_links.append({ 'url': clicked_url, 'type': 'clicked_link', 'selector': xpath, 'element_text': elem.text.strip() if elem.text else '' }) links_found_with_this_selector += 1 else: # 检查URL是否变化 if self.driver.current_url != original_url: clicked_url = self.driver.current_url # 添加到链接列表 all_coupon_links.append({ 'url': clicked_url, 'type': 'clicked_link', 'selector': xpath, 'element_text': elem.text.strip() if elem.text else '' }) links_found_with_this_selector += 1 self.driver.back() except Exception as e: print(f"点击元素时出错: {e}") # 如果点击失败,记录一个占位符 all_coupon_links.append({ 'url': "点击获取链接失败", 'type': 'failed_click', 'selector': xpath, 'element_text': elem.text.strip() if elem.text else '' }) # 如果当前选择器找到了链接,可以选择继续使用同一选择器寻找更多链接 # 或者跳出循环使用下一个选择器 if links_found_with_this_selector > 0: # 可以选择继续尝试其他选择器,或者停止 # 这里我们选择继续尝试其他选择器以获取更多可能的链接 continue except Exception as e: print(f"使用选择器 {xpath} 时出错: {e}") continue # 处理收集到的所有链接 if all_coupon_links: # 去重处理 seen_urls = set() unique_links = [] for link in all_coupon_links: if link['url'] not in seen_urls and link['url'] not in ["点击获取链接失败", "请在商品页面手动查找优惠券"]: seen_urls.add(link['url']) unique_links.append(link) # 将去重后的链接存入coupon_info coupon_info['urls'] = unique_links coupon_info['primary_url'] = unique_links[0]['url'] if unique_links else "无优惠券链接" else: coupon_info['urls'] = [] coupon_info['primary_url'] = "无优惠券链接" # 如果找到优惠券描述但没有链接 if coupon_info['amount'] != '无优惠券' and not coupon_info['primary_url']: coupon_info['primary_url'] = "请在商品页面手动领取优惠券" return coupon_info except Exception as e: print(f"❌ 提取优惠券信息失败: {e}") return {'amount': '获取失败', 'urls': [], 'primary_url': '', 'description': ''} def display_result(self, product_info, coupon_info, index=None, total=None): """显示优惠券结果""" if not product_info: print(f"\n❌ 未获取到商品信息") return # 批量处理时的标题 if index and total: print(f"\n📊 处理进度: {index}/{total}") print("\n" + "="*80) if index: print(f"🎉 第{index}个商品优惠券获取结果") else: print("🎉 优惠券获取结果") print("="*80) print(f"\n📦 商品信息") print(f" 标题: {product_info['title']}") print(f" 价格: {product_info['price']}") print(f" 商品链接: {product_info['url']}") print("-" * 50) print(f"\n🎫 优惠券信息") print(f" 优惠券: {coupon_info['amount']}") if coupon_info['description']: print(f" 描述: {coupon_info['description']}") # 显示所有找到的优惠券链接 if coupon_info['urls']: print(f" 找到 {len(coupon_info['urls'])} 个优惠券链接:") for i, link_info in enumerate(coupon_info['urls'], 1): print(f" {i}. {link_info['url']} (来源: {link_info['selector']})") else: print(f" 优惠券链接: {coupon_info['primary_url']}") print("="*80) def process_single_url(self, product_url): """处理单个商品URL""" try: # 获取商品信息 product_info = self.get_product_info(product_url) if not product_info: return None, None # 提取优惠券信息 coupon_info = self.extract_coupon_info() return product_info, coupon_info except Exception as e: print(f"❌ 处理商品失败: {e}") return None, None def run_batch(self, url_list): """批量处理URL列表""" try: print(f"🚀 开始批量获取优惠券,共{len(url_list)}个商品...") # 初始化 if not self.setup_driver(): return False results = [] for index, url in enumerate(url_list, 1): print(f"\n{'='*80}") print(f"🔄 开始处理第{index}个商品...") product_info, coupon_info = self.process_single_url(url) if product_info and coupon_info: self.display_result(product_info, coupon_info, index, len(url_list)) results.append({ 'product': product_info, 'coupon': coupon_info, 'success': True }) else: print(f"❌ 第{index}个商品处理失败") results.append({ 'url': url, 'success': False }) # 处理间隔,避免被限制 if index < len(url_list): print(f"⏳ 等待3秒后继续处理下一个商品...") time.sleep(3) # 显示批量处理总结 self.display_batch_summary(results) # 询问是否保存结果 if any(r.get('success') for r in results): self.ask_save_results(results) return True except Exception as e: print(f"❌ 批量处理失败: {e}") return False finally: if self.driver: self.driver.quit() print("🔚 浏览器已关闭") def display_batch_summary(self, results): """显示批量处理总结""" successful = sum(1 for r in results if r.get('success')) total = len(results) print("\n" + "="*80) print("📊 批量处理完成总结") print("="*80) print(f"✅ 成功处理: {successful}/{total} 个商品") if successful > 0: print("\n🎫 优惠券汇总:") for i, result in enumerate(results, 1): if result.get('success'): product = result['product'] coupon = result['coupon'] print(f" {i}. {product['title'][:50]}...") print(f" 价格: {product['price']} | 优惠券: {coupon['amount']}") print("="*80) def save_results(self, results, format_type='json', filename=None): """保存优惠券结果到文件""" try: if not filename: timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") filename = f"coupon_results_{timestamp}.{format_type}" # 只保存成功的结果 successful_results = [r for r in results if r.get('success')] if not successful_results: print("⚠️ 没有成功获取的优惠券信息可保存") return None if format_type.lower() == 'json': return self._save_json(successful_results, filename) elif format_type.lower() == 'csv': return self._save_csv(successful_results, filename) elif format_type.lower() == 'txt': return self._save_txt(successful_results, filename) else: print(f"❌ 不支持的格式: {format_type}") return None except Exception as e: print(f"❌ 保存文件失败: {e}") return None def _save_json(self, results, filename): """保存为JSON格式""" try: # 格式化数据 json_data = { 'export_time': datetime.now().strftime("%Y-%m-%d %H:%M:%S"), 'total_items': len(results), 'results': [] } for result in results: json_data['results'].append({ 'product': result['product'], 'coupon': result['coupon'] }) with open(filename, 'w', encoding='utf-8') as f: json.dump(json_data, f, ensure_ascii=False, indent=2) print(f"✅ JSON文件已保存: {filename}") return filename except Exception as e: print(f"❌ 保存JSON文件失败: {e}") return None def _save_csv(self, results, filename): """保存为CSV格式""" try: with open(filename, 'w', encoding='utf-8-sig', newline='') as f: writer = csv.writer(f) # 写入表头 writer.writerow(['序号', '商品标题', '商品价格', '优惠券金额', '优惠券描述', '优惠券链接', '所有优惠券链接', '商品链接']) # 写入数据 for i, result in enumerate(results, 1): product = result['product'] coupon = result['coupon'] # 将所有优惠券链接合并为字符串 all_links = "; ".join([link_info['url'] for link_info in coupon['urls']]) if coupon['urls'] else coupon['primary_url'] writer.writerow([ i, product['title'], product['price'], coupon['amount'], coupon['description'], coupon['primary_url'], all_links, product['url'] ]) print(f"✅ CSV文件已保存: {filename}") return filename except Exception as e: print(f"❌ 保存CSV文件失败: {e}") return None def _save_txt(self, results, filename): """保存为TXT格式""" try: with open(filename, 'w', encoding='utf-8') as f: f.write("淘宝优惠券获取结果\n") f.write("=" * 80 + "\n") f.write(f"导出时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n") f.write(f"成功获取: {len(results)} 个商品的优惠券信息\n") f.write("=" * 80 + "\n\n") for i, result in enumerate(results, 1): product = result['product'] coupon = result['coupon'] f.write(f"【第{i}个商品】\n") f.write(f"商品标题: {product['title']}\n") f.write(f"商品价格: {product['price']}\n") f.write(f"优惠券: {coupon['amount']}\n") if coupon['description']: f.write(f"描述: {coupon['description']}\n") # 写入所有优惠券链接 if coupon['urls']: f.write(f"找到 {len(coupon['urls'])} 个优惠券链接:\n") for j, link_info in enumerate(coupon['urls'], 1): f.write(f" {j}. {link_info['url']} (来源: {link_info['selector']})\n") else: f.write(f"优惠券链接: {coupon['primary_url']}\n") f.write(f"商品链接: {product['url']}\n") f.write("-" * 80 + "\n\n") print(f"✅ TXT文件已保存: {filename}") return filename except Exception as e: print(f"❌ 保存TXT文件失败: {e}") return None def run(self, product_url): """运行单个商品优惠券获取""" try: print("🚀 开始获取商品优惠券...") # 初始化 if not self.setup_driver(): return False # 处理单个URL product_info, coupon_info = self.process_single_url(product_url) if product_info and coupon_info: self.display_result(product_info, coupon_info) # 询问是否保存结果 results = [{ 'product': product_info, 'coupon': coupon_info, 'success': True }] self.ask_save_results(results) return True return False except Exception as e: print(f"❌ 运行失败: {e}") return False finally: if self.driver: self.driver.quit() print("🔚 浏览器已关闭") def ask_save_results(self, results): """询问用户是否保存结果""" try: successful_results = [r for r in results if r.get('success')] if not successful_results: return print("\n💾 是否保存优惠券结果到文件?") choice = input("请输入 (y/n) [默认y]: ").strip().lower() if choice == '' or choice == 'y': print("\n📁 请选择保存格式:") print("1. JSON格式 (推荐)") print("2. CSV格式 (Excel兼容)") print("3. TXT格式 (纯文本)") format_choice = input("请选择格式 (1/2/3) [默认1]: ").strip() format_map = { '1': 'json', '2': 'csv', '3': 'txt', '': 'json' } format_type = format_map.get(format_choice, 'json') # 询问文件名 custom_filename = input("请输入文件名(留空使用默认名称): ").strip() if custom_filename: # 确保有扩展名 if not custom_filename.endswith(f'.{format_type}'): filename = f"{custom_filename}.{format_type}" else: filename = custom_filename else: filename = None # 保存文件 saved_file = self.save_results(results, format_type, filename) if saved_file: print(f"✅ 文件已保存到当前目录: {saved_file}") print(f"📂 文件路径: {os.path.abspath(saved_file)}") else: print("❌ 保存失败") else: print("⏭️ 跳过保存") except KeyboardInterrupt: print("\n⏹️ 用户取消保存") except Exception as e: print(f"❌ 保存过程出错: {e}") def validate_url(url): """验证URL格式""" if not url: return False # 检查是否为淘宝商品链接 taobao_patterns = [ r'https?://item\.taobao\.com/.*', r'https?://detail\.tmall\.com/.*', r'https?://detail\.tmall\.hk/.*' ] for pattern in taobao_patterns: if re.match(pattern, url): return True return False def read_urls_from_file(filename): """从文件读取URL列表""" try: with open(filename, 'r', encoding='utf-8') as f: urls = [line.strip() for line in f if line.strip() and not line.startswith('#')] return urls except Exception as e: print(f"❌ 读取文件失败: {e}") return [] def get_batch_urls(): """获取批量URL列表""" urls = [] print("\n📋 批量URL输入方式:") print("1. 直接输入多个URL(用空格分隔)") print("2. 从文件读取URL列表") print("3. 交互式逐行输入") choice = input("\n请选择输入方式 (1/2/3): ").strip() if choice == '1': # 直接输入多个URL url_input = input("🔗 请输入多个URL(用空格分隔): ").strip() urls = [url.strip() for url in url_input.split() if url.strip()] elif choice == '2': # 从文件读取 filename = input("📁 请输入文件名(如 urls.txt): ").strip() if os.path.exists(filename): urls = read_urls_from_file(filename) print(f"✅ 从文件读取到 {len(urls)} 个URL") else: print("❌ 文件不存在") return [] elif choice == '3': # 交互式输入 print("🔗 请逐行输入URL(输入空行结束):") while True: url = input("URL: ").strip() if not url: break urls.append(url) else: print("❌ 无效选择") return [] # 验证URL valid_urls = [url for url in urls if validate_url(url)] invalid_urls = [url for url in urls if not validate_url(url)] if invalid_urls: print(f"\n⚠️ 发现 {len(invalid_urls)} 个无效URL,已自动过滤") return valid_urls def main(): """主函数""" print("=" * 80) print("🎫 淘宝优惠券批量获取工具") print("=" * 80) print("📋 功能说明:") print(" • 支持单个商品URL获取优惠券") print(" • 支持批量处理多个商品URL") print(" • 支持从文件读取URL列表") print(" • 支持淘宝、天猫、天猫国际商品") print(" • 支持收集多个优惠券链接") print("=" * 80) try: url_list = [] # 检查命令行参数 if len(sys.argv) > 1: # 检查是否为文件 if os.path.exists(sys.argv[1]): url_list = read_urls_from_file(sys.argv[1]) print(f"📁 从文件读取到 {len(url_list)} 个URL") else: # 检查是否为单个URL url = sys.argv[1] if validate_url(url): url_list = [url] else: print("❌ 无效的URL格式") return else: # 交互式选择 print("\n🎯 请选择操作模式:") print("1. 单个商品处理") print("2. 批量商品处理") mode = input("\n请选择模式 (1/2): ").strip() if mode == '1': # 单个商品 product_url = input("🔗 请输入商品详情页URL: ").strip() if product_url and validate_url(product_url): url_list = [product_url] else: print("❌ 请输入有效的淘宝/天猫商品链接") return elif mode == '2': # 批量商品 url_list = get_batch_urls() if not url_list: print("❌ 未获取到有效URL") return else: print("❌ 无效选择") return if not url_list: print("❌ 未提供有效URL") return # 询问是否需要登录 login_choice = input("\n🔑 是否需要登录淘宝账号?(y/n) [默认n]: ").strip().lower() if login_choice == 'y': username = input("请输入淘宝用户名: ").strip() password = input("请输入淘宝密码: ").strip() else: username = None password = None finder = CouponFinder(username, password) if len(url_list) == 1: # 单个商品 success = finder.run(url_list[0]) else: # 批量商品 success = finder.run_batch(url_list) if success: print("\n✅ 优惠券获取完成!") else: print("\n❌ 优惠券获取失败!") except KeyboardInterrupt: print("\n⏹️ 用户中断程序") except Exception as e: print(f"\n❌ 程序异常: {e}") if __name__ == "__main__": main()
08-27
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 淘宝优惠券批量优惠券获取工具 - GUI版本 支持批量处理多个商品URL获取优惠券信息 支持结果导出为CSV格式文件 支持收集多个优惠券链接 """ import sys import os import re import time import csv from datetime import datetime from selenium import webdriver from selenium.common.exceptions import TimeoutException from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.chrome.options import Options from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QTextEdit, QTabWidget, QFileDialog, QProgressBar, QMessageBox, QListWidget, QGroupBox, QCheckBox) from PyQt5.QtCore import Qt, QThread, pyqtSignal from PyQt5.QtGui import QFont, QIcon class CouponFinder: def __init__(self): self.driver = None self.wait = None def setup_driver(self): """初始化Chrome浏览器驱动""" try: options = Options() options.add_argument('--no-sandbox') options.add_argument('--disable-dev-shm-usage') options.add_argument('--disable-gpu') options.add_argument('--window-size=1920,1080') # 关闭自动测试状态显示 options.add_experimental_option("excludeSwitches", ['enable-automation']) options.add_experimental_option('useAutomationExtension', False) # 设置用户代理 options.add_argument('--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36') self.driver = webdriver.Chrome(options=options) # 反爬虫检测 self.driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", { "source": """ Object.defineProperty(navigator, 'webdriver', { get: () => undefined }) """ }) self.driver.maximize_window() self.wait = WebDriverWait(self.driver, 20) return True except Exception as e: return False def get_product_info(self, product_url, batch_mode=False): """获取商品基本信息""" try: self.driver.get(product_url) time.sleep(3) # 等待页面加载 if batch_mode: time.sleep(10) product_info = { 'title': '', 'price': '', 'url': product_url } try: # 获取商品标题 title_selectors = [ 'h1[data-spm="1000983"]', '.tb-detail-hd h1', '[class*="ItemTitle"]', 'h1[class*="title"]', '.tb-main-title' ] for selector in title_selectors: try: title_elem = self.driver.find_element(By.CSS_SELECTOR, selector) if title_elem and title_elem.text.strip(): product_info['title'] = title_elem.text.strip() break except: continue # 获取商品价格 price_selectors = [ '.tb-rmb-num', '.notranslate', '[class*="price"] [class*="num"]', '.tm-price-cur', '.tb-price-cur' ] for selector in price_selectors: try: price_elem = self.driver.find_element(By.CSS_SELECTOR, selector) if price_elem and price_elem.text.strip(): price_text = price_elem.text.strip() # 提取数字 price_match = re.search(r'[\d.]+', price_text) if price_match: product_info['price'] = f"¥{price_match.group()}" break except: continue if not product_info['title']: product_info['title'] = "商品标题获取失败" if not product_info['price']: product_info['price'] = "价格获取失败" return product_info except Exception as e: return product_info except Exception as e: return None def extract_coupon_info(self): """从当前页面提取优惠券信息""" try: coupon_info = { 'amount': '无优惠券', 'urls': [], # 改为列表存储多个链接 'primary_url': '', # 主链接 'description': '', 'type': '未知' } # 查找优惠券信息的各种选择器 coupon_selectors = [ "//*[contains(@class, 'coupon') and (contains(text(), '减') or contains(text(), '满减') or contains(text(), '折扣'))]", "//*[contains(@class, 'Coupon') and (contains(text(), '减') or contains(text(), '满减') or contains(text(), '折扣'))]", "//*[contains(text(), '优惠券') and (contains(text(), '减') or contains(text(), '满减') or contains(text(), '折扣'))]", "//*[contains(@class, 'promo') and (contains(text(), '减') or contains(text(), '满减') or contains(text(), '折扣'))]", "//div[contains(@class, 'coupon')]", "//span[contains(@class, 'coupon')]" ] # 查找优惠券金额 for xpath in coupon_selectors: try: coupon_elements = self.driver.find_elements(By.XPATH, xpath) for elem in coupon_elements: text = elem.text.strip() if text and ('减' in text or '满减' in text or '折扣' in text): # 提取优惠券金额 if '满减' in text: match = re.search(r'满(\d+)减(\d+)', text) if match: coupon_info['amount'] = f"满{match.group(1)}减{match.group(2)}元" coupon_info['type'] = '满减' coupon_info['description'] = text break elif '折扣' in text: match = re.search(r'(\d+\.?\d*)折', text) if match: coupon_info['amount'] = f"{match.group(1)}折" coupon_info['type'] = '折扣券' coupon_info['description'] = text break elif '减' in text: match = re.search(r'减(\d+)', text) if match: coupon_info['amount'] = f"减{match.group(1)}元" coupon_info['type'] = '减价券' coupon_info['description'] = text break if coupon_info['amount'] != '无优惠券': break except: continue # 查找优惠券链接 - 修改为收集多个链接 coupon_link_selectors = [ "//a[contains(@href, 'coupon')]", "//a[contains(@href, 'activity')]", "//a[contains(text(), '领券')]", "//button[contains(text(), '领券')]", "//a[contains(@class, 'coupon')]" ] # 初始化一个列表来存储所有找到的优惠券链接 all_coupon_links = [] for xpath in coupon_link_selectors: try: link_elements = self.driver.find_elements(By.XPATH, xpath) for elem in link_elements: if elem.is_displayed() and elem.is_enabled(): # 尝试获取href属性 href = elem.get_attribute('href') if href and ('coupon' in href.lower() or 'activity' in href.lower()): if not href.startswith('http'): href = 'https:' + href # 添加到链接列表而不是直接赋值 all_coupon_links.append({ 'url': href, 'type': 'direct_link', 'selector': xpath, 'element_text': elem.text.strip() if elem.text else '' }) # 如果没有href,可能是按钮,点击获取链接 elif elem.text and '领券' in elem.text: try: # 尝试点击获取弹窗链接 original_url = self.driver.current_url elem.click() time.sleep(2) # 检查是否有新窗口或弹窗 if len(self.driver.window_handles) > 1: self.driver.switch_to.window(self.driver.window_handles[-1]) clicked_url = self.driver.current_url self.driver.close() self.driver.switch_to.window(self.driver.window_handles[0]) # 添加到链接列表 all_coupon_links.append({ 'url': clicked_url, 'type': 'clicked_link', 'selector': xpath, 'element_text': elem.text.strip() if elem.text else '' }) else: # 检查URL是否变化 if self.driver.current_url != original_url: clicked_url = self.driver.current_url # 添加到链接列表 all_coupon_links.append({ 'url': clicked_url, 'type': 'clicked_link', 'selector': xpath, 'element_text': elem.text.strip() if elem.text else '' }) self.driver.back() except Exception as e: # 如果点击失败,记录一个占位符 all_coupon_links.append({ 'url': "点击获取链接失败", 'type': 'failed_click', 'selector': xpath, 'element_text': elem.text.strip() if elem.text else '' }) except Exception as e: continue # 处理收集到的所有链接 if all_coupon_links: # 去重处理 - 在循环外部初始化 seen_urls = set() seen_activity_ids = set() unique_links = [] for link in all_coupon_links: url = link['url'] # 跳过无效链接 if url in ["点击获取链接失败", "请在商品页面手动查找优惠券"]: continue # 只处理包含activityId的URL if '&activityId=' not in url: continue # 跳过不包含activityId的URL # 尝试提取activityId activity_id = None if '&activityId=' in url: # 提取&activityId=后面的部分(直到下一个&或字符串结束) start_index = url.find('&activityId=') + len('&activityId=') end_index = url.find('&', start_index) activity_id = url[start_index:] if end_index == -1 else url[start_index:end_index] if activity_id: # 如果找到activityId,使用它进行去重 if activity_id not in seen_activity_ids: seen_activity_ids.add(activity_id) unique_links.append(link) else: # 如果没有activityId,回退到使用完整URL去重 if url not in seen_urls: seen_urls.add(url) unique_links.append(link) # 将去重后的链接存入coupon_info - 在循环外部设置 coupon_info['urls'] = unique_links coupon_info['primary_url'] = unique_links[0]['url'] if unique_links else "无优惠券链接" else: coupon_info['urls'] = [] coupon_info['primary_url'] = "无优惠券链接" # 如果找到优惠券描述但没有链接 if coupon_info['amount'] != '无优惠券' and not coupon_info['primary_url']: coupon_info['primary_url'] = "请在商品页面手动领取优惠券" return coupon_info except Exception as e: return {'amount': '获取失败', 'urls': [], 'primary_url': '', 'description': ''} def process_single_url(self, product_url, batch_mode=False): """处理单个商品URL""" try: # 获取商品信息 product_info = self.get_product_info(product_url, batch_mode) if not product_info: return None, None # 提取优惠券信息 coupon_info = self.extract_coupon_info() return product_info, coupon_info except Exception as e: return None, None def save_results(self, results, filename): """保存优惠券结果到CSV文件""" try: # 只保存成功的结果 successful_results = [r for r in results if r.get('success')] if not successful_results: return False with open(filename, 'w', encoding='utf-8-sig', newline='') as f: writer = csv.writer(f) # 写入表头 - 修改为只包含需要的列 writer.writerow(['序号', '商品链接', '优惠券链接', '所有优惠券链接']) # 写入数据 for i, result in enumerate(successful_results, 1): product = result['product'] coupon = result['coupon'] # 将所有优惠券链接合并为字符串 all_links = "; ".join([link_info['url'] for link_info in coupon['urls']]) if coupon['urls'] else coupon['primary_url'] # 只写入需要的列 writer.writerow([ i, product['url'], # 商品链接 coupon['primary_url'], # 优惠券链接 all_links # 所有优惠券链接 ]) return True except Exception as e: return False class WorkerThread(QThread): """工作线程,用于执行耗时的爬取任务""" progress_signal = pyqtSignal(int, int, str) # 当前进度, 总数, 状态消息 result_signal = pyqtSignal(dict) # 单个结果 finished_signal = pyqtSignal(list, str) # 所有结果, 保存的文件路径 def __init__(self, urls, save_path=None): super().__init__() self.urls = urls self.save_path = save_path self.results = [] def run(self): """执行爬取任务""" try: finder = CouponFinder() if not finder.setup_driver(): self.progress_signal.emit(0, len(self.urls), "浏览器初始化失败") return total = len(self.urls) for index, url in enumerate(self.urls, 1): self.progress_signal.emit(index, total, f"正在处理第 {index} 个商品...") product_info, coupon_info = finder.process_single_url(url, batch_mode=True) if product_info and coupon_info: result = { 'product': product_info, 'coupon': coupon_info, 'success': True } self.results.append(result) self.result_signal.emit(result) else: result = { 'url': url, 'success': False } self.results.append(result) # 处理间隔,避免被限制 if index < total: time.sleep(3) # 保存结果 if self.save_path and any(r.get('success') for r in self.results): timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") filename = os.path.join(self.save_path, f"coupon_results_{timestamp}.csv") if finder.save_results(self.results, filename): self.finished_signal.emit(self.results, filename) else: self.finished_signal.emit(self.results, "") else: self.finished_signal.emit(self.results, "") # 关闭浏览器 if finder.driver: finder.driver.quit() except Exception as e: self.progress_signal.emit(0, len(self.urls), f"处理失败: {str(e)}") class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("淘宝优惠券批量获取工具") self.setGeometry(100, 100, 800, 600) # 中心部件 central_widget = QWidget() self.setCentralWidget(central_widget) # 主布局 layout = QVBoxLayout(central_widget) # 创建选项卡 self.tabs = QTabWidget() layout.addWidget(self.tabs) # 创建单个商品选项卡 self.setup_single_tab() # 创建批量处理选项卡 self.setup_batch_tab() # 创建结果选项卡 self.setup_results_tab() # 状态栏 self.statusBar().showMessage("就绪") # 工作线程 self.worker = None def setup_single_tab(self): """设置单个商品选项卡""" single_tab = QWidget() layout = QVBoxLayout(single_tab) # URL输入组 url_group = QGroupBox("商品URL") url_layout = QVBoxLayout(url_group) self.single_url_input = QLineEdit() self.single_url_input.setPlaceholderText("请输入淘宝/天猫商品URL") url_layout.addWidget(self.single_url_input) layout.addWidget(url_group) # 按钮 btn_layout = QHBoxLayout() self.single_start_btn = QPushButton("开始获取") self.single_start_btn.clicked.connect(self.start_single) btn_layout.addWidget(self.single_start_btn) layout.addLayout(btn_layout) # 结果显示 result_group = QGroupBox("结果") result_layout = QVBoxLayout(result_group) self.single_result_display = QTextEdit() self.single_result_display.setReadOnly(True) result_layout.addWidget(self.single_result_display) layout.addWidget(result_group) self.tabs.addTab(single_tab, "单个商品") def setup_batch_tab(self): """设置批量处理选项卡""" batch_tab = QWidget() layout = QVBoxLayout(batch_tab) # 文件选择组 file_group = QGroupBox("URL列表文件") file_layout = QVBoxLayout(file_group) file_btn_layout = QHBoxLayout() self.file_path_input = QLineEdit() self.file_path_input.setReadOnly(True) self.browse_btn = QPushButton("浏览...") self.browse_btn.clicked.connect(self.browse_file) file_btn_layout.addWidget(self.file_path_input) file_btn_layout.addWidget(self.browse_btn) file_layout.addLayout(file_btn_layout) layout.addWidget(file_group) # URL列表显示 url_list_group = QGroupBox("URL列表") url_list_layout = QVBoxLayout(url_list_group) self.url_list_widget = QListWidget() url_list_layout.addWidget(self.url_list_widget) layout.addWidget(url_list_group) # 保存选项 save_group = QGroupBox("保存选项") save_layout = QVBoxLayout(save_group) save_btn_layout = QHBoxLayout() self.save_path_input = QLineEdit() self.save_path_input.setReadOnly(True) self.save_browse_btn = QPushButton("浏览...") self.save_browse_btn.clicked.connect(self.browse_save_path) save_btn_layout.addWidget(self.save_path_input) save_btn_layout.addWidget(self.save_browse_btn) save_layout.addLayout(save_btn_layout) layout.addWidget(save_group) # 进度条 self.progress_bar = QProgressBar() self.progress_bar.setVisible(False) layout.addWidget(self.progress_bar) # 按钮 btn_layout = QHBoxLayout() self.batch_start_btn = QPushButton("开始批量处理") self.batch_start_btn.clicked.connect(self.start_batch) self.batch_stop_btn = QPushButton("停止") self.batch_stop_btn.clicked.connect(self.stop_processing) self.batch_stop_btn.setEnabled(False) btn_layout.addWidget(self.batch_start_btn) btn_layout.addWidget(self.batch_stop_btn) layout.addLayout(btn_layout) self.tabs.addTab(batch_tab, "批量处理") def setup_results_tab(self): """设置结果选项卡""" results_tab = QWidget() layout = QVBoxLayout(results_tab) # 结果统计 stats_group = QGroupBox("统计信息") stats_layout = QVBoxLayout(stats_group) self.stats_display = QTextEdit() self.stats_display.setReadOnly(True) stats_layout.addWidget(self.stats_display) layout.addWidget(stats_group) # 详细结果 details_group = QGroupBox("详细结果") details_layout = QVBoxLayout(details_group) self.results_display = QTextEdit() self.results_display.setReadOnly(True) details_layout.addWidget(self.results_display) layout.addWidget(details_group) self.tabs.addTab(results_tab, "结果汇总") def browse_file(self): """浏览文件""" file_path, _ = QFileDialog.getOpenFileName( self, "选择URL列表文件", "", "文本文件 (*.txt);;所有文件 (*)" ) if file_path: self.file_path_input.setText(file_path) self.load_urls_from_file(file_path) def load_urls_from_file(self, file_path): """从文件加载URL列表""" try: with open(file_path, 'r', encoding='utf-8') as f: urls = [line.strip() for line in f if line.strip() and not line.startswith('#')] self.url_list_widget.clear() for url in urls: self.url_list_widget.addItem(url) self.statusBar().showMessage(f"已加载 {len(urls)} 个URL") except Exception as e: QMessageBox.critical(self, "错误", f"读取文件失败: {str(e)}") def browse_save_path(self): """浏览保存路径""" save_path = QFileDialog.getExistingDirectory(self, "选择保存目录") if save_path: self.save_path_input.setText(save_path) def start_single(self): """开始处理单个商品""" url = self.single_url_input.text().strip() if not url: QMessageBox.warning(self, "警告", "请输入商品URL") return if not self.validate_url(url): QMessageBox.warning(self, "警告", "请输入有效的淘宝/天猫商品链接") return # 禁用按钮 self.single_start_btn.setEnabled(False) self.statusBar().showMessage("正在处理...") # 在工作线程中处理 self.worker = WorkerThread([url]) self.worker.progress_signal.connect(self.update_progress) self.worker.result_signal.connect(self.display_single_result) self.worker.finished_signal.connect(self.on_single_finished) self.worker.start() def start_batch(self): """开始批量处理""" if self.url_list_widget.count() == 0: QMessageBox.warning(self, "警告", "请先加载URL列表") return save_path = self.save_path_input.text().strip() if not save_path: QMessageBox.warning(self, "警告", "请选择保存目录") return # 收集URL urls = [] for i in range(self.url_list_widget.count()): urls.append(self.url_list_widget.item(i).text()) # 禁用按钮 self.batch_start_btn.setEnabled(False) self.batch_stop_btn.setEnabled(True) self.progress_bar.setVisible(True) self.progress_bar.setMaximum(len(urls)) self.progress_bar.setValue(0) self.statusBar().showMessage("开始批量处理...") # 在工作线程中处理 self.worker = WorkerThread(urls, save_path) self.worker.progress_signal.connect(self.update_progress) self.worker.result_signal.connect(self.add_batch_result) self.worker.finished_signal.connect(self.on_batch_finished) self.worker.start() def stop_processing(self): """停止处理""" if self.worker and self.worker.isRunning(): self.worker.terminate() self.worker.wait() self.statusBar().showMessage("处理已停止") self.reset_ui() def update_progress(self, current, total, message): """更新进度""" self.progress_bar.setMaximum(total) self.progress_bar.setValue(current) self.statusBar().showMessage(message) def display_single_result(self, result): """显示单个结果""" if not result.get('success'): self.single_result_display.append(f"❌ 处理失败: {result.get('url', '未知URL')}") return product = result['product'] coupon = result['coupon'] text = f""" 📦 商品信息 商品链接: {product['url']} 🎫 优惠券信息 优惠券: {coupon['amount']} """ if coupon['description']: text += f" 描述: {coupon['description']}\n" # 显示所有找到的优惠券链接 if coupon['urls']: text += f" 找到 {len(coupon['urls'])} 个优惠券链接:\n" for i, link_info in enumerate(coupon['urls'], 1): text += f" {i}. {link_info['url']}\n" else: text += f" 优惠券链接: {coupon['primary_url']}\n" self.single_result_display.setText(text) def add_batch_result(self, result): """添加批量结果到汇总""" # 切换到结果选项卡 self.tabs.setCurrentIndex(2) if not result.get('success'): self.results_display.append(f"❌ 处理失败: {result.get('url', '未知URL')}") return product = result['product'] coupon = result['coupon'] text = f""" ✅ 成功处理: {product['url']} 优惠券: {coupon['amount']} 优惠券链接: {coupon['primary_url']} """ self.results_display.append(text) def on_single_finished(self, results, save_path): """单个处理完成""" self.single_start_btn.setEnabled(True) self.statusBar().showMessage("处理完成") if save_path: QMessageBox.information(self, "完成", f"结果已保存到: {save_path}") def on_batch_finished(self, results, save_path): """批量处理完成""" self.reset_ui() successful = sum(1 for r in results if r.get('success')) total = len(results) # 更新统计信息 stats_text = f""" 📊 批量处理完成总结 ✅ 成功处理: {successful}/{total} 个商品 """ if successful > 0: stats_text += "\n🎫 优惠券汇总:\n" for i, result in enumerate(results, 1): if result.get('success'): product = result['product'] coupon = result['coupon'] stats_text += f" {i}. {product['url']}\n" stats_text += f" 优惠券: {coupon['amount']}\n" self.stats_display.setText(stats_text) if save_path: self.statusBar().showMessage(f"处理完成,结果已保存到: {save_path}") QMessageBox.information(self, "完成", f"批量处理完成!\n成功处理: {successful}/{total} 个商品\n结果已保存到: {save_path}") else: self.statusBar().showMessage("处理完成,但保存失败") QMessageBox.warning(self, "完成", f"批量处理完成!\n成功处理: {successful}/{total} 个商品\n但保存结果失败") def reset_ui(self): """重置UI状态""" self.batch_start_btn.setEnabled(True) self.batch_stop_btn.setEnabled(False) self.progress_bar.setVisible(False) def validate_url(self, url): """验证URL格式""" if not url: return False # 检查是否为淘宝商品链接 taobao_patterns = [ r'https?://item\.taobao\.com/.*', r'https?://detail\.tmall\.com/.*', r'https?://detail\.tmall\.hk/.*' ] for pattern in taobao_patterns: if re.match(pattern, url): return True return False def main(): app = QApplication(sys.argv) app.setApplicationName("淘宝优惠券批量获取工具") window = MainWindow() window.show() sys.exit(app.exec_()) if __name__ == "__main__": main() 怎么实现保存文件时可以自定义文件名
09-05
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值