批量生成公众号内容:基于Selenium自动化的Perplexity内容生成方案

一、背景

在做公众号爆文内容的时候,我现在用的是 Perplexity 里的 Claude 4 Sonnet 模型来帮我生成文章。这个模型写出来的内容自然、有逻辑,而且不像很多 AI 那样一读就“假”,非常适合写公众号。

不过,每次用它生成文章的操作流程其实都一模一样:先打开 Perplexity 网站,然后把提前写好的提示词复制进去,等待生成好文章,随后下载 Markdown 文件,最后再手动排版、发布。这一整套流程差不多要 10 分钟,虽然也不算太久,但每天重复做这些机械动作,说实话挺费神,也挺浪费时的。

于是我就开始琢磨:这些重复的步骤能不能自动化?比如,我只需要提前把提示词准备好,程序就可以帮我自动打开网站、粘贴提示词、生成文章、下载文件——整个过程我都不用动手。最后我只需要花点时间排版、点击发布,这样不仅节省大量时间,还能让我把精力花在更重要的地方。

一)模型选择

为什么我选的是 Perplexity,而不是别的 AI 工具?主要有三个原因:

Perplexity 是可以联网的这一点非常重要。很多大模型虽然看起来很强,但信息是“闭门造车”的,经常一本正经地胡说八道。而 Perplexity 可以实时上网查资料,生成的内容更靠谱,也更接近最新的热点和趋势。同时相同的提示词,每次生成的内容不同。

它可以调用 Claude 模型。我自己测试过不少 AI 模型,写文章最自然、最不带“AI 味”的就是 Claude。它写出来的东西读起来像真人写的,不会干巴巴或尴尬,非常适合用在公众号里。

性价比真的高。Perplexity Pro 一年的会员,闲鱼上不到 70 块就能搞定。相较于 claude pro 每月 20 美金,这个简直是真香。唯一的问题是需要代理。

二)方案选择

之前我也尝试过用影刀 RPA 来实现网站自动化操作,比如自动打开网页、输入提示词、下载文件等。这类工具对非程序员很友好,逻辑也比较清晰。

但在实际使用中,我始终感觉不太顺手。操作流程看起来简单,但一旦遇到需要做判断、分支等稍复杂的逻辑时,学习成本就明显上来了。而且整个搭建过程对我来说比较别扭,效率也不高。

最终我还是选择用代码的方式来实现这些自动化功能。虽然前期写代码花了一些时间,但逻辑更灵活,后续维护也更轻松,整体体验比用可视化工具舒服得多。(可能也有自己是程序员的视角)

二、核心难点

一)cloudflare 验证如何过

自己常用浏览器配置,而非空白配置或其他配置。这种方式下,cloudflare 的验证会自动跳过。查看方法是自动化工具打开的浏览器,可以查看到之前的历史记录、登录状态。

在自己浏览器环境下打开 Perplexity 网站时,卡在 Cloudflare 的安全验证页面,需要手动点一下才能跳转到正常的搜索界面。这个问题的解决方式其实也很简单:多用几次 Perplexity 网站,保持登录状态,Cloudflare 就不会频繁弹出验证了。等网站“认出”你是正常用户后,后续基本可以自动跳转,不再卡在验证环节。

二)程序理解需要需要点击的位置

在让程序自动点击网页上的按钮之前,我们需要先搞清楚这个按钮在网页代码中的位置。就像你要告诉程序:‘去点这个地方’。即通过页面获取到需要点击位置的前端代码。

大模型可以帮我们看懂网页的代码,然后自动写出一段程序,告诉去点击这里。后面有关于具体的提示词写法(也很简单)。

介绍一个自己在处理正则表达式的一个小技巧:

大模型在处理正则表达式上面有很高的造诣,最起码比我要强(毕竟是真的不懂😧)。关于处理此类问题我采用最多的提示词是:

1
采用xx语言帮我实现正则表达式,输入的内容是:<原始内容>,输出的内容是:<提取内容>,请你帮我进行实现。

三、整体方案

根据日常的操作总结,分为以下 5 个步骤:

  1. 打开浏览器

  2. 打开 perplexity 网站

  3. 选择 Claude 4.0 Sonnet 模型

  4. 输入提示词并进行检索

  5. 导出 markdown 文件

四、操作步骤

一) 环境准备

1、下载 miniconda

下载地址:https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/ ,推荐下载 Miniconda3-py312 的版本

2、conda 修改清华源

在 conda 命令行下输入

1
conda config --set show_channel_urls yes

输入上述命令,可以直接生成。condara 文件。

修改.condarc 文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
channels:
- defaults
show_channel_urls: true
default_channels:
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/msys2
custom_channels:
conda-forge: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
pytorch: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud

更多具体可参考:

3、pip 修改为阿里源
1
2
pip config set global.index-url https://mirrors.aliyun.com/pypi/simple
pip config set install.trusted-host mirrors.aliyun.com
4、创建新环境
1
conda create -n <环境名> python=3.12

二)获取页面中的前端代码

鼠标右键单击就可以看到《检查》,点击后就可以看到对应的前端代码,然后直接双击代码就可以看到页面上哪部分被选中。然后就可以直接复制前端的代码。

三)开发阶段

1、创建基础代码提示词
1
python 使用selenium 打开perplexity 网站。

Windsurf 生成内容:

运行结果:

打开后发现,没有加载本机的配置文件及会出现目前浏览器处于调试状态

没有加载本机的配置文件可以通过历史记录等信息查看;浏览器处于调试状态是打开浏览器后可以看到有一行小字的。

2、根据上面出现的问题,输入以下提示词:
1
在配置中添加,用户的配置文件参数,同时打开的页面中有debug的内容,这部分需要去除

Windsurf 生成内容:

运行结果:

只打开了 chrome 浏览器,没有打开 perplexity 网站,同时出现错误内容输出

3、根据具体错误及具体的要求(加载用户数据),给出以下提示词:
1
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=chrome_options),出现这样子的 错误:Message: session not created: probably user data directory is already in use, please specify a unique value for --user-data-dir argument, or don't use --user-data-dir 。但是我还需要加载用户数据。应该怎么进行修改 @web

