create_ad_layout.py 37 KB

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