个体工商户版批量上传 接口破解
本人是个体户,所以可以网页上传商品到草稿状态
一直期待着开放对个体工商户的API,奈何迟迟不来,只能自己开发一版:
工具:python3 + selenium
思路如下
第一步 先把需要上传的图片全部上传到腾讯 并记录其返回到code 与所属spu对应 主要包含三类图片注意区分:
主图(最多九张) 详图(最多九张) 属性图(张数不限制)
第二步 将整理好的json以{data: data}的形式以form表单格式上传到微信端 ---- 草稿状态的SPU完成 稍作修改就可以轻松上架了
from selenium import webdriver
import ssl
import mysql.connector
from requests_toolbelt.multipart.encoder import MultipartEncoder
import random
import time
from urllib import parse
import re
import json
import requests
import urllib.request
price = re.compile('[0-9]+')
ssl._create_default_https_context = ssl._create_unverified_context
chrome_options = webdriver.ChromeOptions()
# chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-dev-shm-usage')
# chrome_options.add_argument('blink-settings=imagesEnabled=false') # 不加载图片, 提升速度
client = webdriver.Chrome(chrome_options=chrome_options)
client.maximize_window()
client.implicitly_wait(3)
# 登陆微信小商店首页
client.get('https://shop.weixin.qq.com/')
# 写入本地数据库
conn = mysql.connector.connect(
host="", # 数据库主机地址
user="", # 数据库用户名
password="", # 数据库密码
database="",
auth_plugin='mysql_native_password',
use_unicode=True)
cursor = conn.cursor()
sql = 'SELECT sd.spu_id, sd.main_img, sd.price, sd.attr_list, sd.attr_imgs, sd.main_imgs, sd.detail_imgs, sd.data_desc from spu_detail sd where not EXISTS(SELECT spu_id from spu_upped su where su.status = 1 and su.spu_id = sd.spu_id)'
cursor.execute(sql)
fetchall = cursor.fetchall()
def get_price(data):
if price.findall(data):
return int(int(price.findall(data)[-1]) * 1.2 + 6)
else:
return False
def download_img(url):
opener = urllib.request.build_opener()
opener.addheaders = [('user-agent',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36')]
urllib.request.install_opener(opener)
urllib.request.urlretrieve(url, 'download.jpg')
return upload_img('download.jpg')
# 上传图片返回数据
def upload_img(tmp_file):
token = get_para(client.current_url, 'token')
client.get_cookies()
ticket_current = client.execute_script("return window.__INITIAL_STATE__.userInfo.mediaTicket")
ticket = f'ticket_id=gh_8b58b7d45703&ticket={ticket_current}'
upload_url = f'https://mp.weixin.qq.com/cgi-bin/filetransfer?action=preview&f=json&{ticket}&token={token}&lang=zh_CN'
cookie = ';'.join(list(map(lambda x: x['name'] + '=' + x['value'], client.get_cookies())))
multipart_encoder = MultipartEncoder(
fields={
'file': ('download.jpg', open(tmp_file, 'rb'), 'image/jpeg'),
}
)
len = multipart_encoder.len
content_type = multipart_encoder.content_type
headers = {
'authority': 'mp.weixin.qq.com',
'method': 'POST',
'scheme': 'https',
'content-length': f'{len}',
'pragma': 'no-cache',
'accept': 'application/json, text/plain, */*',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'zh-CN,zh;q=0.9',
'cache-control': 'no-cache',
'content-type': content_type,
'cookie': cookie,
'origin': 'https://mp.weixin.qq.com',
'referer': f'https://mp.weixin.qq.com/wxatrade/goods/entry?catId=6839&brandId=2100000000&token={token}&lang=zh_CN',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'same-origin',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36',
'x-requested-with': 'XMLHttpRequest'
}
headers['Content-Type'] = content_type
# 请求头必须包含一个特殊的头信息,类似于Content-Type: multipart/form-data; boundary=${bound}
# 注意:这里请求头也可以自己设置Content-Type信息,用于自定义boundary
r = requests.post(upload_url, data=multipart_encoder, headers=headers)
res = json.loads(r.text)
print(r.text)
return res['content']
def get_up_data(headImg, detailImg, desc, products_sku):
false, true, null = 'false', 'true', None
return {
"product": {
"productKey": {},
"info": {
"title": "待完善",
"model": "",
"subTitle": "待完善",
"headImg": headImg,
"detail": {
"detailImg": detailImg
},
"param": [],
"expressInfo": {
"templateId": "2049928"
},
"serviceDesc": {
"desc": desc
},
"qualificationUrl": [],
"brandId": "2100000000",
"category": [
{
"catId": "6831",
"name": "箱包皮具",
"fCatId": "0",
"createTime": "1588338821",
"updateTime": "1594184186",
"catType": 1,
"version": 0,
"bizuin": "0",
"brandCat": "9223372036854775807",
"catInfo": {
"catLevel": 1,
"highestPriceFixing": 250000,
"isRequired": 0,
"isCustomize": 0,
"specType": 0,
"specTypeDesc": "",
"attrTemplateId": "0",
"attrTemplateName": "",
"maxAttrNum": 0,
"editByUser": 0,
"wording": "",
"description": "",
"maxProductNum": 0,
"qualification": "",
"productQualification": "",
"quaNeedAudit": "",
"maxQualificationImg": 0,
"catGroup": 4294967295,
"qualificationId": "0",
"qualificationType": 0,
"productQualificationType": 0,
"productQualificationId": "0",
"isVirtualGoods": false
},
"auditId": "0",
"desc": "",
"priority": 0
}, {
"catId": "6843",
"name": "潮流女包",
"fCatId": "6831",
"createTime": "1588338821",
"updateTime": "1594184156",
"catType": 1,
"version": 0,
"bizuin": "0",
"brandCat": "9223372036854775807",
"catInfo": {
"catLevel": 2,
"highestPriceFixing": 250000,
"isRequired": 0,
"isCustomize": 0,
"specType": 0,
"specTypeDesc": "",
"attrTemplateId": "0",
"attrTemplateName": "",
"maxAttrNum": 0,
"editByUser": 0,
"wording": "",
"description": "",
"maxProductNum": 0,
"qualification": "",
"productQualification": "",
"quaNeedAudit": "",
"maxQualificationImg": 0,
"catGroup": 4294967295,
"qualificationId": "0",
"qualificationType": 0,
"productQualificationType": 0,
"productQualificationId": "0",
"isVirtualGoods": false
},
"auditId": "0",
"desc": "",
"priority": 0
}, {
"catId": "6851",
"name": "特殊商品",
"fCatId": "6843",
"createTime": "1588338821",
"updateTime": "1599221665",
"catType": 1,
"version": 0,
"bizuin": "0",
"brandCat": "9223372036854775807",
"catInfo": {
"maxAttrNum": 28,
"catLevel": 3,
"highestPriceFixing": 250000,
"maxQualificationImg": 0,
"enterpriseAuditInfo": {
"qualificationType": 0,
"productQualificationType": 0,
"qualification": "",
"productQualification": "",
"quaNeedAudit": "",
"maxQualificationImg": 0,
"qualificationId": "0",
"productQualificationId": "0"
},
"qualificationType": 0,
"productQualificationType": 0,
"isRequired": 0,
"isCustomize": 0,
"specType": 0,
"specTypeDesc": "",
"attrTemplateId": "0",
"attrTemplateName": "",
"editByUser": 0,
"wording": "",
"description": "",
"maxProductNum": 0,
"qualification": "",
"productQualification": "",
"quaNeedAudit": "",
"catGroup": 4294967295,
"qualificationId": "0",
"productQualificationId": "0",
"isVirtualGoods": false
},
"auditId": "0",
"desc": "",
"priority": 0
}],
"shopcatId": []
},
"productSkus": products_sku
},
"type": 1,
"listingFlag": false
}
catId = 1603702999999
def get_product_sku(index, color, image_key, spu_price, spu_id):
false, true, null = 'false', 'true', None
global catId
catId = catId - 1
return {
"__skuKey": f"134923.{catId}",
"__skuTitle": color,
"__skuSelected": true,
"__selectedKey": index,
"productSkuInfo": {
"thumbImg": image_key,
"salePrice": spu_price,
"skuCode": spu_id,
"stockInfo": {
"stockNum": 10
},
"saleParam": [{
"categorys": [{
"thumbImg": "",
"desc": "",
"catId": "134923",
"name": "选择颜色",
"fCatId": "6851",
"createTime": "1589356824",
"updateTime": "1589356824",
"catType": 3,
"version": 0,
"bizuin": "0",
"brandCat": "0",
"catInfo": {
"isCustomize": 1,
"highestPriceFixing": 250000,
"isRequired": 0,
"specType": 0,
"specTypeDesc": "",
"attrTemplateId": "0",
"attrTemplateName": "",
"maxAttrNum": 0,
"editByUser": 0,
"catLevel": 0,
"wording": "",
"description": "",
"maxProductNum": 0,
"qualification": "",
"productQualification": "",
"quaNeedAudit": "",
"maxQualificationImg": 0,
"catGroup": 4294967295,
"qualificationId": "0",
"qualificationType": 0,
"productQualificationType": 0,
"productQualificationId": "0",
"isVirtualGoods": false
},
"auditId": "0",
"priority": 0,
"maxLevel": 2
}, {
"fCatId": "134923",
"catId": catId,
"name": color,
"desc": "有什么疑问可以联系客服",
"catInfo": {
"editByUser": 1
},
"priority": index + 1,
"selected": true,
"thumbImg": image_key
}]
}]
}
}
def draft_spu(item):
spu_id = item[0] # 无需处理
attr_imgs = json.loads(item[4])
main_imgs = get_9pic(json.loads(item[5]))
detail_imgs = get_9pic(json.loads(item[6]))
attr_imgs_big = []
for index, attr in enumerate(attr_imgs):
url = attr_imgs[index].replace('310x310', '750x750')
attr_imgs_big.append(url)
main_img_arr = list(map(lambda x: download_img(x), main_imgs))
detail_img_arr = list(map(lambda x: download_img(x), detail_imgs))
product_sku_arr = list(map(lambda x: download_img(x), attr_imgs_big))
cursor.execute(
f'''insert into spu_img_key (spu_id, product_sku_arr, main_img_arr, detail_img_arr) values ('{spu_id}', '{json.dumps(product_sku_arr)}', '{json.dumps(main_img_arr)}', '{json.dumps(detail_img_arr)}')''')
def get_para(url, param):
params = parse.parse_qs(parse.urlparse(url).query)
return params[param][0]
def get_9pic(items):
if len(items) <= 9:
return items
else:
return items[0: 9]
def refresh_client():
client.refresh()
time.sleep(3)
print('暂停点')
for index, item in enumerate(fetchall):
try:
if index != 0 and index % 10 == 0:
refresh_client()
draft_spu(item)
cursor.execute(f'''insert into spu_upped (spu_id) values ('{item[0]}')''')
conn.commit()
except:
pass
client.quit()