这是一个失败的经历,Windsurf 生成内容:

根据 AI 提示的内容,可以看到非常在努力的工作,给出推荐方案是:创建一个新的用户目录。

分析结果:

如果采用创建新用户的方案,cloudflare 验证码是通过不了的。抱着试试看的态度,准备切换浏览器,Chrome 不可以,那 Edge 是否可以呢。

4、Chrome 浏览器切换成 Edge 浏览器
1
由于采用chrome无法完成上述工作,请你帮我在采用相同的配置的前提下,采用edge浏览器完成上述内容。

Windsurf 生成内容:

运行结果:

顺利打开 perplexity 网站,同时采用的是本机的配置文件。

5、搜索框中输入提示词进行检索功能,提示词如下:
1
打开perplexity以后,需要进行输入内容,然后进行检索。

Windsurf 生成内容:

部分核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
# 查找搜索框 (Perplexity的主要输入框)
search_box = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "div[contenteditable='true']")))

# 清空搜索框并输入查询内容"
search_query = query
search_box.clear()
search_box.send_keys(search_query)
print(f"已在搜索框中输入: {search_query}")

# 提交查询 (按回车键)
search_box.send_keys(Keys.RETURN)
print("已提交搜索查询")

运行结果:

检索框中没有任何内容。大胆猜测没有定位到检索框的位置。

6、定位到检索框,提示词如下:
1
@perplexity_selenium.py#L108 目前输入框的前端代码是:<textarea placeholder="Ask anything…" class="overflow-auto max-h-[45vh] lg:max-h-[40vh] sm:max-h-[25vh] outline-none w-full font-sans caret-superDuper resize-none selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark dark:bg-offsetDark dark:text-textMainDark dark:placeholder-textOffDark placeholder:select-none bg-background text-textMain placeholder-textOff scrollbar-thumb-idle dark:scrollbar-thumb-idleDark scrollbar-thin scrollbar-track-transparent" autocomplete="off" rows="2" id="ask-input" data-1p-ignore="true" style="height: 48px !important;"></textarea>。请根据此选择合适的。

Windsurf 生成内容:

部分修改代码:

1
search_box = wait.until(EC.element_to_be_clickable((By.ID, "ask-input")))

运行结果:

实现了预期效果:打开 perplexity 了并输入了检索内容,完成检索

目前缺少的是选择模型和导出功能。

7、点击选择模型,提示词如下:

提示词的核心内容(自己实际操作选择模型的过程):

1
@perplexity_selenium.py#L108 目前点击页面的内容是<选择模型的长条出的前端代码1>,其中我需要选择的是<选择模型第一次要点击的位置处的前端代码2>,,需要对这个位置进行点击。

上面的 1,2 的位置如下图所示:

完整版内容如下,其实看上面的内容就够了。

1
2

