create_ad_layout.py 39 KB

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