wangzhibo
2025-07-30 25087cbe79c8c4992551477d55d9db8bbea2202e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# -*- coding: utf-8 -*-
from playwright.async_api import Playwright, Page
import asyncio
import datetime
 
from auto_media_publisher.uploaders.uploader_base import UploaderBase
 
CONTENT_UPLOAD_URL="https://baijiahao.baidu.com/builder/rc/edit?type=videoV2"
VIDEO_PUBLISH_SECCESSFULL_URL = "https://baijiahao.baidu.com/builder/rc/clue**"
                                 
 
class UploaderBaijiahao(UploaderBase):
 
 
    async def virtual_scroll_select(self,page,text, wrapper_index = 0,panel_selector="div.rc-virtual-list-holder-inner",
                                item_selector_template='div.cheetah-select-item-option-content:has-text("{text}")',
                                step=200,    # 每次滚动像素
                                max_attempts=15,
                                direction="down"  # "down" 或 "up"
                               ):
        await page.locator('div.select-wrap').nth(wrapper_index).click()
        await asyncio.sleep(1) 
 
        panel = page.locator(panel_selector).nth(wrapper_index)
        item_selector = item_selector_template.format(text=text)
        
 
        for i in range(max_attempts):
            # 尝试查找
            items = page.locator(item_selector)
            if await items.count() > 0:
                # 找到了,滚下去保证可见,再点击
                await items.first.scroll_into_view_if_needed()
                await items.first.click()
                return
 
            # 没找到就滚一轮
            delta = step if direction == "down" else -step
            await panel.evaluate(f"(el) => el.scrollTop += {delta}")
            await asyncio.sleep(0.2)
        await asyncio.sleep(1)
 
        
    async def set_schedule_time(self, page):
        """
        TODO 时间选择,日后在处理 百家号的时间选择不准确,目前是随机
        """
        scheduled_time_day = f"{self.scheduled_time.month}月{self.scheduled_time.day}日" if self.scheduled_time.day >9  else f"{self.scheduled_time.month}月0{self.scheduled_time.day}日"
        scheduled_time_hour = f"{self.scheduled_time.hour}点"
        scheduled_time_min = f"{self.scheduled_time.minute}分"
        await page.locator("div.cheetah-modal-confirm-body-wrapper").wait_for(state='visible',timeout=30000)
 
        current_hour = datetime.datetime.now().hour
        current_min = datetime.datetime.now().minute
        direction_hour = "down" if self.scheduled_time.hour > current_hour else "up"
        direction_minute = "down" if self.scheduled_time.minute > current_min else "up"
 
        print(direction_hour)
        print(dir)
 
 
        try:
            await page.locator('div.select-wrap').nth(0).click()
            await asyncio.sleep(1) 
            target = page.locator(f'div[title="{scheduled_time_day}"]')
            #await target.wait_for(state='visible')
            await target.click()
            await asyncio.sleep(1) 
            await self.virtual_scroll_select(page,scheduled_time_hour,wrapper_index=1,direction=direction_hour)
            await self.virtual_scroll_select(page,scheduled_time_min,wrapper_index=2,direction=direction_minute)
            #await page.locator('div.select-wrap').nth(2).click()
            #await asyncio.sleep(1) 
            #target = page.locator(f'div.cheetah-select-item-option-content:has-text("{scheduled_time_min}")')
            #await target.wait_for(state='visible')
            #await target.scroll_into_view_if_needed()
            #await target.click()
            #await asyncio.sleep(1)
 
        except Exception as e:
            self.logger.error(f"[Baidu BaiJiaHao] 设置时间发生错误.{e}")
        
        #await page.locator("div.cheetah-modal-confirm-btns > button").nth(1).click()
 
    async def handle_upload_error(self, page):
        # 日后实现,目前没遇到
        return
        print("视频出错了,重新上传中")
 
    async def uploading_video(self, page):
        while True:
            upload_failed = await page.locator('div .cover-overlay:has-text("上传失败")').count()
            if upload_failed:
                self.logger.error("发现上传出错了...")
                # await self.handle_upload_error(page)  # 假设这是处理上传错误的函数
                return False
 
            uploading = await page.locator('div .cover-overlay:has-text("上传中")').count()
            if uploading:
                self.logger.info("正在上传视频中...")
                await asyncio.sleep(2)  # 等待2秒再次检查
                continue
 
            # 检查上传是否成功
            if not uploading and not upload_failed:
                self.logger.info("视频上传完毕")
                return True
 
    async def set_schedule_publish(self, page):
        while True:
            try:
                self.logger.info("开始点击发布定时...")
                buttons = page.get_by_role("button", name="定时发布")
                count = await buttons.count()
                for i in range(count):
                    btn = buttons.nth(i)
                    if await btn.is_visible():
                        await btn.click()
                        break
                await asyncio.sleep(2)  # 等待2秒再次检查
                await self.set_schedule_time(page)
            except Exception as e:
                self.logger.error(f"定时发布失败: {e}")
                raise  # 重新抛出异常,让重试装饰器捕获
 
    async def publish_video(self, page: Page):
        if self.scheduled_time != 0:
            # 定时发布
            await self.set_schedule_publish(page)
        else:
            # 立即发布
            await self.direct_publish(page)
 
    async def direct_publish(self, page):
        try:
            publish_button = page.locator("button >> text=发布")
            if await publish_button.count():
                await publish_button.click()
        except Exception as e:
            self.logger.error(f"直接发布视频失败: {e}")
            raise  # 重新抛出异常,让重试装饰器捕获
 
    async def add_title_tags(self, page):
        title_page = page.get_by_placeholder('添加标题获得更多推荐')
        if len(self.title) <= 8:
            self.title += " 你不知道的"
        await title_page.fill(self.title[:30])
    
    
    async def _upload_core(self) -> bool:
        try:
 
            page = await self.context.new_page()
            await page.goto(CONTENT_UPLOAD_URL, timeout=120000)
            self.logger.info(f"正在上传-------{self.path_media}")
            await page.wait_for_url(CONTENT_UPLOAD_URL, timeout=120000)
 
            # 点击 "上传视频" 按钮
            upload_div  =  page.locator(".PL3VsCg3MxIPY5UYiCS_")
            await upload_div.wait_for(state="visible", timeout=10000)
            async with page.expect_file_chooser() as fc_info:
                await upload_div.click()
            file_chooser = await fc_info.value
            await file_chooser.set_files(self.path_media)
            await asyncio.sleep(2)
 
            # 等待页面跳转到指定的 URL
            for i in range(10):
                try:
                    await page.locator("div#formMain:visible").wait_for(timeout=30000)
                    break
                except:
                    self.logger.info(f"第 {i+1} 次尝试:正在等待进入视频发布页面...")
                    await asyncio.sleep(0.1)
            else:
                self.logger.warning("超过最大尝试次数,仍未进入视频发布页面。")
 
            # 填充标题和话题
            # 这里为了避免页面变化,故使用相对位置定位:作品标题父级右侧第一个元素的input子元素
            await asyncio.sleep(1)
            self.logger.info("正在填充标题和话题...")
            await self.add_title_tags(page)
 
            upload_status = await self.uploading_video(page)
            if not upload_status:
                self.logger.error(f"发现上传出错了... 文件:{self.path_media}")
                raise
 
            # 判断视频封面图是否生成成功
            while True:
                self.logger.info("正在确认封面完成, 准备去点击定时/发布...")
                if await page.locator("div.erVanTLKMOvnU7OgFaoD img").count():
                    self.logger.info("封面已完成,点击定时/发布...")
                    break
                else:
                    self.logger.info("等待封面生成...")
                    await asyncio.sleep(3)
 
            await self.publish_video(page)
            await page.wait_for_timeout(2000)
            if await page.locator('div.passMod_dialog-page >> text=百度安全验证:visible').count():
                self.logger.error("出现验证,退出")
                raise Exception("出现验证,退出")
            await page.wait_for_url(VIDEO_PUBLISH_SECCESSFULL_URL, timeout=20000)
            self.logger.info("视频发布成功")
 
            await self.context.storage_state(path=self.cookie_path)  # 保存cookie
            self.logger.info('cookie更新完毕!')
            await asyncio.sleep(2)  # 这里延迟是为了方便眼睛直观的观看
 
            return True
        except Exception as e:
            self.logger.error(f"{self.user} upload video {self.path_media} fails, error is :{e}")
            return False