@perplexity_selenium.py#L108 目前点击页面的内容是<div class="bg-background dark:bg-offsetDark flex items-center justify-self-end rounded-full col-start-3 row-start-2 -mr-1"><div style="opacity: 1;"><span><button aria-label="Choose a model" type="button" class="focus-visible:bg-offsetPlus dark:focus-visible:bg-offsetPlusDark hover:bg-offsetPlus text-textOff dark:text-textOffDark hover:text-textMain dark:hover:bg-offsetPlusDark dark:hover:text-textMainDark max-w-24 sm:max-w-none font-sans focus:outline-none outline-none outline-transparent transition duration-300 ease-out font-sans select-none items-center relative group/button justify-center text-center items-center rounded-lg cursor-pointer active:scale-[0.97] active:duration-150 active:ease-outExpo origin-center whitespace-nowrap inline-flex text-sm h-8 aspect-[9/8]" data-state="closed"><div class="flex items-center min-w-0 font-medium gap-1.5 justify-center"><div class="flex shrink-0 items-center justify-center size-4"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7999999999999998" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-cpu "><path d="M5 5m0 1a1 1 0 0 1 1 -1h12a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-12a1 1 0 0 1 -1 -1z"></path><path d="M9 9h6v6h-6z"></path><path d="M3 10h2"></path><path d="M3 14h2"></path><path d="M10 3v2"></path><path d="M14 3v2"></path><path d="M21 10h-2"></path><path d="M21 14h-2"></path><path d="M14 21v-2"></path><path d="M10 21v-2"></path></svg></div></div></button></span></div><span><button type="button" class="focus-visible:bg-offsetPlus dark:focus-visible:bg-offsetPlusDark hover:bg-offsetPlus text-textOff dark:text-textOffDark hover:text-textMain dark:hover:bg-offsetPlusDark dark:hover:text-textMainDark px-[4px] font-sans focus:outline-none outline-none outline-transparent transition duration-300 ease-out font-sans select-none items-center relative group/button justify-center text-center items-center rounded-lg cursor-pointer active:scale-[0.97] active:duration-150 active:ease-outExpo origin-center whitespace-nowrap inline-flex text-sm h-8 aspect-[9/8]" data-state="closed"><div class="flex items-center min-w-0 font-medium gap-1.5 justify-center"><div><div class="my-xs flex items-center"><div class="flex items-center gap-2"><div class="relative flex items-center justify-center rounded-full size-6" style="z-index: 0;"><div class="flex size-5 items-center justify-center"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7999999999999998" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-world "><path d="M3 12a9 9 0 1 0 18 0a9 9 0 0 0 -18 0"></path><path d="M3.6 9h16.8"></path><path d="M3.6 15h16.8"></path><path d="M11.5 3a17 17 0 0 0 0 18"></path><path d="M12.5 3a17 17 0 0 1 0 18"></path></svg></div></div></div></div></div></div></button></span><div class="flex items-center"><div data-state="closed"><div style="opacity: 1;"><span><button aria-label="Attach files" type="button" class="focus-visible:bg-offsetPlus dark:focus-visible:bg-offsetPlusDark hover:bg-offsetPlus text-textOff dark:text-textOffDark hover:text-textMain dark:hover:bg-offsetPlusDark dark:hover:text-textMainDark font-sans focus:outline-none outline-none outline-transparent transition duration-300 ease-out font-sans select-none items-center relative group/button justify-center text-center items-center rounded-lg cursor-pointer active:scale-[0.97] active:duration-150 active:ease-outExpo origin-center whitespace-nowrap inline-flex text-sm h-8 aspect-[9/8]" data-state="closed"><div class="flex items-center min-w-0 font-medium gap-1.5 justify-center"><div class="flex shrink-0 items-center justify-center size-4"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7999999999999998" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-paperclip "><path d="M15 7l-6.5 6.5a1.5 1.5 0 0 0 3 3l6.5 -6.5a3 3 0 0 0 -6 -6l-6.5 6.5a4.5 4.5 0 0 0 9 9l6.5 -6.5"></path></svg></div></div></button></span></div><input multiple="" accept=".pdf,.txt,.py,.ipynb,.js,.jsx,.html,.css,.java,.cs,.php,.c,.cpp,.cxx,.h,.hpp,.rs,.R,.Rmd,.swift,.go,.rb,.kt,.kts,.ts,.tsx,.m,.scala,.rs,.dart,.lua,.pl,.pm,.t,.sh,.bash,.zsh,.csv,.log,.ini,.config,.json,.yaml,.yml,.toml,.lua,.sql,.bat,.md,.coffee,.tex,.latex,.less,.pptx,.docx,.xlsx,.jpeg,.jpg,.jpe,.jp2,.png,.gif,.bmp,.tiff,.tif,.svg,.webp,.ico,.avif,.heic,.heif,.mp3,.wav,.aiff,.ogg,.flac,.mp4,.mpeg,.mov,.avi,.flv,.mpg,.webm,.wmv,.3gp" type="file" style="display: none;"></div></div><div class="relative"><button aria-label="Dictation" type="button" class="focus-visible:bg-offsetPlus dark:focus-visible:bg-offsetPlusDark hover:bg-offsetPlus text-textOff dark:text-textOffDark hover:text-textMain dark:hover:bg-offsetPlusDark dark:hover:text-textMainDark font-sans focus:outline-none outline-none outline-transparent transition duration-300 ease-out font-sans select-none items-center relative group/button justify-center text-center items-center rounded-lg cursor-pointer active:scale-[0.97] active:duration-150 active:ease-outExpo origin-center whitespace-nowrap inline-flex text-sm h-8 aspect-[9/8]" data-state="closed"><div class="flex items-center min-w-0 font-medium gap-1.5 justify-center"><div class="flex shrink-0 items-center justify-center size-4"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" stroke="none" class="tabler-icon tabler-icon-microphone-filled "><path d="M19 9a1 1 0 0 1 1 1a8 8 0 0 1 -6.999 7.938l-.001 2.062h3a1 1 0 0 1 0 2h-8a1 1 0 0 1 0 -2h3v-2.062a8 8 0 0 1 -7 -7.938a1 1 0 1 1 2 0a6 6 0 0 0 12 0a1 1 0 0 1 1 -1m-7 -8a4 4 0 0 1 4 4v5a4 4 0 1 1 -8 0v-5a4 4 0 0 1 4 -4"></path></svg></div></div></button></div><div class="ml-2"><button aria-label="Voice mode" type="button" class="bg-super dark:bg-superDark dark:text-backgroundDark text-white hover:opacity-80 font-sans focus:outline-none outline-none outline-transparent transition duration-300 ease-out font-sans select-none items-center relative group/button justify-center text-center items-center rounded-lg cursor-pointer active:scale-[0.97] active:duration-150 active:ease-outExpo origin-center whitespace-nowrap inline-flex text-sm h-8 aspect-[9/8]" data-state="closed"><div class="flex items-center min-w-0 font-medium gap-1.5 justify-center"><div class="flex shrink-0 items-center justify-center size-4"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" color="currentColor" fill="currentColor" fill-rule="evenodd"><path d="M0 12.6663C0 13.4018 0.59792 13.9997 1.33333 13.9997C2.06875 13.9997 2.66667 13.4018 2.66667 12.6663V11.333C2.66667 10.5975 2.06875 9.99967 1.33333 9.99967C0.59792 9.99967 0 10.5975 0 11.333V12.6663ZM6.66667 5.33301C7.40213 5.33301 8 5.93087 8 6.66634V17.333C8 18.0685 7.40213 18.6663 6.66667 18.6663C5.9312 18.6663 5.33333 18.0685 5.33333 17.333V6.66634C5.33333 5.93087 5.9312 5.33301 6.66667 5.33301ZM10.6667 21.333C10.6667 22.0685 11.2645 22.6663 12 22.6663C12.7355 22.6663 13.3333 22.0685 13.3333 21.333V2.66634C13.3333 1.93093 12.7355 1.33301 12 1.33301C11.2645 1.33301 10.6667 1.93093 10.6667 2.66634V21.333ZM17.3333 5.33301C18.0688 5.33301 18.6667 5.93087 18.6667 6.66634V17.333C18.6667 18.0685 18.0688 18.6663 17.3333 18.6663C16.5979 18.6663 16 18.0685 16 17.333V6.66634C16 5.93087 16.5979 5.33301 17.3333 5.33301ZM24 11.333C24 10.5975 23.4021 9.99967 22.6667 9.99967C21.9312 9.99967 21.3333 10.5975 21.3333 11.333V12.6663C21.3333 13.4018 21.9312 13.9997 22.6667 13.9997C23.4021 13.9997 24 13.4018 24 12.6663V11.333Z"></path></svg></div></div></button></div></div>,其中我需要选择的是<div style="opacity: 1;"><span><button aria-label="Choose a model" type="button" class="focus-visible:bg-offsetPlus dark:focus-visible:bg-offsetPlusDark hover:bg-offsetPlus text-textOff dark:text-textOffDark hover:text-textMain dark:hover:bg-offsetPlusDark dark:hover:text-textMainDark max-w-24 sm:max-w-none font-sans focus:outline-none outline-none outline-transparent transition duration-300 ease-out font-sans select-none items-center relative group/button justify-center text-center items-center rounded-lg cursor-pointer active:scale-[0.97] active:duration-150 active:ease-outExpo origin-center whitespace-nowrap inline-flex text-sm h-8 aspect-[9/8]" data-state="closed"><div class="flex items-center min-w-0 font-medium gap-1.5 justify-center"><div class="flex shrink-0 items-center justify-center size-4"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7999999999999998" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-cpu "><path d="M5 5m0 1a1 1 0 0 1 1 -1h12a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-12a1 1 0 0 1 -1 -1z"></path><path d="M9 9h6v6h-6z"></path><path d="M3 10h2"></path><path d="M3 14h2"></path><path d="M10 3v2"></path><path d="M14 3v2"></path><path d="M21 10h-2"></path><path d="M21 14h-2"></path><path d="M14 21v-2"></path><path d="M10 21v-2"></path></svg></div></div></button></span></div>,需要对这个位置进行点击。

