create_ad_plan.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. from selenium.webdriver import ActionChains
  2. from selenium.webdriver.support.wait import WebDriverWait
  3. from wechat_action.create_ad_plan_idea import IdeaAction
  4. from wechat_action.human_ad import HumanAd
  5. from selenium.webdriver.common.keys import Keys
  6. from datetime import datetime
  7. import re
  8. import time
  9. import logging
  10. import random
  11. import requests
  12. class CreateAdPlan():
  13. # TODO:因为人群包现在可能存在唯一性,公众号间的复制,需要怎么处理
  14. def __init__(self, task, login_ad, service_name, wechat_name):
  15. self.task = task
  16. # self.task_check()
  17. self.service_name = service_name
  18. self.wechat_name = wechat_name
  19. self.login_ad = login_ad
  20. self.driver = login_ad.get_driver_loged()
  21. self.ad_idea_action = IdeaAction(self.driver, task)
  22. self.ad_human_action = HumanAd(self.driver)
  23. def set_ad_title(self):
  24. logging.info('开始设置广告标题')
  25. title_ = self.driver.find_element_by_xpath('//*[@class="input-2lFnByGCRh"]')
  26. title_.send_keys(Keys.CONTROL+'a')
  27. title_.send_keys(Keys.BACKSPACE)
  28. title_.click()
  29. time.sleep(0.1)
  30. title_.send_keys(self.task['title'])
  31. title_.send_keys(Keys.RETURN)
  32. time.sleep(random.uniform(0.1, 0.2))
  33. def set_ad_cpm(self):
  34. # TODO:设置完之后,test函数中进行对应修改
  35. logging.info('开始设置cpm')
  36. if self.task['cpm']['cpm_set'] == '2':
  37. # self.driver.save_screenshot('cpm{}.png'.format(time.time()))
  38. self.driver.find_element_by_xpath(
  39. '//*[@id="wxadcontainer"]/div[1]/div/div[2]/main/div/div[2]/div[1]/div/div[2]/div/div[1]/section[2]/form/div/div[1]/div[1]/button[2]/span').click()
  40. time.sleep(0.1)
  41. select_elements = self.driver.find_elements_by_xpath('//*[@class="adui-radio-base adui-radio-small"]')
  42. for _ in select_elements:
  43. if '优先跑量' in _.text and self.task['cpm']['module'] == '2':
  44. _.click()
  45. time.sleep(0.1)
  46. if '下单' in _.text:
  47. _.click()
  48. time.sleep(0.1)
  49. def set_ad_locations(self):
  50. logging.info('设置对应广告位')
  51. gg_button = self.driver.find_element_by_class_name(
  52. 'adui-button-hasLeftIcon')
  53. WebDriverWait(self.driver, 100).until(
  54. lambda driver: gg_button.is_displayed() and gg_button.is_enabled())
  55. gg_button.click()
  56. # 设置广告形式
  57. self.driver.switch_to.window(self.driver.window_handles[-1])
  58. self.driver.execute_script('''
  59. window.scroll(100000,0);
  60. ''')
  61. WebDriverWait(self.driver, 10).until(
  62. lambda driver: self.driver.find_element_by_css_selector('#PRODUCTTYPE_WECHAT'))
  63. # 设置公众号名字
  64. gzh_title=self.driver.find_element_by_xpath('//*[@id="test_plan_name"]/input')
  65. gzh_title.send_keys(Keys.CONTROL+'a')
  66. self.driver.find_element_by_xpath('//*[@id="test_plan_name"]/input').send_keys(
  67. self.task['title'])
  68. # 推广公众号
  69. wechat_gg = self.driver.find_element_by_css_selector('#PRODUCTTYPE_WECHAT')
  70. wechat_gg.click()
  71. localtion = self.task['plan_base'][1]
  72. if localtion == 'gzh_botoom':
  73. # 默认是这个选项,所以不用进行点选
  74. pass
  75. if localtion == 'gzh_movie':
  76. wechat_localtion = self.driver.find_element_by_id('pos_8')
  77. wechat_localtion.click()
  78. if localtion == 'pyq':
  79. wechat_localtion = self.driver.find_element_by_css_selector(
  80. '#wxadcontainer > div:nth-child(1) > div > div.content-2-HFBij0Uh > main > div > div:nth-child(3) > div.adui-grid-row > div:nth-child(1)')
  81. wechat_localtion.click()
  82. if localtion == 'gzh_big_pic':
  83. self.driver.find_element_by_xpath(
  84. '//*[@id="wxadcontainer"]/div[1]/div/div[2]/main/div/div[3]/div[3]/div[1]/div/button').click()
  85. time.sleep(0.5)
  86. self.driver.find_element_by_xpath(
  87. '//*[@id="wxadcontainer"]/div[1]/div/div[2]/main/div/div[3]/div[3]/div[3]/ol/li[2]').click()
  88. time.sleep(random.uniform(0.1, 0.3))
  89. # 花销设置
  90. # cost_input = self.driver.find_element_by_css_selector(
  91. # '#wxadcontainer > div:nth-child(1) > div > div.content-2-HFBij0Uh > main > div > div:nth-child(5) > div:nth-child(2) > div:nth-child(1) > div > input')
  92. # cost_input.send_keys('100')
  93. # 进入下一页
  94. next_button = self.driver.find_element_by_css_selector('#plan_next_step')
  95. next_button.click()
  96. def set_ad_time(self):
  97. # TODO:log 添加user_id方便后期查看
  98. logging.info('设置对应投放时间')
  99. WebDriverWait(self.driver, 10).until(
  100. lambda driver: self.driver.find_elements_by_xpath('//*[@class="adui-input-base"]'))
  101. input_elements = self.driver.find_elements_by_xpath('//*[@class="adui-input-base"]')
  102. if self.task['date_set']['date_module'] == '2':
  103. select_elements = self.driver.find_elements_by_xpath('//*[@class="adui-radio-base adui-radio-small"]')
  104. for _ in select_elements:
  105. if '长期投放' in _.text:
  106. _.click()
  107. input_elements[0].click()
  108. time.sleep(0.1)
  109. input_elements[0].send_keys(Keys.RIGHT)
  110. for i in range(7):
  111. input_elements[0].send_keys(Keys.BACKSPACE)
  112. for _ in self.task['date_set']['date_list'][0][1:]:
  113. input_elements[0].send_keys(_)
  114. input_elements[0].send_keys(Keys.RETURN)
  115. time.sleep(random.uniform(0.1, 0.2))
  116. else:
  117. input_elements[0].click()
  118. input_elements[0].send_keys(Keys.RIGHT)
  119. for i in range(7):
  120. input_elements[0].send_keys(Keys.BACKSPACE)
  121. for _ in self.task['date_set']['date_list'][0][1:]:
  122. input_elements[0].send_keys(_)
  123. time.sleep(random.uniform(0.1, 0.2))
  124. input_elements[1].click()
  125. time.sleep(0.1)
  126. input_elements[1].send_keys(Keys.RIGHT)
  127. for i in range(7):
  128. input_elements[1].send_keys(Keys.BACKSPACE)
  129. time.sleep(random.uniform(0.1, 0.2))
  130. for _ in self.task['date_set']['date_list'][1][1:]:
  131. input_elements[1].send_keys(_)
  132. input_elements[1].send_keys(Keys.RETURN)
  133. time.sleep(0.2)
  134. # self.driver.save_screenshot('time-{}.png'.format(time.time()))
  135. time.sleep(0.1)
  136. def set_ad_human_location(self):
  137. # TODO:对常驻地,出差旅行,进行区分
  138. logging.info('设置对应人群信息-非人群包')
  139. WebDriverWait(self.driver, 10).until(
  140. lambda driver: self.driver.find_element_by_css_selector('#area_input > div > i'))
  141. self.driver.find_element_by_css_selector('#area_input > div > i').click()
  142. if self.task['plan_base'][1] == 'gzh_botoom':
  143. self.driver.find_element_by_class_name('icon-3cAquX1RLZ').click()
  144. if self.task['plan_base'][1] == 'gzh_movie':
  145. citys = self.driver.find_elements_by_class_name('checkbox-1pfRMRqv-R')
  146. for _ in citys:
  147. _.click()
  148. if self.task['plan_base'][1] == 'gzh_big_pic':
  149. self.driver.find_element_by_class_name('icon-3cAquX1RLZ').click()
  150. self.driver.find_element_by_xpath('//*[@id="bid_objective_6"]/span[1]').click()
  151. if self.task['plan_base'][1] == 'pyq':
  152. try:
  153. self.driver.find_element_by_class_name('icon-3cAquX1RLZ').click()
  154. except:
  155. citys = self.driver.find_elements_by_class_name('checkbox-1pfRMRqv-R')
  156. for _ in citys:
  157. ActionChains(self.driver).move_to_element(_).click().perform()
  158. def set_ad_cost(self):
  159. # 投放计划创建
  160. # 广告创建
  161. logging.info('设置对应广告花销')
  162. day_cost = self.driver.find_element_by_css_selector('#budget_input')
  163. WebDriverWait(self.driver, 10).until(lambda x: (day_cost.is_displayed() and day_cost.is_enabled()))
  164. day_cost.send_keys(self.task['cost']['day_cost'])
  165. time.sleep(random.uniform(0.2, 0.3))
  166. bid_cost = self.driver.find_element_by_xpath('//*[@id="price_input"]')
  167. bid_cost.send_keys(Keys.CONTROL + 'a')
  168. bid_cost.send_keys(Keys.BACKSPACE)
  169. time.sleep(random.uniform(0.1, 0.15))
  170. bid_cost.send_keys(self.task['cost']['bid'])
  171. self.driver.find_element_by_css_selector('#target_next_step').click()
  172. def set_ad_human(self):
  173. logging.info('设置人群包')
  174. self.driver.find_element_by_xpath('//*[@id="test-dmp-container"]/div[2]/span/span[1]').click()
  175. time.sleep(0.1)
  176. select_human_element = self.driver.find_element_by_xpath(
  177. '//*[@id="test-dmp-container"]/div[2]/div[2]/div[1]/div/div[1]/button/span')
  178. ActionChains(self.driver).move_to_element(select_human_element).click().perform()
  179. WebDriverWait(self.driver, 100).until(
  180. lambda x: [_ for _ in self.driver.find_elements_by_xpath('//*[@class="adui-input-base"]') if
  181. _.get_attribute("placeholder") == '输入名称搜索人群包'])
  182. logging.info('翻页开始')
  183. page_text_element = self.driver.find_element_by_xpath(
  184. '//*[@class="adui-pagination-text"]')
  185. page_num_info = re.findall('\d+', page_text_element.text)
  186. page_num = int(page_num_info[1].strip())
  187. for z in range(page_num):
  188. human_infos = self.driver.find_elements_by_class_name('adui-table-cellInner')
  189. WebDriverWait(self.driver, 5).until(
  190. lambda driver: human_infos[0].is_displayed() and human_infos[-1].is_enabled())
  191. human_infos = self.driver.find_elements_by_class_name('adui-table-cellInner')
  192. for i in range(int(len(human_infos) / 3)):
  193. _ = human_infos[i*3]
  194. if _.text in self.task['human_info_package']['name']:
  195. _.click()
  196. new_page_num_info = re.findall('\d+', page_text_element.text)
  197. page_num_now = int(new_page_num_info[0].strip())
  198. # 翻到最后一页时停止
  199. if new_page_num_info[0].strip() == new_page_num_info[1].strip():
  200. break
  201. # 翻页
  202. turn_page_buttons = self.driver.find_elements_by_xpath(
  203. '//*[@class="adui-button-base adui-button-normal adui-button-mini adui-button-hasLeftIcon adui-button-hasRightIcon"]')
  204. turn_page_buttons[1].click()
  205. # 确保翻页成功
  206. page_text_element = self.driver.find_element_by_xpath(
  207. '//*[@class="adui-pagination-text"]')
  208. new_page_num_info = re.findall('\d+', page_text_element.text)
  209. page_num_now = int(new_page_num_info[0].strip())
  210. WebDriverWait(self.driver, 10).until(
  211. lambda x: z != page_num_now)
  212. logging.info('翻页结束')
  213. self.driver.find_element_by_xpath(
  214. '//*[@class="adui-button-base adui-button-primary adui-button-small"]').click()
  215. def set_ad_human_old(self):
  216. # TODO:检查人群是否正常
  217. logging.info('设置人群包')
  218. self.driver.find_element_by_xpath('//*[@id="test-dmp-container"]/div[2]/span/span[1]').click()
  219. time.sleep(0.1)
  220. select_human_element = self.driver.find_element_by_xpath(
  221. '//*[@id="test-dmp-container"]/div[2]/div[2]/div[1]/div/div[1]/button/span')
  222. ActionChains(self.driver).move_to_element(select_human_element).click().perform()
  223. time.sleep(0.4)
  224. WebDriverWait(self.driver, 100).until(
  225. lambda x: [_ for _ in self.driver.find_elements_by_xpath('//*[@class="adui-input-base"]') if
  226. _.get_attribute("placeholder") == '输入名称搜索人群包'])
  227. human_input = [_ for _ in self.driver.find_elements_by_xpath('//*[@class="adui-input-base"]') if
  228. _.get_attribute("placeholder") == '输入名称搜索人群包'][0]
  229. for _ in self.task['human_info_package']['name']:
  230. human_input.click()
  231. human_input.send_keys(_)
  232. human_input.send_keys(Keys.RETURN)
  233. WebDriverWait(self.driver, 30).until(
  234. lambda driver: len(self.driver.find_elements_by_xpath('//*[@class="adui-table-cellInner"]')) == 3)
  235. time.sleep(1)
  236. WebDriverWait(self.driver, 30).until(
  237. lambda driver: [x for x in self.driver.find_elements_by_xpath('//*[@class="adui-table-cellInner"]') if
  238. x.is_displayed() and x.is_enabled() and x.text == _])
  239. human_names = self.driver.find_elements_by_xpath('//*[@class="adui-table-cellInner"]')
  240. for human_name in human_names:
  241. if human_name.text == _:
  242. human_name.click()
  243. for _ in range(len(_) + 1):
  244. time.sleep(0.1)
  245. human_input.send_keys(Keys.BACKSPACE)
  246. self.driver.find_element_by_xpath(
  247. '//*[@class="adui-button-base adui-button-primary adui-button-small"]').click()
  248. def set_ad_humam_gender(self):
  249. logging.info('设置性别')
  250. if self.task['human_info_parameter']['gender'] == '0':
  251. self.driver.find_element_by_xpath('//*[@id="gender_"]/span[2]').click()
  252. if self.task['human_info_parameter']['gender'] == '1':
  253. self.driver.find_element_by_xpath('//*[@id="gender_MALE"]/span[2]').click()
  254. if self.task['human_info_parameter']['gender'] == '2':
  255. self.driver.find_element_by_xpath('//*[@id="gender_FEMALE"]/span[2]').click()
  256. def set_ad_age(self):
  257. logging.info('设置年龄')
  258. age_elements = self.driver.find_elements_by_xpath('//*[@class="adui-select-selector"]')
  259. age_elements[0].click()
  260. time.sleep(0.1)
  261. age_list = self.task['human_info_parameter']['age_list']
  262. # TODO:有时间规整一下,前端传过来的数据,dict中的值 数字全为字符串,或者全为数字
  263. # 256,一截,[14(17),24][25,32][33,40][41,48][49,56][57,64]
  264. # 8 为基本下滑单位,+1为实际两数相减需要加一, -0.1因为刚刚好最后一位时就会下滑到下一页,最后一位无法获取
  265. if age_list[0] == 14:
  266. age_elements[1].click()
  267. time.sleep(random.uniform(0.1, 0.2))
  268. self.driver.execute_script('''
  269. document.getElementsByClassName('rc-virtual-list-holder')[0].scrollTop={};
  270. '''.format(((age_list[1] - 17 + 1 - 0.1) / 8) * 256))
  271. self.driver.find_element_by_xpath('//*[@id="end_age_{}"]'.format(int(age_list[1]))).click()
  272. else:
  273. self.driver.execute_script('''
  274. document.getElementsByClassName('rc-virtual-list-holder')[0].scrollTop={};
  275. '''.format(int((age_list[0] - 17 + 1 - 0.1) / 8) * 256))
  276. time.sleep(random.uniform(0.1, 0.2))
  277. self.driver.find_element_by_xpath('//*[@id="begin_age_{}"]'.format(int(age_list[0]))).click()
  278. # 年龄---设置
  279. age_elements[1].click()
  280. time.sleep(random.uniform(0.1, 0.2))
  281. self.driver.execute_script('''
  282. document.getElementsByClassName('rc-virtual-list-holder')[1].scrollTop={};
  283. '''.format(int((age_list[1] - age_list[0] + 1 - 0.1) / 8) * 256))
  284. time.sleep(random.uniform(0.1, 0.2))
  285. self.driver.find_element_by_xpath('//*[@id="end_age_{}"]'.format(int(age_list[1]))).click()
  286. def set_ad_idea(self):
  287. # 创意创建
  288. logging.info('设置创意')
  289. if self.task['plan_base'][1] == 'gzh_botoom':
  290. self.ad_idea_action.idea_gzh_bottom()
  291. if self.task['plan_base'][1] == 'gzh_movie':
  292. self.ad_idea_action.idea_movie_gzh_movie()
  293. if self.task['plan_base'][1] == 'gzh_big_pic':
  294. self.ad_idea_action.idea_big_pic()
  295. if self.task['plan_base'][1] == 'pyq':
  296. self.ad_idea_action.idea_pyq()
  297. next_button = self.driver.find_element_by_class_name('adui-button-primary')
  298. WebDriverWait(self.driver, 1000).until(lambda x: (next_button.is_displayed() and next_button.is_enabled()))
  299. next_button.click()
  300. def push_ad(self):
  301. # 提交
  302. logging.info('提交广告')
  303. time.sleep(random.uniform(3, 4))
  304. agreement_element = self.driver.find_element_by_class_name('label-3_CrbQVNtc')
  305. if agreement_element.get_attribute('aria-checked') != 'true':
  306. self.driver.find_element_by_class_name('icon-3cAquX1RLZ').click()
  307. time.sleep(random.uniform(0.5, 1))
  308. self.driver.find_element_by_class_name('adui-button-primary').click()
  309. WebDriverWait(self.driver, 10).until(
  310. lambda driver: True if len(
  311. re.findall('广告已提交审核', self.driver.page_source)) else False)
  312. def task_check(self):
  313. # TODO:
  314. # 1.检查任务有无输入问题,
  315. # 2.需要重点确认输入金额,时间是否正确.从最后页面内容进行再次确认
  316. # if 'master' not in self.task.keys() or 'localtion' not in self.task.keys():
  317. # exit()
  318. pass
  319. def check_is_sucess(self):
  320. logging.info('检查广告计划创建是否成功')
  321. plan_name = self.task['title']
  322. WebDriverWait(self.login_ad.driver, 100).until(
  323. lambda driver: True if 'token' in self.login_ad.driver.current_url else False)
  324. cookie_dict = self.login_ad.get_cookie(self.login_ad.driver, login_cookie=False)
  325. token_id = re.findall('token=(\d+)', self.login_ad.driver.current_url)[0]
  326. time_big = str(int(time.time() * 1000))
  327. time_small = str(int(time.time()))
  328. token = token_id
  329. plan_check_url = 'https://mp.weixin.qq.com/promotion/as_rock?action=get_campaign_data&args={"op_type":1,"where":{},"page":1,"page_size":20,"pos_type":999,"ad_filter":{"name":"' \
  330. + plan_name + '"},"advanced":true,"create_time_range":{"start_time":1610884851},"time_range":{"start_time":1610748800,"last_time":' + time_small + '}}&token=' + token + '&appid=&spid=&_=' + time_big
  331. rsp = requests.get(plan_check_url, cookies=cookie_dict)
  332. if 'list' in rsp.json():
  333. if len(rsp.json()['list']):
  334. return True
  335. def run(self, sql_session, err_num=0):
  336. try:
  337. # self.ad_human_action.check_human_info()
  338. self.set_ad_locations()
  339. self.set_ad_time()
  340. self.set_ad_title()
  341. self.set_ad_cpm()
  342. self.set_ad_human_location()
  343. self.set_ad_human()
  344. self.set_ad_humam_gender()
  345. self.set_ad_age()
  346. self.set_ad_cost()
  347. self.set_ad_idea()
  348. self.push_ad()
  349. # 切回到前一页
  350. if self.check_is_sucess():
  351. logging.info('创建广告计划成功')
  352. return {'sucess': True, 'result_info': ''}
  353. else:
  354. if err_num > 3:
  355. return {'sucess': False, 'result_info': '过程中全程无错误,失败'}
  356. else:
  357. self.login_ad.refresh_driver()
  358. self.login_ad.select_ad_master(service_name=self, wechat_name=self.wechat_name,
  359. sql_session=sql_session)
  360. return self.run(sql_session, err_num=err_num + 1)
  361. except Exception as e:
  362. self.driver.save_screenshot(
  363. 'user_id:{}_time_{}_plan_name:{}_plan_error.png'.format(self.login_ad.user_id, datetime.now().strftime(
  364. "%Y-%m-%d, %H:%M:%S"), self.task['title']))
  365. time.sleep(5)
  366. # TODO:有空时讲 e 内容设置为原始内容
  367. if err_num > 3:
  368. return {'sucess': False, 'result_info': str(e)}
  369. else:
  370. self.login_ad.refresh_driver()
  371. self.login_ad.select_ad_master(service_name=self.service_name, wechat_name=self.wechat_name,
  372. sql_session=sql_session)
  373. return self.run(sql_session, err_num=err_num + 1)