create_ad.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. from wechat_action.login_ad import LogIn
  2. from selenium import webdriver
  3. from selenium.webdriver import ActionChains
  4. from selenium.webdriver.common.by import By
  5. from selenium.webdriver.support import expected_conditions as EC
  6. from selenium.webdriver.support.wait import WebDriverWait
  7. from selenium.webdriver import ChromeOptions
  8. import logging
  9. import time
  10. import random
  11. import re
  12. import os
  13. from logging import handlers
  14. class CreateAd:
  15. def __init__(self):
  16. # TODO:创建完页面需要关闭所有页面并回到一开始的页面
  17. self.driver = LogIn().get_driver_loged()
  18. self.get_into_create_page()
  19. self.send_file_limit_num = 8
  20. def get_into_create_page(self):
  21. # 进入创建页面
  22. self.driver.find_element_by_id('material').click()
  23. WebDriverWait(self.driver, 10).until(lambda driver: driver.find_element_by_class_name('ui-fl-r'))
  24. time.sleep(random.uniform(0.1, 0.2))
  25. self.driver.find_element_by_class_name('ui-fl-r').click()
  26. WebDriverWait(self.driver, 5).until(lambda driver: driver.find_element_by_css_selector(
  27. '#wxadcontainer > div:nth-child(1) > div:nth-child(2) > div.dialog-1fj_N480ZT > div > div > div:nth-child(1) > div.dialogCardFooter-17KpBD1lgN > button'))
  28. self.driver.find_element_by_css_selector(
  29. '#wxadcontainer > div:nth-child(1) > div:nth-child(2) > div.dialog-1fj_N480ZT > div > div > div:nth-child(1) > div.dialogCardFooter-17KpBD1lgN > button').click()
  30. self.driver.switch_to.window(self.driver.window_handles[-1])
  31. WebDriverWait(self.driver, 100).until(lambda driver: driver.find_element_by_class_name('addContent-8pexaaAGYy'))
  32. self.driver.find_element_by_class_name('addContent-8pexaaAGYy').click()
  33. WebDriverWait(self.driver, 100).until(lambda driver: driver.find_element_by_class_name('topArea-qOwEAeNuIn'))
  34. def create_layout(self):
  35. # 数据库获取数据,然后进行编排
  36. # 创建编排
  37. pass
  38. def set_head_assemb(self, info):
  39. def single_page_set():
  40. self.driver.find_element_by_xpath('//*[@id="stage-sidebar"]/div[1]/div/div[1]/div/div').click()
  41. time.sleep(random.uniform(0.1, 0.2))
  42. file_name = os.path.basename(info_v)
  43. self.driver.find_element_by_class_name('upload-img-item-inner-2gsg7NjaZ8').click()
  44. WebDriverWait(self.driver, 10).until(
  45. lambda x: len([_ for _ in self.driver.find_elements_by_class_name('title-29sncpKgTl') if
  46. _.is_displayed() and _.is_enabled()]) > 0)
  47. turn_page_buttons = self.driver.find_elements_by_class_name('paginationIcon-1EfoH0sNRF')
  48. can_use_button = []
  49. for _ in turn_page_buttons:
  50. if _.is_enabled() and _.is_displayed():
  51. can_use_button.append(_)
  52. # 不断翻页获取到元素为止
  53. chose_over = False
  54. while True:
  55. page_elements = self.driver.find_elements_by_class_name('title-29sncpKgTl')
  56. for _ in page_elements:
  57. if _.is_displayed() and _.is_enabled():
  58. if file_name in _.text:
  59. _.click()
  60. chose_over = True
  61. break
  62. if chose_over or len(can_use_button)==0:
  63. break
  64. # 翻到最后一页时停止
  65. page_text = self.driver.find_element_by_class_name('count_small-37CcvfzoTl').text
  66. page_nums = re.findall('\d+', page_text)
  67. if int(page_nums[0].strip()) == int(page_nums[1].strip()):
  68. break
  69. # 翻页
  70. can_use_button[2].click()
  71. # 确保翻页成功
  72. WebDriverWait(self.driver, 10).until(
  73. lambda x: page_text != self.driver.find_element_by_class_name('count_small-37CcvfzoTl').text)
  74. self.driver.find_element_by_xpath('//*[@id="test_material_container_confirm"]').click()
  75. try:
  76. WebDriverWait(self.driver, 4).until(
  77. lambda driver: driver.find_element_by_class_name(
  78. 'btnFist-uueBS6DQFa'))
  79. _ = self.driver.find_element_by_class_name(
  80. 'btnFist-uueBS6DQFa')
  81. WebDriverWait(self.driver, 4).until(
  82. lambda x: (_.is_displayed() and _.is_enabled()))
  83. time.sleep(1)
  84. _.click()
  85. WebDriverWait(self.driver, 100).until(
  86. lambda driver: driver.find_element_by_class_name(
  87. 'btn-3E823IXt3m'))
  88. _ = self.driver.find_element_by_class_name(
  89. 'btn-3E823IXt3m')
  90. WebDriverWait(self.driver, 100).until(
  91. lambda x: (_.is_displayed() and _.is_enabled()))
  92. time.sleep(1)
  93. _.click()
  94. except Exception as e:
  95. pass
  96. logging.info('头版首页单图上传结束')
  97. def multi_page_set():
  98. self.driver.find_element_by_css_selector(
  99. '#stage-sidebar > div.topArea-qOwEAeNuIn > div > div:nth-child(2)').click()
  100. time.sleep(random.uniform(0.1, 0.2))
  101. page_size = len(info_v)
  102. if page_size == 3:
  103. pass
  104. if page_size == 4:
  105. self.driver.find_element_by_css_selector(
  106. '#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()
  107. if page_size == 6:
  108. self.driver.find_element_by_css_selector(
  109. '#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()
  110. time.sleep(random.uniform(0.1, 0.2))
  111. self.driver.find_element_by_class_name('imageUploadItem-tA9JX0RWua').click()
  112. WebDriverWait(self.driver, 1000).until(
  113. lambda driver: self.driver.find_element_by_class_name('adui-tabs-tab')
  114. )
  115. input_elements = self.driver.find_elements_by_tag_name('input')
  116. input_find_element = None
  117. for _ in input_elements:
  118. if '输入关键词搜索素材' in _.get_attribute('placeholder'):
  119. input_find_element = _
  120. from selenium.webdriver.common.keys import Keys
  121. for _ in info_v:
  122. file_name = os.path.basename(_)
  123. logging.info(file_name)
  124. input_find_element.send_keys(file_name)
  125. input_find_element.send_keys(Keys.RETURN)
  126. WebDriverWait(self.driver, 1000).until(
  127. lambda driver: driver.find_element_by_class_name('img-2HvhMmpnzP'))
  128. file_element = self.driver.find_element_by_class_name('img-2HvhMmpnzP')
  129. WebDriverWait(self.driver, 1000).until(
  130. lambda x: (file_element.is_displayed() and file_element.is_enabled()))
  131. ActionChains(self.driver).move_to_element(file_element).perform()
  132. time.sleep(random.uniform(1, 2))
  133. file_element.click()
  134. for i in range(len(file_name) + 10):
  135. input_find_element.send_keys(Keys.BACKSPACE)
  136. time.sleep(random.uniform(1, 4))
  137. self.driver.find_element_by_xpath('/html/body/div[12]/div/div/div[2]/div/div[3]/button[2]').click()
  138. time.sleep(random.uniform(2, 4))
  139. # 切图操作如果有的话进行对应操作.
  140. for i in range(len(info_v) + 1):
  141. try:
  142. WebDriverWait(self.driver, 4).until(
  143. lambda driver: driver.find_element_by_xpath(
  144. '/html/body/div[13]/div/div/div[2]/div/div[3]/button[2]'))
  145. self.driver.find_element_by_xpath('/html/body/div[13]/div/div/div[2]/div/div[3]/button[2]').click()
  146. except:
  147. pass
  148. logging.info('头版多图选择 结束')
  149. info_key = list(info.keys())[0]
  150. info_v = info[info_key]
  151. if info_key == 'page':
  152. single_page_set()
  153. if info_key == 'multi_page':
  154. multi_page_set()
  155. # 设置头部组件
  156. pass
  157. def set_page(self, page_path):
  158. # 设置图片模块
  159. self.driver.find_element_by_xpath('//*[@id="stage-sidebar"]/section/div[2]/div[1]/div/div').click()
  160. time.sleep(random.uniform(0.1, 0.2))
  161. file_name = os.path.basename(page_path)
  162. upload_elements = self.driver.find_elements_by_class_name('upload-img-item-inner-2gsg7NjaZ8')
  163. for _ in upload_elements:
  164. if _.is_displayed() and _.is_enabled():
  165. _.click()
  166. WebDriverWait(self.driver, 10).until(
  167. lambda x: len([_ for _ in self.driver.find_elements_by_class_name('title-29sncpKgTl') if
  168. _.is_displayed() and _.is_enabled()]) > 0)
  169. turn_page_buttons = self.driver.find_elements_by_class_name('paginationIcon-1EfoH0sNRF')
  170. can_use_button = []
  171. for _ in turn_page_buttons:
  172. if _.is_enabled() and _.is_displayed():
  173. can_use_button.append(_)
  174. # 不断翻页获取到元素为止
  175. chose_over = False
  176. while True:
  177. page_elements = self.driver.find_elements_by_class_name('title-29sncpKgTl')
  178. for _ in page_elements:
  179. if _.is_displayed() and _.is_enabled():
  180. if file_name in _.text:
  181. _.click()
  182. chose_over = True
  183. break
  184. if chose_over or len(can_use_button)==0:
  185. #can_use_button 数量说明是否可以翻页,如果不可以翻页,则直接停止
  186. break
  187. # 翻到最后一页时停止
  188. page_text_elements = self.driver.find_elements_by_class_name('count_small-37CcvfzoTl')
  189. for _ in page_text_elements:
  190. if _.is_enabled() and _.is_displayed():
  191. page_text = _.text
  192. page_text_element = _
  193. page_nums = re.findall('\d+', page_text)
  194. if int(page_nums[0].strip()) == int(page_nums[1].strip()):
  195. break
  196. # 翻页
  197. can_use_button[2].click()
  198. # 确保翻页成功
  199. WebDriverWait(self.driver, 10).until(
  200. lambda x: page_text != page_text_element.text)
  201. sc_buttons = self.driver.find_elements_by_id('test_material_container_confirm')
  202. for _ in sc_buttons:
  203. if _.is_enabled() and _.is_displayed():
  204. _.click()
  205. try:
  206. WebDriverWait(self.driver, 4).until(
  207. lambda driver: driver.find_element_by_class_name(
  208. 'btnFist-uueBS6DQFa'))
  209. _ = self.driver.find_element_by_class_name(
  210. 'btnFist-uueBS6DQFa')
  211. WebDriverWait(self.driver, 4).until(
  212. lambda x: (_.is_displayed() and _.is_enabled()))
  213. #已经出现但任需要等待
  214. time.sleep(1)
  215. _.click()
  216. WebDriverWait(self.driver, 100).until(
  217. lambda x: len([_ for _ in self.driver.find_elements_by_class_name('btn-3E823IXt3m') if
  218. _.is_displayed() and _.is_enabled()]) > 0)
  219. for _ in self.driver.find_elements_by_class_name('btn-3E823IXt3m'):
  220. if _.is_displayed() and _.is_enabled():
  221. _.click()
  222. except Exception as e:
  223. logging.info(e)
  224. pass
  225. logging.info('图片模块设置结束')
  226. def set_title(self):
  227. # 设置标题模块
  228. pass
  229. def set_content(self):
  230. # 输入文本内容
  231. pass
  232. def set_follow_button(self):
  233. # TODO:先暂定设置三种格式
  234. # 设置关注按钮
  235. pass
  236. def set_set_text_button(self):
  237. # 设置图文按钮
  238. pass
  239. def send_file_multi(self, layout, err_num=0):
  240. # 有问题,暂时不使用
  241. # 上传文件,文件上传与整体流程切割开
  242. token = re.findall('token=(\d+)', self.driver.current_url)
  243. url_ = 'https://mp.weixin.qq.com/promotion/frame?t=ad_system/common_frame&t1=material_std/material_library&token={}'.format(
  244. token[0])
  245. js = "window.open('{}')".format(url_)
  246. self.driver.execute_script(js)
  247. time.sleep(random.uniform(1, 2))
  248. self.driver.switch_to.window(self.driver.window_handles[-1])
  249. file_set = set()
  250. for v in layout.values():
  251. if isinstance(v, dict):
  252. if 'multi_page' in v.keys() or 'page' in v.keys() or 'movie' in v.keys():
  253. v_name = list(v.keys())[0]
  254. if isinstance(v[v_name], list):
  255. for _ in v[v_name]:
  256. file_set.add(_)
  257. else:
  258. file_set.add(v[v_name])
  259. file_list = list(file_set)
  260. # print(file_list)
  261. # print('file list size', len(file_list))
  262. send_file_sign = self.send_file_limit_num
  263. for _ in file_list:
  264. # print(_)
  265. self.driver.find_element_by_xpath('//*[@id="wxadcontainer"]/div[1]/div[2]/div[4]/input').send_keys(_)
  266. time.sleep(100)
  267. while True:
  268. # 失败,失败直接退出重跑
  269. # 正在上传数量,如果是正在上传小于限定数量,就不断输入
  270. # 上传完毕,小于限定数量,不断输入,.....已经上传好了,就关闭
  271. time.sleep(1)
  272. send_reslut_content = self.driver.find_element_by_xpath(
  273. '//*[@id="wxadcontainer"]/div[1]/div[2]/div[4]/div/strong').text
  274. if '失败' in send_reslut_content:
  275. if err_num < 3:
  276. self.send_file(layout, err_num=err_num + 1)
  277. else:
  278. exit()
  279. if '正在上传' in send_reslut_content:
  280. if send_file_sign < len(file_list):
  281. send_num = re.findall('(\d+)', send_reslut_content)[0]
  282. if int(send_num) < self.send_file_limit_num:
  283. # 还可以继续上传
  284. x = send_file_sign
  285. can_send_sign = send_file_sign + (self.send_file_limit_num - send_num)
  286. y = can_send_sign if can_send_sign < len(file_list) else len(file_list)
  287. for i in range(x, y):
  288. send_file_sign = send_file_sign + 1
  289. self.driver.find_element_by_xpath(
  290. '//*[@id="wxadcontainer"]/div[1]/div[2]/div[4]/input').send_keys(file_list[i])
  291. else:
  292. pass
  293. if '上传成功' in send_reslut_content:
  294. if send_file_sign < len(file_list):
  295. send_num = re.findall('(\d+)', send_reslut_content)[0]
  296. if int(send_num) < self.send_file_limit_num:
  297. # 还可以继续上传
  298. x = send_file_sign
  299. can_send_sign = send_file_sign + (self.send_file_limit_num - send_num)
  300. y = can_send_sign if can_send_sign < len(file_list) else len(file_list)
  301. for i in range(x, y):
  302. send_file_sign = send_file_sign + 1
  303. self.driver.find_element_by_xpath(
  304. '//*[@id="wxadcontainer"]/div[1]/div[2]/div[4]/input').send_keys(file_list[i])
  305. else:
  306. break
  307. time.sleep(1000)
  308. self.driver.execute_script('window.close();')
  309. def send_file(self, file_path, err_num=0):
  310. # 有问题,暂时不使用
  311. self.driver.execute_script('location.reload();')
  312. self.driver.find_element_by_xpath('//*[@id="wxadcontainer"]/div[1]/div[2]/div[4]/input').send_keys(file_path)
  313. def send_file_alone(self, layout):
  314. # 上传文件,单线程
  315. # 上传文件,文件上传与整体流程切割开
  316. token = re.findall('token=(\d+)', self.driver.current_url)
  317. url_ = 'https://mp.weixin.qq.com/promotion/frame?t=ad_system/common_frame&t1=material_std/material_library&token={}'.format(
  318. token[0])
  319. js = "window.open('{}')".format(url_)
  320. self.driver.execute_script(js)
  321. time.sleep(random.uniform(1, 2))
  322. self.driver.switch_to.window(self.driver.window_handles[-1])
  323. file_set = set()
  324. for v in layout.values():
  325. if isinstance(v, dict):
  326. if 'multi_page' in v.keys() or 'page' in v.keys() or 'movie' in v.keys():
  327. v_name = list(v.keys())[0]
  328. if isinstance(v[v_name], list):
  329. for _ in v[v_name]:
  330. file_set.add(_)
  331. else:
  332. file_set.add(v[v_name])
  333. for _ in file_set:
  334. self.send_file(_)
  335. err_num = 0
  336. while True:
  337. time.sleep(1)
  338. send_reslut_content = self.driver.find_element_by_xpath(
  339. '//*[@id="wxadcontainer"]/div[1]/div[2]/div[4]/div/strong').text
  340. if '上传成功' in send_reslut_content:
  341. break
  342. if '失败' in send_reslut_content:
  343. err_num = err_num + 1
  344. self.send_file(_)
  345. if err_num > 3:
  346. # TODO:退出机制需要考虑一下,tornado返回错误信息
  347. exit()
  348. self.driver.execute_script('window.close();')
  349. self.driver.switch_to.window(self.driver.window_handles[-1])
  350. def send_moive_spic(self):
  351. # 特殊情况下,上传本地视频
  352. pass
  353. def get_layout(self):
  354. # 得到编排
  355. layout = {-1: {'page': '/usr/share/wallpapers/deepin/Beach_by_Samuel_Scrimshaw.jpg'},
  356. 0: {'title': '这是标题'},
  357. 1: {'content': '这是文本'},
  358. 2: {'follow_button': 1},
  359. 3: {'title': '这是标题'},
  360. 4: {'content': '这是文本'},
  361. 5: {'page': '/usr/share/wallpapers/deepin/Beach_by_Samuel_Scrimshaw.jpg'},
  362. 6: {'page': '/usr/share/wallpapers/deepin/Beach_by_Samuel_Scrimshaw.jpg'},
  363. 7: {'follow_button': 2},
  364. 8: {'follow_button': 3},
  365. }
  366. # TODO:测试,多图,测试,图文表
  367. layout = {-1: {'multi_page': ['/usr/share/wallpapers/deepin/Beach_by_Samuel_Scrimshaw.jpg',
  368. '/usr/share/wallpapers/deepin/Sunset_of_the_Lake_Nam_by_Wang_Jinyu.jpg',
  369. '/usr/share/wallpapers/deepin/The_Gongga_Mountain_by_wangjinyu.jpg',
  370. '/usr/share/wallpapers/deepin/Reflection_of_the_Kanas_Lake_by_Wang_Jinyu.jpg']},
  371. 0: {'page': '/usr/share/wallpapers/deepin/Beach_by_Samuel_Scrimshaw.jpg'}
  372. }
  373. layout = {
  374. -1: {'page': '/usr/share/wallpapers/deepin/Reflection_of_the_Kanas_Lake_by_Wang_Jinyu.jpg'},
  375. 0: {'page': '/usr/share/wallpapers/deepin/Reflection_of_the_Kanas_Lake_by_Wang_Jinyu.jpg'},
  376. 1: {'page': '/usr/share/wallpapers/deepin/Beach_by_Samuel_Scrimshaw.jpg'},
  377. 2: {'page': '/usr/share/wallpapers/deepin/The_Gongga_Mountain_by_wangjinyu.jpg'},
  378. 3: {'page': '/usr/share/wallpapers/deepin/Reflection_of_the_Kanas_Lake_by_Wang_Jinyu.jpg'},
  379. }
  380. # self.send_file_alone(layout)
  381. self.set_head_assemb(layout[-1])
  382. for _ in range(max(layout.keys())):
  383. info = layout[_]
  384. info_key = list(info.keys())[0]
  385. info_v = info[info_key]
  386. if info_key == 'page':
  387. self.set_page(info_v)
  388. if info_key == 'title':
  389. self.set_page(info_v)
  390. if info_key == 'content':
  391. self.set_content(info_v)
  392. if info_key == 'follow_button':
  393. self.set_follow_button(info_v)
  394. if __name__ == '__main__':
  395. logging.basicConfig(
  396. handlers=[
  397. logging.handlers.RotatingFileHandler('./create_ad.log',
  398. maxBytes=10 * 1024 * 1024,
  399. backupCount=5,
  400. encoding='utf-8')
  401. , logging.StreamHandler() # 供输出使用
  402. ],
  403. level=logging.INFO,
  404. format="%(asctime)s - %(levelname)s %(filename)s %(funcName)s %(lineno)s - %(message)s"
  405. )
  406. create_ad = CreateAd()
  407. create_ad.get_layout()