Windsurf 生成内容:

运行结果:

可以弹出模型选择页面。

看完给出的修改内容:

//button[@aria-label=’Choose a model’]

感觉可能不太通用

8、点击模型的实现更加通用性,提示词如下:
1
@perplexity_selenium.py#L111-113 这个没有通用性,如果现在不是Choose a model,还有其他的,都是不相同的。有其他的方案么

Windsurf 生成内容:

运行结果:

完美匹配更加普遍的方式弹出模型选择页面。

9、根据弹出的内容,选择 Claude 模型,提示词如下:
1
2

@perplexity_selenium.py#L145-149 弹出的页面中的前端代码是:<弹出框的所有前端代码1>,我需要选择的是:<选择的Claude模型的前端代码2>,然后对这个进行点击。

上面的 1,2 的位置如下图所示:

完整的内容如下:

1
2

@perplexity_selenium.py#L145-149 弹出的页面中的前端代码是:<div class="flex h-full flex-col"><div class="scrollbar-thin scrollbar-track-transparent scrollbar-thumb-idle dark:scrollbar-thumb-idleDark min-h-0 flex-1 overflow-auto"><div class="p-xs"><div class="group/item md:h-full"><div class="relative select-none rounded-lg transition-all duration-300 px-sm py-1.5 md:h-full hover:bg-offset dark:hover:bg-offsetDark cursor-pointer"><div class="flex gap-sm"><div class="flex-1"><div class="flex flex-col gap-y-0.5"><div class="flex items-center gap-x-1.5 font-sans text-xs font-medium text-super dark:text-superDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark"><span>Best</span></div><div class="whitespace-pre-wrap font-sans text-xs font-normal text-textOff dark:text-textOffDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark">Selects the best model for each query</div></div></div></div></div></div><div class="sm:mx-sm my-sm border-t border-borderMain/50 ring-borderMain/50 divide-borderMain/50 dark:divide-borderMainDark/50 dark:ring-borderMainDark/50 dark:border-borderMainDark/50 bg-transparent"></div><div class="group/item md:h-full"><div class="relative select-none rounded-lg transition-all duration-300 px-sm py-1.5 md:h-full hover:bg-offset dark:hover:bg-offsetDark cursor-pointer"><div class="flex gap-sm"><div class="flex-1"><div class="flex flex-col gap-y-0.5"><div class="flex items-center gap-x-1.5 font-sans text-xs font-medium text-textMain dark:text-textMainDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark"><span>Sonar</span></div><div class="whitespace-pre-wrap font-sans text-xs font-normal text-textOff dark:text-textOffDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark">Perplexity's fast model</div></div></div></div></div></div><div class="group/item md:h-full"><div class="relative select-none rounded-lg transition-all duration-300 px-sm py-1.5 md:h-full hover:bg-offset dark:hover:bg-offsetDark cursor-pointer"><div class="flex gap-sm"><div class="flex-1"><div class="flex flex-col gap-y-0.5"><div class="flex items-center gap-x-1.5 font-sans text-xs font-medium text-textMain dark:text-textMainDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark"><span>Claude 4.0 Sonnet</span><div class="px-xs -mt-px inline-flex h-[1rem] rounded-[0.3125rem] border-borderMain/50 ring-borderMain/50 divide-borderMain/50 dark:divide-borderMainDark/50 dark:ring-borderMainDark/50 dark:border-borderMainDark/50 bg-super dark:bg-superDark"><span class="leading-[1rem] font-sans text-xs font-medium dark:text-light-text text-textMainDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark">new</span></div></div><div class="whitespace-pre-wrap font-sans text-xs font-normal text-textOff dark:text-textOffDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark">Anthropic's advanced model</div></div></div></div></div></div><div class="group/item md:h-full"><div class="relative select-none rounded-lg transition-all duration-300 px-sm py-1.5 md:h-full hover:bg-offset dark:hover:bg-offsetDark cursor-pointer"><div class="flex gap-sm"><div class="flex-1"><div class="flex flex-col gap-y-0.5"><div class="flex items-center gap-x-1.5 font-sans text-xs font-medium text-textMain dark:text-textMainDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark"><span>GPT-4.1</span></div><div class="whitespace-pre-wrap font-sans text-xs font-normal text-textOff dark:text-textOffDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark">OpenAI's advanced model</div></div></div></div></div></div><div class="group/item md:h-full"><div class="relative select-none rounded-lg transition-all duration-300 px-sm py-1.5 md:h-full hover:bg-offset dark:hover:bg-offsetDark cursor-pointer"><div class="flex gap-sm"><div class="flex-1"><div class="flex flex-col gap-y-0.5"><div class="flex items-center gap-x-1.5 font-sans text-xs font-medium text-textMain dark:text-textMainDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark"><span>Gemini 2.5 Pro</span></div><div class="whitespace-pre-wrap font-sans text-xs font-normal text-textOff dark:text-textOffDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark">Google's latest model</div></div></div></div></div></div><div class="group/item md:h-full"><div class="relative select-none rounded-lg transition-all duration-300 px-sm py-1.5 md:h-full hover:bg-offset dark:hover:bg-offsetDark cursor-pointer"><div class="flex gap-sm"><div class="flex-1"><div class="flex flex-col gap-y-0.5"><div class="flex items-center gap-x-1.5 font-sans text-xs font-medium text-textMain dark:text-textMainDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark"><span>Grok 3 Beta</span></div><div class="whitespace-pre-wrap font-sans text-xs font-normal text-textOff dark:text-textOffDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark">xAI's latest model</div></div></div></div></div></div><div class="sm:px-sm relative flex items-center px-0"><div class="my-sm pr-sm border-borderMain/50 ring-borderMain/50 divide-borderMain/50 dark:divide-borderMainDark/50 dark:ring-borderMainDark/50 dark:border-borderMainDark/50 bg-background dark:bg-backgroundDark"><div class="w-max text-2xs md:text-xs tracking-wide font-mono leading-none uppercase text-textOff dark:text-textOffDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark">Reasoning</div></div><div class="bg-border/75 h-px w-full"></div></div><div class="group/item md:h-full"><div class="relative select-none rounded-lg transition-all duration-300 px-sm py-1.5 md:h-full hover:bg-offset dark:hover:bg-offsetDark cursor-pointer"><div class="flex gap-sm"><div class="flex-1"><div class="flex flex-col gap-y-0.5"><div class="flex items-center gap-x-1.5 font-sans text-xs font-medium text-textMain dark:text-textMainDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark"><span>R1 1776</span></div><div class="whitespace-pre-wrap font-sans text-xs font-normal text-textOff dark:text-textOffDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark">Perplexity's unbiased reasoning model</div></div></div></div></div></div><div class="group/item md:h-full"><div class="relative select-none rounded-lg transition-all duration-300 px-sm py-1.5 md:h-full hover:bg-offset dark:hover:bg-offsetDark cursor-pointer"><div class="flex gap-sm"><div class="flex-1"><div class="flex flex-col gap-y-0.5"><div class="flex items-center gap-x-1.5 font-sans text-xs font-medium text-textMain dark:text-textMainDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark"><span>o4-mini</span></div><div class="whitespace-pre-wrap font-sans text-xs font-normal text-textOff dark:text-textOffDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark">OpenAI's latest reasoning model</div></div></div></div></div></div><div class="group/item md:h-full"><div class="relative select-none rounded-lg transition-all duration-300 px-sm py-1.5 md:h-full hover:bg-offset dark:hover:bg-offsetDark cursor-pointer"><div class="flex gap-sm"><div class="flex-1"><div class="flex flex-col gap-y-0.5"><div class="flex items-center gap-x-1.5 font-sans text-xs font-medium text-textMain dark:text-textMainDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark"><span>Claude 4.0 Sonnet Thinking</span><div class="px-xs -mt-px inline-flex h-[1rem] rounded-[0.3125rem] border-borderMain/50 ring-borderMain/50 divide-borderMain/50 dark:divide-borderMainDark/50 dark:ring-borderMainDark/50 dark:border-borderMainDark/50 bg-super dark:bg-superDark"><span class="leading-[1rem] font-sans text-xs font-medium dark:text-light-text text-textMainDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark">new</span></div></div><div class="whitespace-pre-wrap font-sans text-xs font-normal text-textOff dark:text-textOffDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark">Anthropic's reasoning model</div></div></div></div></div></div></div></div></div>.我需要选择的是:<div class="flex flex-col gap-y-0.5"><div class="flex items-center gap-x-1.5 font-sans text-xs font-medium text-textMain dark:text-textMainDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark"><span>Claude 4.0 Sonnet</span><div class="px-xs -mt-px inline-flex h-[1rem] rounded-[0.3125rem] border-borderMain/50 ring-borderMain/50 divide-borderMain/50 dark:divide-borderMainDark/50 dark:ring-borderMainDark/50 dark:border-borderMainDark/50 bg-super dark:bg-superDark"><span class="leading-[1rem] font-sans text-xs font-medium dark:text-light-text text-textMainDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark">new</span></div></div><div class="whitespace-pre-wrap font-sans text-xs font-normal text-textOff dark:text-textOffDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark">Anthropic's advanced model</div></div>。然后对这个进行点击

