create_ad_layout.py 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879
  1. from selenium.webdriver.support.wait import WebDriverWait
  2. from selenium.webdriver.common.keys import Keys
  3. from selenium.webdriver import ActionChains
  4. from logging import handlers
  5. from datetime import datetime
  6. import logging
  7. import requests
  8. import random
  9. import shutil
  10. import time
  11. import re
  12. import os
  13. class CreateAd:
  14. def __init__(self, login_ad, service_name, wechat_name):
  15. self.log_ad = login_ad
  16. self.service_name = service_name
  17. self.wechat_name = wechat_name
  18. self.driver = login_ad.get_driver_loged()
  19. self.get_into_create_page()
  20. self.send_file_limit_num = 8
  21. self.img_dir = './img'
  22. def get_into_create_page(self):
  23. # 进入创建页面
  24. logging.info('开始进入创建页面')
  25. self.driver.find_element_by_id('material').click()
  26. for i in range(10):
  27. try:
  28. WebDriverWait(self.driver, 10).until(lambda driver: self.driver.find_element_by_xpath(
  29. '//*[@class="ui-fl-r adui-button-base adui-button-primary adui-button-small"]'))
  30. create_element = self.driver.find_element_by_xpath(
  31. '//*[@class="ui-fl-r adui-button-base adui-button-primary adui-button-small"]')
  32. WebDriverWait(self.driver, 10).until(
  33. lambda driver: create_element.is_displayed() and create_element.is_enabled())
  34. create_element.click()
  35. break
  36. except Exception as e:
  37. logging.info(str(e))
  38. pass
  39. if i == 10:
  40. raise ValueError('点击新建推广页 出错')
  41. logging.info('点击新建推广页 结束')
  42. WebDriverWait(self.driver, 5).until(lambda driver: driver.find_element_by_css_selector(
  43. '#wxadcontainer > div:nth-child(1) > div:nth-child(2) > div.dialog-1fj_N480ZT > div > div > div:nth-child(1) > div.dialogCardFooter-17KpBD1lgN > button'))
  44. self.driver.find_element_by_css_selector(
  45. '#wxadcontainer > div:nth-child(1) > div:nth-child(2) > div.dialog-1fj_N480ZT > div > div > div:nth-child(1) > div.dialogCardFooter-17KpBD1lgN > button').click()
  46. self.driver.switch_to.window(self.driver.window_handles[-1])
  47. WebDriverWait(self.driver, 100).until(lambda driver: driver.find_element_by_class_name('addContent-8pexaaAGYy'))
  48. self.driver.find_element_by_class_name('addContent-8pexaaAGYy').click()
  49. WebDriverWait(self.driver, 100).until(lambda driver: driver.find_element_by_class_name('topArea-qOwEAeNuIn'))
  50. logging.info('进入到推广页,编辑界面')
  51. def set_advertisement_sign(self, layout_name):
  52. # 设置广告标记
  53. self.driver.find_element_by_xpath('//*[@class="icon-edQB0KK2VG"]').click()
  54. input_element = self.driver.find_element_by_xpath('//input[@class="input-2lFnByGCRh"]')
  55. input_element.send_keys(Keys.BACKSPACE)
  56. input_element.send_keys(layout_name)
  57. def set_background_color(self, color):
  58. color_buttons = self.driver.find_elements_by_class_name('adui-cp-picker')
  59. c_buttons_can_use = []
  60. for _ in color_buttons:
  61. if _.is_displayed() and _.is_enabled():
  62. c_buttons_can_use.append(_)
  63. c_buttons_can_use[0].click()
  64. time.sleep(random.uniform(0.2, 0.3))
  65. input_elements = self.driver.find_elements_by_xpath('//input[@class="adui-input-base"]')
  66. for _ in input_elements:
  67. if _.is_enabled() and _.is_displayed() and _.get_attribute("value") == 'FFFFFF':
  68. _.click()
  69. _.send_keys(color)
  70. def set_head_assemb(self, info):
  71. logging.info('开始设置头版首页')
  72. def single_page_set():
  73. self.driver.find_element_by_xpath('//*[@id="stage-sidebar"]/div[1]/div/div[1]/div/div').click()
  74. time.sleep(random.uniform(0.1, 0.2))
  75. # 设置图片格式
  76. if info['content']['adSite'] != '朋友圈信息流':
  77. self.driver.find_elements_by_xpath('//*[@class="adui-radio-indicator"]')[1].click()
  78. elif info['content']['outerStyle'] != '常规广告':
  79. select_e = self.driver.find_elements_by_xpath('//*[@class="adui-select-selection-item"]')
  80. for _ in select_e:
  81. if _.text == '常规广告':
  82. _.click()
  83. time.sleep(0.1)
  84. select_e = self.driver.find_elements_by_xpath('//*[@class="adui-select-item-option-content"]')
  85. for _ in select_e:
  86. if _.text == '卡片广告':
  87. _.click()
  88. # 上传图片
  89. file_name = re.split('\/', info['content']['url'])[-1]
  90. self.driver.find_element_by_class_name('upload-img-item-inner-2gsg7NjaZ8').click()
  91. WebDriverWait(self.driver, 10).until(
  92. lambda x: len([_ for _ in self.driver.find_elements_by_xpath(
  93. '//*[@class="base-1H7btNX4v9 primary-2diSJcTI_7 small-ZbOBQ_8kVy iconBtn-NYPRTZ5yJB webuploader-container"]')
  94. if _.is_displayed() and _.is_enabled()]) > 0)
  95. turn_page_buttons = self.driver.find_elements_by_class_name('paginationIcon-1EfoH0sNRF')
  96. can_use_button = []
  97. for _ in turn_page_buttons:
  98. if _.is_enabled() and _.is_displayed():
  99. can_use_button.append(_)
  100. # 不断翻页获取到元素为止
  101. chose_over = False
  102. while True:
  103. WebDriverWait(self.driver, 10).until(
  104. lambda driver: self.driver.find_elements_by_class_name('title-29sncpKgTl'))
  105. #TODO:翻页后,有时没有加载出来
  106. page_elements = self.driver.find_elements_by_class_name('title-29sncpKgTl')
  107. for _ in page_elements:
  108. if _.is_displayed() and _.is_enabled():
  109. if file_name in _.text:
  110. _.click()
  111. chose_over = True
  112. break
  113. if chose_over or len(can_use_button) == 0:
  114. break
  115. # 翻到最后一页时停止
  116. page_text = self.driver.find_element_by_class_name('count_small-37CcvfzoTl').text
  117. page_nums = re.findall('\d+', page_text)
  118. if int(page_nums[0].strip()) == int(page_nums[1].strip()):
  119. break
  120. # 翻页
  121. can_use_button[2].click()
  122. # 确保翻页成功
  123. WebDriverWait(self.driver, 10).until(
  124. lambda x: page_text != self.driver.find_element_by_class_name('count_small-37CcvfzoTl').text)
  125. self.driver.find_element_by_xpath('//*[@id="test_material_container_confirm"]').click()
  126. try:
  127. WebDriverWait(self.driver, 4).until(
  128. lambda driver: driver.find_element_by_class_name(
  129. 'btnFist-uueBS6DQFa'))
  130. _ = self.driver.find_element_by_class_name(
  131. 'btnFist-uueBS6DQFa')
  132. WebDriverWait(self.driver, 4).until(
  133. lambda x: (_.is_displayed() and _.is_enabled()))
  134. time.sleep(1)
  135. _.click()
  136. WebDriverWait(self.driver, 100).until(
  137. lambda driver: driver.find_element_by_class_name(
  138. 'btn-3E823IXt3m'))
  139. _ = self.driver.find_element_by_class_name(
  140. 'btn-3E823IXt3m')
  141. WebDriverWait(self.driver, 100).until(
  142. lambda x: (_.is_displayed() and _.is_enabled()))
  143. time.sleep(1)
  144. _.click()
  145. except Exception as e:
  146. pass
  147. logging.info('头版首页单图上传结束')
  148. def multi_page_set():
  149. # TODO:现在图片设置为素材库,需要对应下载然后存储
  150. self.driver.find_element_by_css_selector(
  151. '#stage-sidebar > div.topArea-qOwEAeNuIn > div > div:nth-child(2)').click()
  152. time.sleep(random.uniform(0.1, 0.2))
  153. page_size = len(info_v)
  154. if page_size == 3:
  155. pass
  156. if page_size == 4:
  157. self.driver.find_element_by_css_selector(
  158. '#stage-settings > div:nth-child(1) > div > div > div:nth-child(3) > div.adui-form-item > div.adui-form-control > div > button:nth-child(2)').click()
  159. if page_size == 6:
  160. self.driver.find_element_by_css_selector(
  161. '#stage-settings > div:nth-child(1) > div > div > div:nth-child(3) > div.adui-form-item > div.adui-form-control > div > button:nth-child(3)').click()
  162. time.sleep(random.uniform(0.1, 0.2))
  163. self.driver.find_element_by_class_name('imageUploadItem-tA9JX0RWua').click()
  164. WebDriverWait(self.driver, 1000).until(
  165. lambda driver: self.driver.find_element_by_class_name('adui-tabs-tab')
  166. )
  167. input_elements = self.driver.find_elements_by_tag_name('input')
  168. input_find_element = None
  169. for _ in input_elements:
  170. if '输入关键词搜索素材' in _.get_attribute('placeholder'):
  171. input_find_element = _
  172. for _ in info_v:
  173. file_name = os.path.basename(_)
  174. logging.info(file_name)
  175. input_find_element.send_keys(file_name)
  176. input_find_element.send_keys(Keys.RETURN)
  177. WebDriverWait(self.driver, 1000).until(
  178. lambda driver: driver.find_element_by_class_name('img-2HvhMmpnzP'))
  179. file_element = self.driver.find_element_by_class_name('img-2HvhMmpnzP')
  180. WebDriverWait(self.driver, 1000).until(
  181. lambda x: (file_element.is_displayed() and file_element.is_enabled()))
  182. ActionChains(self.driver).move_to_element(file_element).perform()
  183. time.sleep(random.uniform(0.5, 1))
  184. file_element.click()
  185. for i in range(len(file_name) + 10):
  186. input_find_element.send_keys(Keys.BACKSPACE)
  187. time.sleep(random.uniform(0.5, 1))
  188. self.driver.find_element_by_xpath('/html/body/div[12]/div/div/div[2]/div/div[3]/button[2]').click()
  189. # 切图操作如果有的话进行对应操作.
  190. for i in range(len(info_v) + 1):
  191. try:
  192. WebDriverWait(self.driver, 6).until(
  193. lambda driver: driver.find_element_by_xpath(
  194. '/html/body/div[13]/div/div/div[2]/div/div[3]/button[2]'))
  195. self.driver.find_element_by_xpath('/html/body/div[13]/div/div/div[2]/div/div[3]/button[2]').click()
  196. except:
  197. pass
  198. logging.info('头版多图选择 结束')
  199. def movie_set():
  200. # TODO:视频转为链接,
  201. file_name = os.path.basename(info_v)
  202. self.driver.find_element_by_xpath('//*[@id="stage-sidebar"]/div[1]/div/div[3]/div/div').click()
  203. self.driver.find_element_by_xpath('//*[@class="comptEditButton-2JsnAFdOGZ"]').click()
  204. WebDriverWait(self.driver, 100).until(
  205. lambda x: len([_ for _ in self.driver.find_elements_by_class_name('title-29sncpKgTl') if
  206. _.is_displayed() and _.is_enabled()]) > 0)
  207. #
  208. turn_page_buttons = self.driver.find_elements_by_class_name('paginationIcon-1EfoH0sNRF')
  209. can_use_button = []
  210. for _ in turn_page_buttons:
  211. if _.is_enabled() and _.is_displayed():
  212. can_use_button.append(_)
  213. # 不断翻页获取到元素为止
  214. chose_over = False
  215. while True:
  216. page_elements = self.driver.find_elements_by_class_name('title-29sncpKgTl')
  217. for _ in page_elements:
  218. if _.is_displayed() and _.is_enabled():
  219. if file_name in _.text:
  220. _.click()
  221. chose_over = True
  222. break
  223. if chose_over or len(can_use_button) == 0:
  224. # can_use_button 数量说明是否可以翻页,如果不可以翻页,则直接停止
  225. break
  226. # 翻到最后一页时停止
  227. page_text_elements = self.driver.find_elements_by_class_name('count_small-37CcvfzoTl')
  228. for _ in page_text_elements:
  229. if _.is_enabled() and _.is_displayed():
  230. page_text = _.text
  231. page_text_element = _
  232. page_nums = re.findall('\d+', page_text)
  233. if int(page_nums[0].strip()) == int(page_nums[1].strip()):
  234. break
  235. # 翻页
  236. can_use_button[2].click()
  237. # 确保翻页成功
  238. WebDriverWait(self.driver, 10).until(
  239. lambda x: page_text != page_text_element.text)
  240. sc_buttons = self.driver.find_elements_by_id('test_material_container_confirm')
  241. for _ in sc_buttons:
  242. if _.is_enabled() and _.is_displayed():
  243. _.click()
  244. info_type = info['type']
  245. info_v = None # TODO: 暂时存在,之后修改好multi_page_set 和movie_set.就删除
  246. if info['type'] == 'topImg':
  247. single_page_set()
  248. if info['type'] == 'topSlider':
  249. multi_page_set()
  250. if info['type'] == 'topVideo':
  251. movie_set()
  252. # 设置头部组件
  253. pass
  254. def set_page(self, config_info):
  255. logging.info('开始设置图片模块')
  256. page_link = config_info['url']
  257. # 设置图片模块
  258. self.driver.find_element_by_xpath('//*[@id="stage-sidebar"]/section/div[2]/div[1]/div/div').click()
  259. time.sleep(random.uniform(0.1, 0.2))
  260. file_name = re.split('\/', page_link)[-1]
  261. upload_elements = self.driver.find_elements_by_class_name('upload-img-item-inner-2gsg7NjaZ8')
  262. for _ in upload_elements:
  263. if _.is_displayed() and _.is_enabled():
  264. _.click()
  265. WebDriverWait(self.driver, 10).until(
  266. lambda x: len([_ for _ in self.driver.find_elements_by_xpath(
  267. '//*[@class="base-1H7btNX4v9 primary-2diSJcTI_7 small-ZbOBQ_8kVy iconBtn-NYPRTZ5yJB webuploader-container"]')
  268. if _.is_displayed() and _.is_enabled()]) > 0)
  269. # 不断翻页获取到元素为止
  270. chose_over = False
  271. logging.info('翻页开始')
  272. while True:
  273. WebDriverWait(self.driver, 10).until(
  274. lambda driver: self.driver.find_elements_by_class_name('title-29sncpKgTl'))
  275. # TODO:翻页后,有时没有加载出来
  276. page_elements = self.driver.find_elements_by_class_name('title-29sncpKgTl')
  277. for _ in page_elements:
  278. if _.is_displayed() and _.is_enabled():
  279. if file_name in _.text:
  280. _.click()
  281. chose_over = True
  282. break
  283. turn_page_buttons = self.driver.find_elements_by_class_name('paginationIcon-1EfoH0sNRF')
  284. can_use_button = []
  285. for _ in turn_page_buttons:
  286. if _.is_enabled() and _.is_displayed():
  287. can_use_button.append(_)
  288. if chose_over or len(can_use_button) == 0:
  289. # can_use_button 数量说明是否可以翻页,如果不可以翻页,则直接停止
  290. break
  291. # 翻到最后一页时停止
  292. page_text_elements = self.driver.find_elements_by_class_name('count_small-37CcvfzoTl')
  293. for _ in page_text_elements:
  294. if _.is_enabled() and _.is_displayed():
  295. page_text = _.text
  296. page_text_element = _
  297. page_nums = re.findall('\d+', page_text)
  298. if int(page_nums[0].strip()) == int(page_nums[1].strip()):
  299. break
  300. # 翻页
  301. can_use_button[2].click()
  302. # 确保翻页成功
  303. WebDriverWait(self.driver, 10).until(
  304. lambda x: page_text != page_text_element.text)
  305. logging.info('翻页结束')
  306. sc_buttons = self.driver.find_elements_by_id('test_material_container_confirm')
  307. for _ in sc_buttons:
  308. if _.is_enabled() and _.is_displayed():
  309. _.click()
  310. logging.info('是否截图开始')
  311. try:
  312. WebDriverWait(self.driver, 4).until(
  313. lambda driver: driver.find_element_by_class_name(
  314. 'btnFist-uueBS6DQFa'))
  315. _ = self.driver.find_element_by_class_name(
  316. 'btnFist-uueBS6DQFa')
  317. WebDriverWait(self.driver, 4).until(
  318. lambda x: (_.is_displayed() and _.is_enabled()))
  319. # 已经出现但任需要等待
  320. time.sleep(1)
  321. _.click()
  322. WebDriverWait(self.driver, 100).until(
  323. lambda x: len([_ for _ in self.driver.find_elements_by_class_name('btn-3E823IXt3m') if
  324. _.is_displayed() and _.is_enabled()]) > 0)
  325. for _ in self.driver.find_elements_by_class_name('btn-3E823IXt3m'):
  326. if _.is_displayed() and _.is_enabled():
  327. _.click()
  328. except Exception as e:
  329. logging.info(e)
  330. logging.info('是否截图结束')
  331. # 设置文本边距
  332. if config_info['marginTop'] != 0 or config_info['marginBottom'] != 0:
  333. distance_buttons = self.driver.find_elements_by_xpath(
  334. '//div[@class="adui-numeric-input adui-input-wrapper adui-input-small adui-input-normal"]/input')
  335. distance_buttons_can_use = []
  336. for _ in distance_buttons:
  337. if _.is_enabled() and _.is_displayed() and _.get_attribute("value") == '0':
  338. distance_buttons_can_use.append(_)
  339. distance_buttons_can_use[0].click()
  340. for i in range(4):
  341. distance_buttons_can_use[0].send_keys(Keys.BACKSPACE)
  342. distance_buttons_can_use[0].send_keys(config_info['marginTop'])
  343. distance_buttons_can_use[1].click()
  344. for i in range(4):
  345. distance_buttons_can_use[1].send_keys(Keys.BACKSPACE)
  346. distance_buttons_can_use[1].send_keys(config_info['marginBottom'])
  347. logging.info('图片模块设置结束')
  348. def set_content(self, config_info):
  349. logging.info(' 开始设置文本模块')
  350. # 设置文本模块
  351. # 1.文本内容2.文本颜色3.是否加粗4.文本位置设置5.文本大小
  352. # 设置文本内容
  353. self.driver.find_element_by_xpath('//*[@id="stage-sidebar"]/section/div[2]/div[5]/div/div').click()
  354. time.sleep(random.uniform(0.2, 0.3))
  355. input_elements = self.driver.find_elements_by_xpath('//textarea[@class="adui-input-base"]')
  356. # pyperclip.copy(config_info['text'])
  357. js_base = '''
  358. function copyToClipboard(text) {
  359. var dummy = document.createElement("textarea");
  360. // to avoid breaking orgain page when copying more words
  361. // cant copy when adding below this code
  362. // dummy.style.display = 'none'
  363. document.body.appendChild(dummy);
  364. //Be careful if you use texarea. setAttribute('value', value), which works with "input" does not work with "textarea". – Eduard
  365. dummy.value = text;
  366. dummy.select();
  367. document.execCommand("copy");
  368. document.body.removeChild(dummy);
  369. }
  370. '''
  371. content_change = config_info['text']
  372. content_change = content_change.replace("\\", "\\\\")
  373. content_change = content_change.replace('\n', '\\n')
  374. content_change = content_change.replace('\n', '\\n')
  375. content_change = content_change.replace("'", "\'")
  376. js_base = js_base + '''copyToClipboard('{}');'''.format(content_change)
  377. self.driver.execute_script(js_base)
  378. for _ in input_elements:
  379. if _.is_enabled() and _.is_displayed():
  380. _.send_keys(Keys.CONTROL, 'v')
  381. logging.info('文本内容设置完毕')
  382. # 设置颜色
  383. if config_info['color'] != '#595959':
  384. color_buttons = self.driver.find_elements_by_class_name('adui-cp-picker')
  385. c_buttons_can_use = []
  386. for _ in color_buttons:
  387. if _.is_displayed() and _.is_enabled():
  388. c_buttons_can_use.append(_)
  389. c_buttons_can_use[1].click()
  390. time.sleep(random.uniform(0.2, 0.3))
  391. input_elements = self.driver.find_elements_by_xpath(
  392. '//div[@class="adui-cp-input adui-input-wrapper adui-input-small adui-input-light adui-input-normal"]/input')
  393. for _ in input_elements:
  394. if _.is_enabled() and _.is_displayed() and _.get_attribute("value") == '595959':
  395. _.click()
  396. _.send_keys(config_info['color'])
  397. # 设置是否加粗
  398. if config_info['fontWeight'] != 'normal':
  399. big_elements = self.driver.find_elements_by_xpath(
  400. '//button[@class="adui-button-base adui-button-normal adui-button-mini"]/span')
  401. for _ in big_elements:
  402. if _.text == '加粗' and _.is_displayed() and _.is_enabled():
  403. _.click()
  404. # 设置文本位置
  405. if config_info['textAlign'] != 'left':
  406. loc_buttons = self.driver.find_elements_by_xpath(
  407. '//button[@class="adui-button-base adui-button-normal adui-button-mini adui-button-hasLeftIcon adui-button-hasRightIcon"]')
  408. loc_buttons_can_use = []
  409. for _ in loc_buttons:
  410. if _.is_enabled() and _.is_displayed():
  411. loc_buttons_can_use.append(_)
  412. if config_info['textAlign'] == 'center':
  413. loc_buttons_can_use[0].click()
  414. if config_info['textAlign'] == 'right':
  415. loc_buttons_can_use[1].click()
  416. # 设置文本大小
  417. if config_info['fontSize'] != 15:
  418. str_buttons = self.driver.find_elements_by_xpath("//*[@class='adui-select-selection-search']")
  419. str_buttons_can_use = []
  420. for _ in str_buttons:
  421. if _.is_displayed() and _.is_enabled():
  422. str_buttons_can_use.append(_)
  423. str_buttons_can_use[1].click()
  424. time.sleep(random.uniform(0.5, 1))
  425. str_num_buttons = self.driver.find_elements_by_xpath("//*[@class='adui-select-item-option-content']")
  426. str_num_can_use = []
  427. for _ in str_num_buttons:
  428. if _.is_displayed() and _.is_enabled():
  429. str_num_can_use.append(_)
  430. # str_num_map = {14: 0, 15: 1, 16: 2, 18: 3, 20: 4, 24: 5, 36: 6}
  431. # str_num_can_use[str_num_map[config_info['fontSize']]].click()
  432. for _ in str_num_can_use:
  433. if _.text == str(config_info['fontSize']):
  434. _.click()
  435. # 设置文本边距
  436. if config_info['marginTop'] != 22 or config_info['marginBottom'] != 22:
  437. distance_buttons = self.driver.find_elements_by_xpath(
  438. '//div[@class="adui-numeric-input adui-input-wrapper adui-input-small adui-input-normal"]/input')
  439. distance_buttons_can_use = []
  440. for _ in distance_buttons:
  441. if _.is_enabled() and _.is_displayed() and _.get_attribute("value") == '22':
  442. distance_buttons_can_use.append(_)
  443. distance_buttons_can_use[0].click()
  444. for i in range(4):
  445. distance_buttons_can_use[0].send_keys(Keys.BACKSPACE)
  446. distance_buttons_can_use[0].send_keys(config_info['marginTop'])
  447. distance_buttons_can_use[1].click()
  448. for i in range(4):
  449. distance_buttons_can_use[1].send_keys(Keys.BACKSPACE)
  450. distance_buttons_can_use[1].send_keys(config_info['marginBottom'])
  451. def set_follow_button(self, config_info):
  452. logging.info('设置一键关注')
  453. # 设置关注按钮
  454. # 0.是否一键关注 1.设置button文本内容 2.是否加粗 3.设置字体颜色,边框颜色,填充色 4.设置边距
  455. self.driver.find_element_by_xpath('//*[@id="stage-sidebar"]/section/div[4]/div[2]/div/div').click()
  456. # 开始设置一键关注
  457. if 'follow' in config_info.keys():
  458. if not config_info['follow']:
  459. time.sleep(0.1)
  460. follow_bs = self.driver.find_elements_by_xpath(
  461. '//*[@class="form-caption-3Xp3o6Drwf"]/div/span')
  462. for _ in follow_bs:
  463. if _.is_displayed() and _.is_enabled():
  464. _.click()
  465. # 文本设置
  466. input_elements = self.driver.find_elements_by_xpath(
  467. '//div[@class="adui-input-wrapper adui-input-mini adui-input-limited adui-input-normal"]/input')
  468. for _ in input_elements:
  469. if _.is_enabled() and _.is_displayed() and _.get_attribute("value") == '关注公众号':
  470. _.click()
  471. # for i in range(10):
  472. # _.send_keys(Keys.BACKSPACE)
  473. _.send_keys(Keys.CONTROL + 'a')
  474. _.send_keys(Keys.BACKSPACE)
  475. _.send_keys(config_info['text'])
  476. # 文本是否加粗
  477. if config_info['fontWeight'] != 'normal':
  478. big_elements = self.driver.find_elements_by_xpath(
  479. '//button[@class="adui-button-base adui-button-normal adui-button-mini"]/span')
  480. for _ in big_elements:
  481. if _.text == '加粗' and _.is_displayed() and _.is_enabled():
  482. _.click()
  483. # 颜色设置
  484. input_elements = self.driver.find_elements_by_xpath(
  485. '//div[@class="adui-cp-input adui-input-wrapper adui-input-mini adui-input-normal"]/input')
  486. input_elements_can_use = []
  487. for _ in input_elements:
  488. if _.is_enabled() and _.is_displayed() and (
  489. _.get_attribute("value") == '07C160' or _.get_attribute("value") == 'FFFFFF'):
  490. input_elements_can_use.append(_)
  491. if config_info['textColor'] != '#FFFFFF':
  492. input_elements_can_use[0].click()
  493. input_elements_can_use[0].send_keys(config_info['textColor'])
  494. time.sleep(random.uniform(0.1, 0.5))
  495. if config_info['borderColor'] != '#FFFFFF':
  496. input_elements_can_use[1].click()
  497. input_elements_can_use[1].send_keys(config_info['borderColor'])
  498. time.sleep(random.uniform(0.1, 0.5))
  499. input_elements_can_use[2].click()
  500. input_elements_can_use[2].send_keys(config_info['backColor'])
  501. time.sleep(random.uniform(0.1, 0.5))
  502. # 间隙设置
  503. size_buttons = self.driver.find_elements_by_xpath(
  504. '//div[@class="adui-numeric-input adui-input-wrapper adui-input-small adui-input-normal"]/input')
  505. size_buttons_can_use = []
  506. for _ in size_buttons:
  507. if _.is_enabled() and _.is_displayed() and _.get_attribute("value") == '28':
  508. size_buttons_can_use.append(_)
  509. size_buttons_can_use[0].click()
  510. for i in range(4):
  511. size_buttons_can_use[0].send_keys(Keys.BACKSPACE)
  512. size_buttons_can_use[0].send_keys(config_info['marginTop'])
  513. size_buttons_can_use[1].click()
  514. for i in range(4):
  515. size_buttons_can_use[1].send_keys(Keys.BACKSPACE)
  516. size_buttons_can_use[1].send_keys(config_info['marginBottom'])
  517. logging.info('设置一键关注,结束')
  518. def set_text_button(self):
  519. # 设置图文按钮
  520. pass
  521. def send_file_multi(self, layout, err_num=0):
  522. # 有问题,暂时不使用
  523. # 上传文件,文件上传与整体流程切割开
  524. token = re.findall('token=(\d+)', self.driver.current_url)
  525. url_ = 'https://mp.weixin.qq.com/promotion/frame?t=ad_system/common_frame&t1=material_std/material_library&token={}'.format(
  526. token[0])
  527. js = "window.open('{}')".format(url_)
  528. self.driver.execute_script(js)
  529. time.sleep(random.uniform(1, 2))
  530. self.driver.switch_to.window(self.driver.window_handles[-1])
  531. file_set = set()
  532. for v in layout.values():
  533. if isinstance(v, dict):
  534. if 'multi_page' in v.keys() or 'page' in v.keys() or 'movie' in v.keys():
  535. v_name = list(v.keys())[0]
  536. if isinstance(v[v_name], list):
  537. for _ in v[v_name]:
  538. file_set.add(_)
  539. else:
  540. file_set.add(v[v_name])
  541. file_list = list(file_set)
  542. # print(file_list)
  543. # print('file list size', len(file_list))
  544. send_file_sign = self.send_file_limit_num
  545. for _ in file_list:
  546. # print(_)
  547. self.driver.find_element_by_xpath('//*[@id="wxadcontainer"]/div[1]/div[2]/div[4]/input').send_keys(_)
  548. time.sleep(100)
  549. while True:
  550. # 失败,失败直接退出重跑
  551. # 正在上传数量,如果是正在上传小于限定数量,就不断输入
  552. # 上传完毕,小于限定数量,不断输入,.....已经上传好了,就关闭
  553. time.sleep(1)
  554. send_reslut_content = self.driver.find_element_by_xpath(
  555. '//*[@id="wxadcontainer"]/div[1]/div[2]/div[4]/div/strong').text
  556. if '失败' in send_reslut_content:
  557. if err_num < 3:
  558. self.send_file(layout, err_num=err_num + 1)
  559. else:
  560. exit()
  561. if '正在上传' in send_reslut_content:
  562. if send_file_sign < len(file_list):
  563. send_num = re.findall('(\d+)', send_reslut_content)[0]
  564. if int(send_num) < self.send_file_limit_num:
  565. # 还可以继续上传
  566. x = send_file_sign
  567. can_send_sign = send_file_sign + (self.send_file_limit_num - send_num)
  568. y = can_send_sign if can_send_sign < len(file_list) else len(file_list)
  569. for i in range(x, y):
  570. send_file_sign = send_file_sign + 1
  571. self.driver.find_element_by_xpath(
  572. '//*[@id="wxadcontainer"]/div[1]/div[2]/div[4]/input').send_keys(file_list[i])
  573. else:
  574. pass
  575. if '上传成功' in send_reslut_content:
  576. if send_file_sign < len(file_list):
  577. send_num = re.findall('(\d+)', send_reslut_content)[0]
  578. if int(send_num) < self.send_file_limit_num:
  579. # 还可以继续上传
  580. x = send_file_sign
  581. can_send_sign = send_file_sign + (self.send_file_limit_num - send_num)
  582. y = can_send_sign if can_send_sign < len(file_list) else len(file_list)
  583. for i in range(x, y):
  584. send_file_sign = send_file_sign + 1
  585. self.driver.find_element_by_xpath(
  586. '//*[@id="wxadcontainer"]/div[1]/div[2]/div[4]/input').send_keys(file_list[i])
  587. else:
  588. break
  589. time.sleep(1000)
  590. self.driver.execute_script('window.close();')
  591. def send_file(self, file_link, err_num=0):
  592. # 下载图片,存储于
  593. try:
  594. if not os.path.exists(self.img_dir):
  595. os.makedirs(self.img_dir)
  596. link_pra = re.split('\/', file_link)
  597. file_path = os.getcwd() + '/' + self.img_dir + '/' + link_pra[-1]
  598. rsp = requests.get(file_link, timeout=0.5)
  599. with open(file_path, 'wb') as f:
  600. f.write(rsp.content)
  601. # 有问题,暂时不使用
  602. self.driver.execute_script('location.reload();')
  603. self.driver.find_element_by_xpath('//*[@id="wxadcontainer"]/div[1]/div[2]/div[4]/input').send_keys(
  604. file_path)
  605. except:
  606. if err_num < 20:
  607. self.send_file(file_link, err_num=err_num + 1)
  608. def send_file_alone(self, layout):
  609. def check_file(file_set, err_num=0):
  610. try:
  611. logging.info('start check file')
  612. token = re.findall('token=(\d+)', self.driver.current_url)
  613. token = str(token[0])
  614. cookies = self.driver.get_cookies()
  615. cookie_dict = {}
  616. for _ in cookies:
  617. cookie_dict[_['name']] = _['value']
  618. wechat_url = 'https://mp.weixin.qq.com/promotion/material/material_library'
  619. headers = {
  620. 'content-type': 'application/x-www-form-urlencoded',
  621. 'referer': 'https://mp.weixin.qq.com/promotion/frame?t=ad_system/common_simple_frame&t1=rdcanvas/canvas_edit_v2&token={}'.format(
  622. token),
  623. 'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36'}
  624. data = 'action=get_material_data&args={"condition_list":[{"min_width":0,"min_height":0}],"type":1,"page":1,"page_size":8}&token=' + token + '&appid=&spid=&_=' + str(
  625. int(time.time() * 1000))
  626. rsp = requests.post(wechat_url, data=data, headers=headers, cookies=cookie_dict, timeout=1)
  627. total_page = rsp.json()['data']['page_info']['total_page']
  628. import copy
  629. tmp_file_set = copy.deepcopy(file_set)
  630. for file_url in tmp_file_set:
  631. link_pra = re.split('\/', file_url)[-1]
  632. for page_info in rsp.json()['data']['list']:
  633. if link_pra == page_info['name']:
  634. if file_url in file_set:
  635. file_set.remove(file_url)
  636. for i in range(2, total_page + 1):
  637. data = 'action=get_material_data&args={"condition_list":[{"min_width":0,"min_height":0}],"type":1,"page":' + str(
  638. i) + ',"page_size":8}&token=' + token + '&appid=&spid=&_=' + str(
  639. int(time.time() * 1000))
  640. rsp = requests.post(wechat_url, data=data, headers=headers, cookies=cookie_dict, timeout=1)
  641. tmp_file_set = copy.deepcopy(file_set)
  642. for file_url in tmp_file_set:
  643. link_pra = re.split('\/', file_url)[-1]
  644. for page_info in rsp.json()['data']['list']:
  645. if link_pra == page_info['name']:
  646. file_set.remove(file_url)
  647. except Exception as e:
  648. if err_num < 10:
  649. check_file(file_set, err_num + 1)
  650. def get_url(v_info):
  651. if 'url' in v_info.keys():
  652. file_set.add(v_info['url'])
  653. for v in v_info.values():
  654. if isinstance(v, dict):
  655. get_url(v)
  656. if isinstance(v, list):
  657. for _ in v:
  658. if isinstance(_, dict):
  659. get_url(_)
  660. # 上传文件,单线程
  661. # 上传文件,文件上传与整体流程切割开
  662. logging.info('开始传送文件')
  663. file_set = set()
  664. get_url(layout)
  665. check_file(file_set)
  666. if not file_set:
  667. logging.info('文件都已传送,无需重新上传')
  668. return
  669. WebDriverWait(self.driver, 10).until(
  670. lambda x: len(re.findall('token=(\d+)', self.driver.current_url)))
  671. token = re.findall('token=(\d+)', self.driver.current_url)
  672. url_ = 'https://mp.weixin.qq.com/promotion/frame?t=ad_system/common_frame&t1=material_std/material_library&token={}'.format(
  673. token[0])
  674. js = "window.open('{}')".format(url_)
  675. self.driver.execute_script(js)
  676. time.sleep(random.uniform(1, 2))
  677. self.driver.switch_to.window(self.driver.window_handles[-1])
  678. # TODO:之后还有multi_page 和 movie
  679. # for v in layout.values():
  680. # if isinstance(v, dict):
  681. # if 'multi_page' in v.keys() or 'page' in v.keys() or 'movie' in v.keys():
  682. # v_name = list(v.keys())[0]
  683. # if isinstance(v[v_name], list):
  684. # for _ in v[v_name]:
  685. # file_set.add(_)
  686. # else:
  687. # file_set.add(v[v_name])
  688. for _ in file_set:
  689. self.send_file(_)
  690. err_num = 0
  691. while True:
  692. time.sleep(1)
  693. send_reslut_content = self.driver.find_element_by_xpath(
  694. '//*[@id="wxadcontainer"]/div[1]/div[2]/div[4]/div/strong').text
  695. if '上传成功' in send_reslut_content:
  696. break
  697. if '失败' in send_reslut_content:
  698. err_num = err_num + 1
  699. self.send_file(_)
  700. if err_num > 3:
  701. raise ValueError("图片上传失败")
  702. self.driver.execute_script('window.close();')
  703. self.driver.switch_to.window(self.driver.window_handles[-1])
  704. logging.info('传送文件,结束')
  705. def set_share_content(self, title_content, des_content):
  706. self.driver.find_element_by_xpath('//*[@id="wxadcontainer"]/div[1]/div/section/header/div[2]/button[2]').click()
  707. WebDriverWait(self.driver, 50).until(lambda driver: driver.find_element_by_xpath(
  708. '//*[@id="wxadcontainer"]/div[1]/div/div/div/div[1]/section/section/div[2]/div[1]/div/div/input'))
  709. title_input = self.driver.find_element_by_xpath(
  710. '//*[@id="wxadcontainer"]/div[1]/div/div/div/div[1]/section/section/div[2]/div[1]/div/div/input')
  711. title_input.click()
  712. title_input.send_keys(title_content)
  713. des_input = self.driver.find_element_by_xpath(
  714. '//*[@id="wxadcontainer"]/div[1]/div/div/div/div[1]/section/section/div[2]/div[2]/div/div/input')
  715. des_input.click()
  716. des_input.send_keys(des_content)
  717. self.driver.find_element_by_xpath('//*[@id="wxadcontainer"]/div[1]/div/header/div[2]/button').click()
  718. WebDriverWait(self.driver, 10).until(
  719. lambda driver: driver.find_element_by_xpath('/html/body/div[6]/div/div/div[2]/div/div[3]/button[2]'))
  720. self.driver.find_element_by_xpath('/html/body/div[6]/div/div/div[2]/div/div[3]/button[2]').click()
  721. time.sleep(1)
  722. @staticmethod
  723. def check_sucess_api(log_ad, layout_name):
  724. # 默认在进入公众号初始页面
  725. logging.info('开始检查落地页是否创建成功')
  726. WebDriverWait(log_ad.driver, 100).until(
  727. lambda driver: True if 'token' in log_ad.driver.current_url else False)
  728. cookie_dict = log_ad.get_cookie(log_ad.driver, login_cookie=False)
  729. token_id = re.findall('token=(\d+)', log_ad.driver.current_url)[0]
  730. requests.session()
  731. layout_url = 'https://mp.weixin.qq.com/promotion/landingpage_manager?action=list_pages&page=1&page_size=10&canvas_name={layout_name}&login_from=&is_grant=0&owner_uid=&token={wechat_token}&appid=&spid=&_={time_}'.format(
  732. layout_name=layout_name, wechat_token=token_id, time_=int(time.time() * 1000))
  733. session = requests.session()
  734. rsp = session.get(url=layout_url, cookies=cookie_dict)
  735. result_json = rsp.json()
  736. if 'landing_page_list' in result_json.keys():
  737. if len(result_json['landing_page_list']):
  738. logging.info('对应落地页存在于微信后台')
  739. return True
  740. else:
  741. logging.info('对应落地页不存在于微信后台')
  742. def remove_all_file(self):
  743. try:
  744. shutil.rmtree(os.getcwd() + '/' + self.img_dir)
  745. except Exception as e:
  746. logging.info(str(e))
  747. def create_layout(self, layout, sql_session, err_num=0):
  748. try:
  749. self.send_file_alone(layout)
  750. self.set_advertisement_sign(layout_name=layout['layoutName'])
  751. self.set_head_assemb(layout['topContent'])
  752. self.set_background_color(layout['pageBackColor'])
  753. for _ in layout['content']:
  754. if _['type'] == 'img':
  755. self.set_page(_['content'])
  756. if _['type'] == 'txt':
  757. self.set_content(_['content'])
  758. if _['type'] == 'followAcc':
  759. self.set_follow_button(_['content'])
  760. self.set_share_content(title_content=layout['shareTittle'], des_content=layout['shareDesc'])
  761. self.remove_all_file()
  762. if self.check_sucess_api(layout_name=layout['layoutName'], log_ad=self.log_ad):
  763. return {'sucess': True, 'result_info': ''}
  764. else:
  765. if err_num > 3:
  766. return {'sucess': False, 'result_info': '过程中全程无错误,失败'}
  767. else:
  768. self.log_ad.refresh_driver()
  769. self.log_ad.select_ad_master(service_name=self.service_name, wechat_name=self.wechat_name,
  770. sql_session=sql_session)
  771. self.get_into_create_page()
  772. return self.create_layout(layout, sql_session, err_num=err_num + 1)
  773. except Exception as e:
  774. # raise
  775. logging.error(e)
  776. self.log_ad.driver.save_screenshot(
  777. 'user_id:{}_time_{}_layout_name:{}_layout_error.png'.format(self.log_ad.user_id,
  778. datetime.now().strftime(
  779. "%Y-%m-%d, %H:%M:%S"),
  780. layout['layoutName']))
  781. # TODO:有空时讲 e 内容设置为原始内容
  782. if err_num > 3:
  783. return {'sucess': False, 'result_info': str(e)}
  784. else:
  785. self.log_ad.refresh_driver()
  786. self.log_ad.select_ad_master(service_name=self.service_name, wechat_name=self.wechat_name,
  787. sql_session=sql_session)
  788. self.get_into_create_page()
  789. return self.create_layout(layout, sql_session, err_num=err_num + 1)
  790. if __name__ == '__main__':
  791. logging.basicConfig(
  792. handlers=[
  793. logging.handlers.RotatingFileHandler('./create_ad_layout.log',
  794. maxBytes=10 * 1024 * 1024,
  795. backupCount=5,
  796. encoding='utf-8')
  797. , logging.StreamHandler() # 供输出使用
  798. ],
  799. level=logging.INFO,
  800. format="%(asctime)s - %(levelname)s %(filename)s %(funcName)s %(lineno)s - %(message)s"
  801. )