1. from FileUtil import *
    2. from OtherUtil import *
    3. from RequestUtil import *
    4. from selenium import webdriver
    5. class JdHelper:
    6. def __init__(self, need_type, filter_list, is_reverse_filter, user_name, user_pwd):
    7. self.not_apply_list = [] # 忽略种草官商品
    8. self.user_name = user_name
    9. self.user_pwd = user_pwd
    10. self.cookie_file = "cookie.txt"
    11. self.not_apply_file = "notApply.txt"
    12. self.wait_apply_list = [] # 待申请列表
    13. self.session = requests.session()
    14. self.headers = {
    15. "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36",
    16. "Referer": "https://try.jd.com/activity/getActivityList?page=1&cids=737",
    17. }
    18. self.count = 0
    19. self.base_url_list = {
    20. "家用电器": "https://try.jd.com/activity/getActivityList?activityState=0&cids=737",
    21. "手机数码": "https://try.jd.com/activity/getActivityList?activityState=0&cids=652,9987",
    22. "电脑办公": "https://try.jd.com/activity/getActivityList?activityState=0&cids=670",
    23. "家居家装": "https://try.jd.com/activity/getActivityList?activityState=0&cids=1620,6728,9847,9855,6196,15248,14065",
    24. "美妆护肤": "https://try.jd.com/activity/getActivityList?activityState=0&cids=1316",
    25. "服饰鞋包": "https://try.jd.com/activity/getActivityList?activityState=0&cids=1315,1672,1318,11729",
    26. "母婴玩具": "https://try.jd.com/activity/getActivityList?activityState=0&cids=1319,6233",
    27. "生鲜美食": "https://try.jd.com/activity/getActivityList?activityState=0&cids=12218",
    28. "图书音像": "https://try.jd.com/activity/getActivityList?activityState=0&cids=1713,4051,4052,4053,7191,7192,5272",
    29. "钟表奢品": "https://try.jd.com/activity/getActivityList?activityState=0&cids=5025,6144",
    30. "个人护理": "https://try.jd.com/activity/getActivityList?activityState=0&cids=16750",
    31. "家庭清洁": "https://try.jd.com/activity/getActivityList?activityState=0&cids=15901",
    32. "食品饮料": "https://try.jd.com/activity/getActivityList?activityState=0&cids=1320,12259",
    33. "更多惊喜": "https://try.jd.com/activity/getActivityList?activityState=0&cids=4938,13314,6994,9192,12473,6196,5272,12379,13678,15083,15126,15980"
    34. }
    35. self.not_apply()
    36. if not self.check_login():
    37. LoginHelper().login(self.user_name, self.user_pwd)
    38. self.check_login()
    39. self.sum()
    40. # 是否反转过滤 True:商品名必须包含在filter_list False:商品名不能包含在filter_list
    41. self.is_reverse_filter = is_reverse_filter
    42. self.filter_list = filter_list
    43. self.need_type = need_type
    44. if self.is_reverse_filter:
    45. print("入选词汇:", self.filter_list)
    46. else:
    47. print("过滤词汇:", self.filter_list)
    48. @retry(stop_max_attempt_number=20)
    49. def get_resp(self, url):
    50. return self.session.get(url, headers=self.headers, timeout=3)
    51. # 查询是否已申请
    52. def getApplyStateByActivityIds(self, activityIds):
    53. url = "https://try.jd.com/user/getApplyStateByActivityIds?activityIds=" + activityIds
    54. resp = self.get_resp(url)
    55. for item in resp.json():
    56. obj = self.find_obj(item["activityId"])
    57. if obj:
    58. self.wait_apply_list.remove(obj)
    59. # 过滤list
    60. def find_obj(self, id):
    61. for data in self.wait_apply_list:
    62. if str(id) == str(data["activity_id"]):
    63. return data
    64. # 申请
    65. def apply(self):
    66. can_stop = False
    67. for wait_apply in self.wait_apply_list:
    68. print("\n正在申请,", wait_apply["activity_id"], wait_apply["name"])
    69. # 获取店铺id
    70. activity_id = wait_apply["activity_id"]
    71. item_url = "https://try.jd.com/{}.html".format(activity_id)
    72. shop_id = re.findall('id="_shopId" value="(.*?)"', self.get_resp(item_url).text)[0]
    73. time.sleep(1)
    74. # 关注
    75. follow_url = "https://try.jd.com/migrate/follow?_s=pc&venderId={}".format(shop_id)
    76. resp = self.get_resp(follow_url)
    77. print(resp.json())
    78. time.sleep(2)
    79. # 申请
    80. url = "https://try.jd.com/migrate/apply?activityId={}&source=0".format(activity_id)
    81. resp = self.get_resp(url)
    82. resp_data = resp.json()
    83. print(resp_data)
    84. if resp_data["success"]:
    85. self.count += 1
    86. print("当天已申请次数:", self.count)
    87. elif '种草官' in resp_data["message"]:
    88. with open("notApply.txt", encoding="utf-8", mode="a+") as f:
    89. f.write(activity_id + "\n")
    90. elif '超过' in resp_data["message"] or '已超' in resp_data["message"]:
    91. self.cancel_follow_item()
    92. self.cancel_follow_shop()
    93. can_stop = True
    94. break
    95. time.sleep(3)
    96. return can_stop
    97. # 获取页面大小
    98. def get_page_size(self, url):
    99. resp = self.get_resp(url)
    100. return int(re.findall('"pages":(\d+)', resp.text)[0])
    101. # 检查登录状态
    102. def check_login(self):
    103. self.set_cookie()
    104. url = "https://passport.jd.com/user/petName/getUserInfoForMiniJd.action"
    105. resp = self.get_resp(url)
    106. if 'null' in resp.text:
    107. print("cookie失效, 返回信息:", resp.text)
    108. return False
    109. else:
    110. print("登录成功, 当前用户名:", resp.json()["nickName"])
    111. return True
    112. # 读取并设置cookie
    113. def set_cookie(self):
    114. if os.path.exists(self.cookie_file):
    115. with open(self.cookie_file, encoding="utf-8", mode="r") as f:
    116. self.headers["Cookie"] = f.read().strip()
    117. # 统计今日已申请次数
    118. def sum(self):
    119. page = 1
    120. run = True
    121. today = time.strftime("%Y-%m-%d")
    122. while run:
    123. url = "https://try.jd.com/user/myTrial?page={}".format(page)
    124. resp = self.get_resp(url)
    125. parser = etree.HTML(resp.text)
    126. day_list = parser.xpath('//span[@class="time"]/em/text()')
    127. for day in day_list:
    128. if today in day:
    129. self.count += 1
    130. else:
    131. run = False
    132. page += 1
    133. print("今日已申请次数:", self.count)
    134. # 过滤器
    135. def in_filter_list(self, name):
    136. has_find = True
    137. for filter in self.filter_list:
    138. if filter in name:
    139. has_find = False
    140. if self.is_reverse_filter:
    141. has_find = not has_find
    142. return has_find
    143. # 过滤种草官级别
    144. def not_apply(self):
    145. if os.path.exists(self.not_apply_file):
    146. with open(self.not_apply_file, encoding="utf-8", mode="r") as f:
    147. for line in f.readlines():
    148. self.not_apply_list.append(line.strip())
    149. # 取关商品
    150. def cancel_follow_item(self):
    151. can_run = True
    152. while can_run:
    153. page_url = "https://t.jd.com/home/follow?index=1"
    154. resp = self.get_resp(page_url)
    155. parser = etree.HTML(resp.text)
    156. item_id_list = ""
    157. for item in parser.xpath('//*[@id="main"]/div/div[2]/div[2]/div[1]/div'):
    158. if not item.xpath("./@id"):
    159. print("当前没有需要取关的商品")
    160. can_run = False
    161. break
    162. item_id_list += item.xpath("./@id")[0].replace('goods_', '') + ","
    163. item_id_list = item_id_list[0:-1]
    164. url = "https://api.m.jd.com/api?functionId=batchCancelFavorite&body={%22skus%22:%22" + item_id_list + "%22}&appid=follow_for_concert&soaKey=2e2i4fRvyfAek5MfHNCe;fMeBpvzbQimFgonJp9YpBqyMcz1VvO4sWcyIBRb8VdOTNQDVo7&client=pc&t=1627133437064&jsonp=result&_=1627133437064"
    165. resp = self.get_resp(url)
    166. print(resp.text)
    167. # 取关店铺
    168. def cancel_follow_shop(self):
    169. can_run = True
    170. while can_run:
    171. page_url = "https://t.jd.com/follow/vender/list.do"
    172. resp = self.get_resp(page_url)
    173. re_list = re.findall('id="vender_(.*?)"', resp.text)
    174. if not re_list:
    175. print("当前没有需要取关的店铺")
    176. break
    177. shop_id_list = ""
    178. for item in re_list:
    179. shop_id_list += item + ","
    180. shop_id_list = shop_id_list[0:-1]
    181. url = "https://t.jd.com/follow/vender/batchUnfollow.do"
    182. resp = self.session.post(url, data={"venderIds": shop_id_list}, headers=self.headers)
    183. print(resp.text)
    184. def run(self):
    185. can_stop = False
    186. for need in self.need_type:
    187. base_url = self.base_url_list[need]
    188. page_size = self.get_page_size(base_url)
    189. print("当前类型:{} 总页数:{}".format(need, page_size))
    190. if can_stop:
    191. break
    192. for i in range(1, page_size + 1):
    193. self.wait_apply_list = []
    194. url = base_url + "&page={}".format(i)
    195. print("\n获取第{}页 总页数:{} {}".format(i, page_size, url))
    196. resp = self.get_resp(url)
    197. parser = etree.HTML(resp.text)
    198. activityIds = [] # 查询是否已申请
    199. item_list = parser.xpath('//*[@id="goods-list"]/div[2]/div/ul/li')
    200. for item in item_list:
    201. wait_apply = {}
    202. activity_id = item.xpath("./@activity_id")[0]
    203. activityIds.append(activity_id)
    204. wait_apply["activity_id"] = activity_id
    205. wait_apply["name"] = item.xpath(".//div[@class='p-name']/text()")[0].strip()
    206. if activity_id in self.not_apply_list:
    207. print("该申请需种草官要求,跳过", wait_apply["name"])
    208. continue
    209. if self.in_filter_list(wait_apply["name"]):
    210. print("加入待申请:", wait_apply["name"])
    211. self.wait_apply_list.append(wait_apply)
    212. else:
    213. print("该商品已被过滤:", wait_apply["name"])
    214. continue
    215. self.getApplyStateByActivityIds(",".join(activityIds))
    216. can_stop = self.apply()
    217. if can_stop:
    218. break
    219. # 模拟登录
    220. class LoginHelper:
    221. def login(self, user_name, user_pwd):
    222. chrome_options = webdriver.ChromeOptions()
    223. chrome_options.add_argument('log-level=2')
    224. try:
    225. driver = webdriver.Chrome(options=chrome_options)
    226. except:
    227. driver = webdriver.Chrome(options=chrome_options, executable_path="C:/chromedriver.exe")
    228. driver.get('https://passport.jd.com/new/login.aspx')
    229. driver.find_element_by_css_selector(
    230. '#content > div.login-wrap > div.w > div > div.login-tab.login-tab-r > a').click()
    231. driver.implicitly_wait(2)
    232. input_username = driver.find_element_by_name('loginname')
    233. input_username.send_keys(user_name)
    234. input_password = driver.find_element_by_name('nloginpwd')
    235. input_password.send_keys(user_pwd)
    236. button_logOK = driver.find_elements_by_id('loginsubmit')
    237. button_logOK = button_logOK[0]
    238. button_logOK.click()
    239. while True:
    240. try:
    241. if driver.find_element_by_css_selector('#ttbar-login > div.dt.cw-icon > a'):
    242. break
    243. except:
    244. print_exc()
    245. time.sleep(1)
    246. print('登陆成功!')
    247. cookie_dict_list = driver.get_cookies()
    248. self.save_cookie(cookie_dict_list)
    249. driver.quit()
    250. def save_cookie(self, cookie_dict_list):
    251. cookies_str = ""
    252. for cookie_dict in cookie_dict_list:
    253. name = cookie_dict["name"]
    254. value = cookie_dict["value"]
    255. cookies_str += name + "=" + value + "; "
    256. cookies_str = cookies_str[0:-2]
    257. with open("cookie.txt", encoding="utf-8", mode="w") as f:
    258. f.write(cookies_str)
    259. if __name__ == '__main__':
    260. user_name = "手机账号"
    261. user_pwd = "密码"
    262. need_type = [
    263. "家用电器",
    264. "手机数码",
    265. "电脑办公",
    266. "家居家装",
    267. "美妆护肤",
    268. "服饰鞋包",
    269. "母婴玩具",
    270. "生鲜美食",
    271. "图书音像",
    272. "钟表奢品",
    273. "个人护理",
    274. "家庭清洁",
    275. "食品饮料",
    276. ]
    277. filter_list = [
    278. '电话卡', '流量卡', '电信', '进销存', '贴纸', '书签', '便利贴', '笔', '纸',
    279. '文具', '儿童', '贴画', '除螨', '口罩', '财务', '软件', '马丁靴', '耳罩', '鞋垫', '长虹', '钥匙'
    280. , '系统', '钢化膜', '特雷索', '流量', '卡槽', '手机壳', '手套', '帽子', '指甲', '领带',
    281. '牙刷', '刀', '袜子', '口红', '数据线', '充电器', '茶', '卸妆', '洗脸', '面巾', '毛巾',
    282. '伸缩带', '剂', '灯', '香水', '车贴', '地漏', '雨刷', '胶', '门窗'
    283. ]
    284. jdHelper = JdHelper(need_type, filter_list, False, user_name, user_pwd)
    285. jdHelper.run()