Windsurf 生成内容:

运行结果:

选择 Claude 4.0 Sonnet 模型,并进行了检索。

目前仅剩导出一项功能未完成。

10、增加导出功能,提示词如下:

核心提示词:

1
2

@perplexity_selenium.py#L212 导出按钮的前端代码是:<导出按钮的前端代码>。选择合适的,然后进行点击。

完整提示词:

1
2

@perplexity_selenium.py#L212 导出按钮的前端代码是:<button type="button" class="focus-visible:bg-offsetPlus dark:focus-visible:bg-offsetPlusDark hover:bg-offsetPlus text-textOff dark:text-textOffDark hover:text-textMain dark:hover:bg-offsetPlusDark dark:hover:text-textMainDark font-sans focus:outline-none outline-none outline-transparent transition duration-300 ease-out font-sans select-none items-center relative group/button justify-center text-center items-center rounded-full cursor-pointer active:scale-[0.97] active:duration-150 active:ease-outExpo origin-center whitespace-nowrap inline-flex text-sm h-8 pl-2.5 pr-3" data-state="closed"><div class="flex items-center min-w-0 font-medium gap-1.5 justify-center"><div class="flex shrink-0 items-center justify-center size-4"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7999999999999998" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-file-export "><path d="M14 3v4a1 1 0 0 0 1 1h4"></path><path d="M11.5 21h-4.5a2 2 0 0 1 -2 -2v-14a2 2 0 0 1 2 -2h7l5 5v5m-5 6h7m-3 -3l3 3l-3 3"></path></svg></div><div class="text-align-center relative truncate leading-loose -mb-px">Export</div></div></button>。选择合适的,然后进行点击。

Windsurf 生成内容:

运行结果:

可以点击导出按钮,并弹出导出选项

11、点击 markdown 文件进行下载,提示词如下:

核心提示词:

1
2

@perplexity_selenium.py#L236-237 点击后前端页面是:<弹出的所有选项1>。然后需要选择的是:<markdown选项2>

完整提示词:

1
2

@perplexity_selenium.py#L236-237 点击后前端页面是:<div class="p-xs"><div class="group/item md:h-full"><div class="relative select-none rounded-lg transition-all duration-300 px-sm py-1.5 md:h-full hover:bg-offset dark:hover:bg-offsetDark cursor-pointer"><div class="flex gap-sm"><div class="flex"><div class="pt-two flex size-5 justify-center leading-none font-sans text-base text-textMain dark:text-textMainDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7999999999999998" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-file-type-pdf opacity-90"><path d="M14 3v4a1 1 0 0 0 1 1h4"></path><path d="M5 12v-7a2 2 0 0 1 2 -2h7l5 5v4"></path><path d="M5 18h1.5a1.5 1.5 0 0 0 0 -3h-1.5v6"></path><path d="M17 18h2"></path><path d="M20 15h-3v6"></path><path d="M11 15v6h1a2 2 0 0 0 2 -2v-2a2 2 0 0 0 -2 -2h-1z"></path></svg></div></div><div class="flex-1"><div class="flex flex-col gap-y-0.5"><div class="flex items-center gap-x-1.5 font-sans text-[13px] text-textMain dark:text-textMainDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark"><span>PDF</span></div></div></div></div></div></div><div class="group/item md:h-full"><div class="relative select-none rounded-lg transition-all duration-300 px-sm py-1.5 md:h-full hover:bg-offset dark:hover:bg-offsetDark cursor-pointer"><div class="flex gap-sm"><div class="flex"><div class="pt-two flex size-5 justify-center leading-none font-sans text-base text-textMain dark:text-textMainDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7999999999999998" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-markdown opacity-90"><path d="M3 5m0 2a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v10a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2z"></path><path d="M7 15v-6l2 2l2 -2v6"></path><path d="M14 13l2 2l2 -2m-2 2v-6"></path></svg></div></div><div class="flex-1"><div class="flex flex-col gap-y-0.5"><div class="flex items-center gap-x-1.5 font-sans text-[13px] text-textMain dark:text-textMainDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark"><span>Markdown</span></div></div></div></div></div></div><div class="group/item md:h-full"><div class="relative select-none rounded-lg transition-all duration-300 px-sm py-1.5 md:h-full hover:bg-offset dark:hover:bg-offsetDark cursor-pointer"><div class="flex gap-sm"><div class="flex"><div class="pt-two flex size-5 justify-center leading-none font-sans text-base text-textMain dark:text-textMainDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7999999999999998" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-file-type-docx opacity-90"><path d="M14 3v4a1 1 0 0 0 1 1h4"></path><path d="M5 12v-7a2 2 0 0 1 2 -2h7l5 5v4"></path><path d="M2 15v6h1a2 2 0 0 0 2 -2v-2a2 2 0 0 0 -2 -2h-1z"></path><path d="M17 16.5a1.5 1.5 0 0 0 -3 0v3a1.5 1.5 0 0 0 3 0"></path><path d="M9.5 15a1.5 1.5 0 0 1 1.5 1.5v3a1.5 1.5 0 0 1 -3 0v-3a1.5 1.5 0 0 1 1.5 -1.5z"></path><path d="M19.5 15l3 6"></path><path d="M19.5 21l3 -6"></path></svg></div></div><div class="flex-1"><div class="flex flex-col gap-y-0.5"><div class="flex items-center gap-x-1.5 font-sans text-[13px] text-textMain dark:text-textMainDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark"><span>DOCX</span></div></div></div></div></div></div></div>。然后需要选择的是:<div class="relative select-none rounded-lg transition-all duration-300 px-sm py-1.5 md:h-full hover:bg-offset dark:hover:bg-offsetDark cursor-pointer"><div class="flex gap-sm"><div class="flex"><div class="pt-two flex size-5 justify-center leading-none font-sans text-base text-textMain dark:text-textMainDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7999999999999998" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-markdown opacity-90"><path d="M3 5m0 2a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v10a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2z"></path><path d="M7 15v-6l2 2l2 -2v6"></path><path d="M14 13l2 2l2 -2m-2 2v-6"></path></svg></div></div><div class="flex-1"><div class="flex flex-col gap-y-0.5"><div class="flex items-center gap-x-1.5 font-sans text-[13px] text-textMain dark:text-textMainDark selection:bg-super/50 selection:text-textMain dark:selection:bg-superDuper/10 dark:selection:text-superDark"><span>Markdown</span></div></div></div></div></div>

Windsurf 生成内容:

运行结果:

实现所有内容,完成内容的下载

至此,所有功能均实现。

完整代码如下:

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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
from selenium import webdriver
from selenium.webdriver.edge.service import Service
from selenium.webdriver.edge.options import Options
from webdriver_manager.microsoft import EdgeChromiumDriverManager
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
import time
import os
import psutil
import subprocess

def kill_edge_processes():
# 关闭所有Edge进程
print("检查并关闭已存在的Edge进程...")
for proc in psutil.process_iter(['pid', 'name']):
# 检查进程是否为Edge
if proc.info['name'] and 'msedge' in proc.info['name'].lower():
try:
process = psutil.Process(proc.info['pid'])
process.terminate()
print(f"已终止Edge进程 {proc.info['pid']}")
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
pass
print("Edge进程检查完成")

# 定义检测Cloudflare验证页面的函数
def is_cloudflare_page(driver):
try:
# 检查页面标题是否包含"请稍候"或"Please wait"
if "请稍候" in driver.title or "Please wait" in driver.title:
# 检查是否存在Cloudflare相关元素
cloudflare_elements = driver.find_elements(By.CSS_SELECTOR, "[src*='cloudflare.com']")
if len(cloudflare_elements) > 0:
print("域名匹配成功")
return True

# 检查是否存在challenge-form元素
challenge_elements = driver.find_elements(By.ID, "challenge-form")
if len(challenge_elements) > 0:
print("challenge-form匹配成功")
return True

# 检查页面源代码是否包含cloudflare相关字符串
page_source = driver.page_source.lower()
if "cloudflare" in page_source or "challenge" in page_source:
print("页面源代码匹配成功")
return True
print("未检测到Cloudflare验证页面")
return False
except Exception as e:
print(f"检查Cloudflare页面时出错: {e}")
return False

def open_perplexity(query):
# 首先关闭所有Edge进程
kill_edge_processes()

# 设置Edge选项
edge_options = Options()
# 取消下面的注释可以使Edge在后台运行
# edge_options.add_argument("--headless")

# 基本选项
edge_options.add_argument("--no-sandbox")
edge_options.add_argument("--disable-dev-shm-usage")

# 强制添加Edge本机的配置文件
user_data_dir = "C:\\Users\\robot\\AppData\\Local\\Microsoft\\Edge\\User Data"
profile_directory = "Profile 6"

# 检查用户数据目录是否存在
if os.path.exists(user_data_dir):
print(f"使用Edge配置文件目录: {user_data_dir}")
edge_options.add_argument(f"--user-data-dir={user_data_dir}")
edge_options.add_argument(f"--profile-directory={profile_directory}")
else:
print(f"警告: 配置文件目录不存在: {user_data_dir}")

# 强制设置避免Edge冲突
edge_options.add_argument("--remote-debugging-port=9222")
edge_options.add_argument("--no-first-run")
edge_options.add_argument("--no-default-browser-check")
edge_options.add_argument("--password-store=basic")

# 禁用调试和日志信息
edge_options.add_argument("--disable-logging")
edge_options.add_argument("--log-level=3")
edge_options.add_experimental_option("excludeSwitches", ["enable-logging", "enable-automation"])
edge_options.add_experimental_option("useAutomationExtension", False)
edge_options.add_argument('--remote-debugging-port=9222')
# 自动安装最新的EdgeDriver并启动浏览器
try:
# 尝试直接使用Edge驱动
driver = webdriver.Edge(options=edge_options)
print("成功打开Edge浏览器")
except Exception as e:
print(f"尝试直接使用Edge驱动失败: {e}")
try:
# 尝试使用EdgeChromiumDriverManager安装驱动
driver = webdriver.Edge(service=Service(EdgeChromiumDriverManager().install()), options=edge_options)
print("成功使用EdgeChromiumDriverManager打开Edge浏览器")
except Exception as e2:
print(f"使用EdgeChromiumDriverManager安装驱动失败: {e2}")
# 最后尝试手动指定Edge和驱动路径
try:
# 可能的Edge路径
edge_path = "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe"
if os.path.exists(edge_path):
edge_options.binary_location = edge_path

# 手动下载并指定EdgeDriver路径
driver_path = os.path.join(os.getcwd(), "msedgedriver.exe")
if not os.path.exists(driver_path):
print("EdgeDriver不存在,请手动下载并放置在当前目录")
return

driver = webdriver.Edge(service=Service(driver_path), options=edge_options)
except Exception as e3:
print(f"所有方法都失败了: {e3}")
return

# 打开Perplexity网站
driver.get("https://www.perplexity.ai/")

# 等待页面加载(可以根据需要调整等待时间)
print("成功打开Perplexity网站")

# 等待搜索框出现并进行搜索
try:
# 等待最多30秒直到搜索框可见和可交互
wait = WebDriverWait(driver, 30) # 增加等待时间到30秒
print("等待搜索框出现")
time.sleep(5) # 先等待5秒,让页面完全加载

except Exception as e:
print(f"搜索过程中出现错误: {e}")

# 选择合适的模型
try:
# 方案一:使用CSS选择器定位模型选择按钮(使用它在页面中的位置和内部SVG元素)
# 选择输入框上方区域第一个包含CPU图标SVG的按钮
buttons = driver.find_elements(By.CSS_SELECTOR, "button")
model_button = None

# 选择方案1:尝试查找包含"tabler-icon-cpu"类的按钮
for button in buttons:
try:
# 检查按钮是否包含CPU图标的SVG
svg = button.find_elements(By.CSS_SELECTOR, "svg.tabler-icon-cpu")
if len(svg) > 0:
model_button = button
print("通过CPU图标找到模型选择按钮")
break
except:
continue

# 如果上面的方法失败,尝试方案2:查找页面上第一个带有data-state="closed"属性的按钮
if not model_button:
try:
model_buttons = driver.find_elements(By.CSS_SELECTOR, "button[data-state='closed']")
if len(model_buttons) > 0:
model_button = model_buttons[0] # 取第一个匹配的按钮
print("通过data-state属性找到模型选择按钮")
except:
pass
print("已找到模型选择按钮")

# 点击模型选择按钮
model_button.click()
print("已点击模型选择按钮")

# 等待模型选择列表出现
time.sleep(1) # 等待模型列表弹出

try:
# 尝试定位并选择 Claude 4.0 Sonnet 模型
# 方法1: 使用XPath通过文本内容定位
claude_option = wait.until(EC.element_to_be_clickable(
(By.XPATH, "//span[contains(text(), 'Claude 4.0 Sonnet')]/ancestor::div[contains(@class, 'cursor-pointer')]")))

if claude_option:
print("通过XPath找到 Claude 4.0 Sonnet 模型")

# 如果上面的方法失败,尝试更简单的方法
if not claude_option:
# 找到所有包含 Claude 4.0 Sonnet 文本的元素
elements = driver.find_elements(By.XPATH, "//span[contains(text(), 'Claude 4.0 Sonnet')]")
if elements:
# 得到父元素并点击
parent = elements[0]
for _ in range(5): # 尝试得6层父元素找到可点击的div
parent = parent.find_element(By.XPATH, "..")
if "cursor-pointer" in parent.get_attribute("class"):
claude_option = parent
break

# 点击选择该模型
claude_option.click()
print("已选择 Claude 4.0 Sonnet 模型")

# 等待模型切换完成
time.sleep(10)

except Exception as e:
print(f"选择 Claude 4.0 Sonnet 模型时出错: {e}")
# 如果无法选择特定模型,继续使用默认模型

except Exception as e:
print(f"点击模型选择按钮时出错: {e}")

# 查找搜索框 (Perplexity的主要输入框)
search_box = wait.until(EC.element_to_be_clickable((By.ID, "ask-input")))
# 清空搜索框并输入查询内容"
search_query = query
search_box.clear()
search_box.send_keys(search_query)
print(f"已在搜索框中输入: {search_query}")

# 提交查询 (按回车键)
search_box.send_keys(Keys.RETURN)
print("已提交搜索查询")

# 等待搜索结果加载
print("等待搜索结果加载...")

# 等待搜索结果出现
try:
# 创建一个更长时间的等待对象,特别用于搜索结果加载
long_wait = WebDriverWait(driver, 120) # 将等待时间设置为120秒(2分钟)
print("开始长时间等待搜索结果(最多2分钟)...")

# 方法1:使用导出按钮作为搜索完成的标志
long_wait.until(EC.presence_of_element_located((By.XPATH, "//div[contains(text(), 'Export')]")))
print("检测到导出按钮,搜索结果已加载完成")
except:
try:
# 方法2:如果无法找到导出按钮,尝试原来的方法
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "div.prose")))
print("检测到prose元素,搜索结果已加载完成")
except:
# 方法3:使用导出图标的SVG元素作为标志
wait.until(EC.presence_of_element_located(
(By.XPATH, "//svg[contains(@class, 'tabler-icon-file-export')]")))
print("检测到导出图标,搜索结果已加载完成")

# 点击导出按钮
try:
# 等待导出按钮可点击
print("尝试定位导出按钮...")

# 方法1:使用文本内容定位导出按钮
export_button = None
# 尝试方法1:通过文本内容定位导出按钮
try:
export_button = wait.until(EC.element_to_be_clickable(
(By.XPATH, "//div[contains(text(), 'Export')]/ancestor::button")))
print("通过文本内容找到导出按钮")
except Exception as e:
print(f"通过文本内容找到导出按钮失败: {e}")

# 如果上面的方法失败,尝试方法2
if not export_button:
try:
# 使用SVG图标定位导出按钮
svg_element = driver.find_element(By.XPATH, "//svg[contains(@class, 'tabler-icon-file-export')]")
export_button = svg_element.find_element(By.XPATH, "./ancestor::button")
print("通过SVG图标找到导出按钮")
except Exception as e:
print(f"通过SVG图标找到导出按钮失败: {e}")

# 如果找到了导出按钮
if export_button:
# 确保页面已经加载完成并且没有其他元素遮挡
time.sleep(1)
# 先滚动到元素位置
# 滑动到页面底部
#print("滑动到页面底部...")
#driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")

##向下滑动可以满足,但是看不到页面滑动
# actions = ActionChains(driver)
# for _ in range(5): # 示例:向下滚动5次 Page Down
# actions.send_keys(Keys.PAGE_DOWN).perform()
# time.sleep(0.5) # 短暂等待,让页面有时间响应滚动,更像真人操作
#time.sleep(1)

driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", export_button)
time.sleep(1)

# 尝试直接点击
try:
print("尝试直接点击导出按钮...")
export_button.click()
print("成功点击导出按钮")
except Exception as click_error:
print(f"直接点击失败: {click_error}")

# 尝试使用JavaScript点击
try:
print("尝试使用JavaScript点击导出按钮...")
driver.execute_script("arguments[0].click();", export_button)
print("使用JavaScript成功点击导出按钮")
except Exception as js_error:
print(f"JavaScript点击失败: {js_error}")

# 尝试使用Actions链点击
try:
actions = ActionChains(driver)
actions.move_to_element(export_button).click().perform()
print("使用ActionChains成功点击导出按钮")
except Exception as action_error:
print(f"ActionChains点击失败: {action_error}")
else:
print("未找到导出按钮,无法继续操作")

# 等待导出选项弹出
time.sleep(2)

# 选择Markdown导出格式
try:
# 方法1:通过文本内容定位 Markdown 选项
markdown_option = wait.until(EC.element_to_be_clickable(
(By.XPATH, "//span[text()='Markdown']/ancestor::div[contains(@class, 'cursor-pointer')]")))
print("通过文本内容找到 Markdown 选项")

# 如果方法1失败,尝试方法2
if not markdown_option:
# 方法2:通过 SVG 图标定位 Markdown 选项
svg_markdown = driver.find_element(By.XPATH, "//svg[contains(@class, 'tabler-icon-markdown')]")
markdown_option = svg_markdown.find_element(By.XPATH, "./ancestor::div[contains(@class, 'cursor-pointer')]")
print("通过 SVG 图标找到 Markdown 选项")

# 点击 Markdown 选项
markdown_option.click()
print("已选择 Markdown 格式")

# 等待下载开始
time.sleep(3)

# 如果需要处理下载对话框,可以在这里添加代码
print("导出操作完成")

except Exception as e:
print(f"选择 Markdown 格式时出错: {e}")
# 如果无法选择格式,也不影响程序继续运行

except Exception as e:
print(f"点击导出按钮时出错: {e}")



# 保持浏览器窗口打开一段时间,可以根据需要调整或删除
print("浏览器将保持打开状态30秒...")
time.sleep(30)

# 关闭浏览器
driver.quit() # 如果想要保`持浏览器打开,可以注释掉这一行

if __name__ == "__main__":
query="请帮我写一篇1000字左右的关于如何处理婆媳矛盾的文章,要求引经据典"
open_perplexity(query)

温情提示:

如果出现 cloudflare 验证码不能直接跳过的情况,关闭本脚本。请使用本机的 edge 浏览器(脚本加载的用户配置文件),手动点击验证码,多次尝试打开 perplexity,均可自动跳过验证码的时候,再次使用本脚本。

edge 浏览器采用的语言是英文。