尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于Doubao-Seed-Evolving与Playwright的智能网页正文提取工具实战

基于Doubao-Seed-Evolving与Playwright的智能网页正文提取工具实战 1. 项目概述与核心思路最近在整理一些技术资料经常需要从各种技术博客、文档网站里把正文内容扒下来存成结构清晰的Markdown文件。手动复制粘贴效率低不说还总带着一堆导航栏、侧边栏、广告、评论区的“杂质”后期整理起来特别费劲。市面上虽然有一些现成的网页正文提取工具或浏览器插件但要么收费要么提取效果不稳定尤其是面对现在各种用Vue、React做的单页面应用SPA或者结构复杂的文章页面经常“翻车”。正好我一直在关注一些前沿的AI和自动化工具。最近字节跳动的Doubao豆包系列模型开放了Seed-Evolving种子进化能力简单说就是它能根据你给的“种子”任务描述自我迭代和优化生成更复杂、更精准的解决方案或代码。这给了我一个灵感能不能结合这个“会进化”的AI能力加上Python强大的自动化脚本自己动手搓一个免费、高效且聪明的网页正文提取工具这个工具的核心目标很明确输入一个网页URL输出干净、结构化的正文Markdown文本。它需要能智能地识别并剔除无关的页面元素准确抓取标题、段落、列表、代码块等核心内容。为了实现这个目标我选择了“Doubao-Seed-Evolving Python Playwright”的技术栈。Doubao-Seed-Evolving负责理解和优化“提取网页正文”这个核心指令并生成基础代码框架Python作为粘合剂和主控逻辑Playwright则是一个现代、强大的浏览器自动化库它能完美模拟真实浏览器环境轻松应对动态加载的SPA页面这是传统requestsBeautifulSoup方案难以比拟的优势。整个项目做下来感觉就像教一个聪明的助手学会了一项新技能。你不需要从零开始编写所有复杂的解析规则而是通过“种子指令”让AI去思考如何实现你再基于它的“思考成果”进行微调和优化。这个过程不仅高效而且充满了探索的乐趣。下面我就把从环境搭建、核心代码编写到避坑优化的完整实战过程分享出来无论你是Python新手还是有一定经验的开发者都能跟着一步步实现。2. 环境准备与工具选型解析工欲善其事必先利其器。在开始写代码之前我们需要把“厨房”收拾好把该用的“厨具”都备齐。这个项目的环境搭建主要分三块Python环境、Playwright浏览器自动化环境以及Doubao-Seed-Evolving的接入准备。2.1 Python环境与依赖库安装首先确保你的电脑上安装了Python版本建议在3.8及以上。我个人的主力环境是Python 3.10兼容性和稳定性都很好。你可以通过在终端Windows上是CMD或PowerShellMac/Linux是Terminal输入python --version或python3 --version来检查。接下来是关键的一步创建虚拟环境。这是一个好习惯它能将本项目所需的库与系统全局的Python环境隔离开避免版本冲突。在项目根目录下执行以下命令# 创建虚拟环境环境文件夹名为 venv python -m venv venv # 激活虚拟环境 # Windows (CMD/PowerShell): venv\Scripts\activate # MacOS/Linux: source venv/bin/activate激活后你的命令行提示符前通常会显示(venv)表示你已经在这个独立的环境里了。现在安装我们需要的核心Python库。我们将使用playwright进行网页抓取markdownify用于将HTML转换为Markdownrequests用于简单的网络请求备用当然还有openai库虽然我们用的是Doubao的API但其接口与OpenAI兼容方便使用。一次性安装它们pip install playwright markdownify requests openai注意openai库的版本建议不低于0.28.0以确保API调用的稳定性。如果安装缓慢可以考虑使用国内的PyPI镜像源例如清华源pip install -i https://pypi.tuna.tsinghua.edu.cn/simple playwright markdownify requests openai。2.2 Playwright浏览器安装与配置安装完playwright的Python包后我们还需要安装它实际要控制的浏览器内核Chromium, Firefox, WebKit。Playwright很贴心地提供了一键安装命令playwright install这个命令会下载Chromium、Firefox和WebKit的二进制文件。对于我们的网页正文提取任务使用Chromium就足够了因为它对现代Web标准的支持最好也最稳定。如果你想只安装Chromium可以运行playwright install chromium。这里有一个实操心得第一次运行playwright install时由于需要从海外下载几百兆的浏览器文件可能会非常慢甚至失败。一个有效的解决办法是设置环境变量使用国内的镜像站点进行下载。在运行安装命令前先设置以Windows PowerShell为例$env:PLAYWRIGHT_DOWNLOAD_HOST https://npmmirror.com/mirrors/playwright playwright install chromium对于Mac/Linux在终端中使用export PLAYWRIGHT_DOWNLOAD_HOSThttps://npmmirror.com/mirrors/playwright。这样能极大提升安装速度。2.3 Doubao-Seed-Evolving能力接入准备Doubao-Seed-Evolving是本次项目的“大脑”。我们需要获得调用其API的权限。通常你需要访问对应AI平台的开发者页面创建一个项目并获取API Key。这个过程和获取OpenAI的API Key类似。假设你已经获得了API Key我们这里用YOUR_DOUBAO_API_KEY代替和基础的API端点Base URL。由于Doubao的API可能与OpenAI格式兼容我们可以直接使用openai这个Python库来调用只需要在初始化客户端时传入我们自己的Base URL和API Key。为了安全起见绝对不要将API Key硬编码在代码中。最佳实践是使用环境变量。你可以在终端中临时设置重启后失效# Windows setx DOUBAO_API_KEY your_actual_api_key_here # Mac/Linux export DOUBAO_API_KEYyour_actual_api_key_here或者在项目根目录创建一个名为.env的文件里面写入DOUBAO_API_KEYyour_actual_api_key_here DOUBAO_BASE_URLhttps://你获得的API基础地址然后在Python代码中使用python-dotenv库来读取。我们先安装它pip install python-dotenv。至此我们的开发环境就准备妥当了。接下来让我们进入最核心的部分如何设计这个提取工具的“大脑”和“手脚”。3. 核心逻辑设计与Seed-Evolving指令构建一个高效的网页正文提取工具其核心在于两个部分一是如何让AI理解并优化“提取”这个任务大脑二是如何精准地获取页面内容并清洗手脚。我们首先来设计“大脑”的部分。3.1 理解Seed-Evolving从“种子”到“进化”Seed-Evolving这个概念非常有趣。它不是让你直接向AI提问“怎么写代码”而是你提供一个初始的、可能不完善的“种子”任务描述。AI会基于这个种子进行多轮自我提问、思考和拓展最终生成一个更完整、更健壮的任务解决方案或代码框架。对于“网页正文提取”我们的种子指令不能太笼统。一个差的例子是“写一个提取网页正文的Python脚本”。这过于宽泛AI无法理解具体的技术栈、输出格式和复杂情况处理。我们需要构建一个高质量的“种子指令”。它应该包含核心目标清晰说明要做什么。技术栈约束指定使用Playwright和特定的HTML到Markdown转换库。关键要求提出需要解决的核心难题比如剔除导航栏、广告、评论区。输出格式明确最终需要的是Markdown文本。进化引导鼓励AI思考边缘情况和优化策略。基于以上原则我构建了如下的种子指令它将作为我们与Doubao-Seed-Evolving交互的起点任务种子开发一个Python脚本其主要功能是接受一个URL输入使用Playwright无头浏览器加载该页面然后智能地提取网页的核心正文内容并将其转换为干净、格式良好的Markdown文本。 具体要求与约束 1. 使用Playwright的同步或异步API优先考虑异步以提升性能来模拟真实浏览器访问确保能正确处理JavaScript动态渲染的内容。 2. 提取逻辑需要具备“智能性”不能简单提取整个body。需要尝试识别并排除常见的非正文元素例如网站头部导航(nav, header)、侧边栏(aside)、页脚(footer)、广告区域常见类名如.ad-container, [id*\ad\]、社交媒体分享按钮、评论区域等。 3. 核心正文内容通常位于article, main标签内或者具有特定类名如.post-content, .article-body的容器中。脚本应优先寻找这些语义化标签或常见模式。 4. 使用markdownify库或类似的逻辑将提取到的纯净HTML片段转换为Markdown格式。需注意保留标题(h1-h6)、列表(ul/ol)、代码块(precode)和链接(a)的格式。 5. 脚本应具备基本的健壮性处理网络超时、页面加载失败、元素未找到等异常情况并给出友好的错误提示。 6. 最终输出应为纯文本格式的Markdown字符串并可选地保存到.md文件中。 请基于此种子进行“进化思考”为了提升提取的准确率和鲁棒性脚本还应考虑哪些策略例如是否可以通过计算DOM元素的文本密度、链接密度来辅助判断正文区域对于没有明显语义化标签的页面如何设计备选方案请生成完整的Python代码实现并包含详细的代码注释。这个种子指令已经相当具体它告诉了AI我们要用什么工具、解决什么问题、以及希望它朝哪个方向去“思考”和“进化”。接下来我们就看看如何用代码与Doubao API交互将这个种子“喂”给AI并获取它进化后的成果。3.2 与Doubao API交互获取进化代码我们将编写一个简单的函数用于向Doubao的Seed-Evolving端点发送我们的种子指令并获取返回的代码和建议。这里假设其API与OpenAI ChatCompletion兼容。首先在项目根目录创建config.py文件用于安全地加载配置# config.py import os from dotenv import load_dotenv load_dotenv() # 加载 .env 文件中的环境变量 DOUBAO_API_KEY os.getenv(DOUBAO_API_KEY) DOUBAO_BASE_URL os.getenv(DOUBAO_BASE_URL) # 例如: https://api.doubao.com/v1 if not DOUBAO_API_KEY: raise ValueError(请在 .env 文件中设置 DOUBAO_API_KEY) if not DOUBAO_BASE_URL: raise ValueError(请在 .env 文件中设置 DOUBAO_BASE_URL)接着创建主脚本文件web_extractor.py并编写与AI交互的函数# web_extractor.py import openai from config import DOUBAO_API_KEY, DOUBAO_BASE_URL import time def get_evolved_code_from_seed(seed_instruction): 使用Doubao Seed-Evolving能力根据种子指令获取进化后的代码和方案。 # 配置OpenAI客户端以指向Doubao API client openai.OpenAI( api_keyDOUBAO_API_KEY, base_urlDOUBAO_BASE_URL, ) # 构建请求消息。根据Doubao Seed-Evolving的文档可能需要特定的消息格式。 # 这里我们假设它支持标准的ChatCompletion格式并使用一个“进化”导向的系统提示。 system_prompt 你是一个资深的全栈开发工程师擅长使用Python和Playwright进行网页自动化与内容处理。你拥有“种子进化”能力能够根据用户提供的任务种子进行多轮深度思考拓展需求边界识别潜在问题并最终生成高质量、健壮、可执行的代码解决方案。请充分发挥你的能力对用户的任务种子进行深化和优化。 try: response client.chat.completions.create( modelseed-evolving-model, # 模型名称需根据Doubao实际提供的名称修改例如 doubao-seed messages[ {role: system, content: system_prompt}, {role: user, content: seed_instruction} ], temperature0.7, # 一定的创造性以鼓励“进化” max_tokens4000, # 预留足够的token来返回详细的代码和解释 ) evolved_content response.choices[0].message.content return evolved_content except openai.APIError as e: print(f调用Doubao API时发生错误: {e}) return None except Exception as e: print(f发生未知错误: {e}) return None # 示例使用上面定义好的种子指令 if __name__ __main__: seed 这里填入3.1章节中完整的种子指令 print(正在向Doubao-Seed-Evolving发送请求这可能需要一些时间...) result get_evolved_code_from_seed(seed) if result: print( 进化后的代码与方案 ) print(result) # 可选将结果保存到文件方便后续查阅和整合 with open(evolved_code.md, w, encodingutf-8) as f: f.write(result) print(\n结果已保存至 evolved_code.md) else: print(未能获取进化结果。)运行这个脚本你就能得到一份由AI生成的、经过“进化思考”的网页正文提取代码草案。这份草案通常会包含比我们初始种子更细致的考虑比如对“文本密度算法”的简单实现、更全面的CSS选择器黑名单、以及对异常处理的更多建议。这就是Seed-Evolving的价值所在它充当了一个超级助手帮你完成了第一轮的需求细化和方案设计。注意事项AI生成的代码是很好的起点但绝非最终成品。你必须仔细阅读和理解它生成的每一部分代码因为AI可能会引入不合理的依赖、过于复杂的逻辑或者对某些库的API使用有误。我们的角色是“架构师”和“代码审查员”需要基于这份草案进行重构、调试和优化。4. 代码实现与核心功能模块拆解拿到AI生成的代码草案后我们开始进入动手实现阶段。我不会直接粘贴AI的原始输出而是结合我自己的经验将其重构、优化并拆解成几个清晰的核心模块。我们的最终目标是构建一个名为WebTextExtractor的类它封装所有功能。4.1 提取器类骨架与Playwright初始化首先我们搭建一个基础的类结构并初始化Playwright。# web_extractor.py (续) from playwright.sync_api import sync_playwright, TimeoutError as PlaywrightTimeoutError import logging from html2text import html2text # 我们改用html2text它比markdownify对中文和复杂格式支持更好 from urllib.parse import urlparse import re class WebTextExtractor: 智能网页正文提取器 def __init__(self, headlessTrue, timeout30000): 初始化提取器 :param headless: 是否使用无头模式不显示浏览器界面 :param timeout: 页面加载超时时间毫秒 self.headless headless self.timeout timeout self.playwright None self.browser None self.context None self.page None # 配置日志方便调试 logging.basicConfig(levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s) self.logger logging.getLogger(__name__) def __enter__(self): 支持with语句自动管理资源 self.start() return self def __exit__(self, exc_type, exc_val, exc_tb): 退出with语句时自动关闭资源 self.stop() def start(self): 启动Playwright浏览器实例 self.logger.info(正在启动Playwright浏览器...) self.playwright sync_playwright().start() # 使用Chromium渲染一致性更好 self.browser self.playwright.chromium.launch(headlessself.headless, args[--disable-blink-featuresAutomationControlled]) # 尝试绕过一些反自动化检测 self.context self.browser.new_context( viewport{width: 1920, height: 1080}, user_agentMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 ) self.page self.context.new_page() self.logger.info(浏览器启动成功。) def stop(self): 停止Playwright浏览器实例释放资源 self.logger.info(正在关闭浏览器...) if self.browser: self.browser.close() if self.playwright: self.playwright.stop() self.logger.info(资源已释放。)这里有几个关键点使用同步API为了代码逻辑更直观我们先使用同步API (sync_playwright)。对于需要高并发的场景可以后续改为异步API (async_playwright)。绕过基础检测args[--disable-blink-featuresAutomationControlled]和设置一个常见的user_agent可以帮助我们避免被一些简单的反爬机制识别为自动化脚本。但对于高级反爬如Cloudflare这还不够可能需要更多策略。资源管理实现了__enter__和__exit__方法这样我们可以使用with WebTextExtractor() as extractor:的语法确保浏览器一定会被正确关闭避免资源泄漏。4.2 智能正文定位与清洗策略这是整个工具最核心、最考验技巧的部分。我们的目标是从完整的页面DOM树中精准地找到包含主要文章内容的那个“核心区域”。我综合了AI的建议和自己的经验采用了一种“优先级匹配 备选算法”的混合策略。def _find_main_content_element(self): 智能定位网页正文的核心容器元素。 返回Playwright的ElementHandle对象如果未找到则返回None。 策略优先级 1. 语义化标签article, main 2. 常见内容容器类名/ID.post-content, .article-body, #content等 3. 基于文本密度和链接密度的启发式算法备选 self.logger.info(正在智能定位正文区域...) # 策略1: 语义化标签优先 semantic_selectors [ article, main, [rolemain], ] for selector in semantic_selectors: element self.page.query_selector(selector) if element: self.logger.info(f通过语义化选择器 {selector} 找到候选元素。) return element # 策略2: 常见内容类名/ID模式 common_patterns [ .post-content, .article-content, .entry-content, .content, .main-content, #content, #main, [class*content], [id*content], [class*post], [class*article], .blog-post, .story-body ] for pattern in common_patterns: # 注意使用*通配符的选择器可能返回多个取第一个 element self.page.query_selector(pattern) if element: self.logger.info(f通过常见模式选择器 {pattern} 找到候选元素。) return element # 策略3: 启发式算法 - 文本密度法 (作为备选) self.logger.info(未通过常规选择器找到尝试使用文本密度算法...) # 获取所有潜在的块级元素候选 candidate_elements self.page.query_selector_all(body p, body div, body section, body div div) best_element None max_score -1 for elem in candidate_elements: try: # 获取元素的内部文本和HTML text elem.inner_text() html elem.inner_html() if not text or len(text.strip()) 100: # 文本太短跳过 continue # 计算文本长度 text_length len(text) # 估算HTML标签长度非常粗略 tag_length len(html) - text_length # 计算文本密度文本长度 / (文本长度 标签长度) # 同时惩罚链接过多的元素链接密度高可能是导航或推荐 link_count len(elem.query_selector_all(a)) if elem.query_selector_all(a) else 0 link_density link_count * 20 / text_length if text_length 0 else 1 # 链接惩罚因子 density text_length / (text_length tag_length 1) # 加1防止除零 score density / (1 link_density) # 最终得分文本密度高、链接密度低者胜出 # 额外奖励可能包含文章标题的元素 if elem.query_selector(h1, h2): score * 1.2 if score max_score: max_score score best_element elem except Exception as e: # 某些元素可能已脱离DOM忽略 continue if best_element: self.logger.info(f通过文本密度算法找到候选元素得分: {max_score:.2f}) return best_element self.logger.warning(未能定位到明确的正文容器将回退到整个body。) return self.page.query_selector(body)找到核心容器后我们还需要对它进行“清洗”剔除容器内部可能残留的无关元素比如文章内部的广告、分享按钮、推荐阅读等。def _clean_content_element(self, element): 清理正文元素内部的无关节点。 :param element: Playwright的ElementHandle对象 :return: 清理后的HTML字符串 if not element: return # 创建一个用于清理的CSS选择器黑名单 # 这些选择器匹配的元素将被直接从DOM中移除 blacklist_selectors [ nav, header, footer, aside, # 结构性无关元素 .ad, .advertisement, [class*ad-], [id*ad-], # 广告 .share, .social, .share-buttons, # 分享按钮 .comment, .comments, #comments, # 评论区域 .sidebar, .widget, .related-posts, .recommendation, # 侧边栏和相关推荐 script, style, iframe, form, button, # 脚本、样式、iframe和表单按钮 .navigation, .pagination, .breadcrumb, # 导航和翻页 .modal, .popup, .newsletter, # 弹窗和订阅框 ] # 在元素的副本上进行操作避免影响原始页面 # Playwright 可以通过 evaluate 在浏览器环境中执行JS来操作DOM cleaned_html element.evaluate( (element, selectorsToRemove) { // 克隆元素避免修改原DOM const clone element.cloneNode(true); selectorsToRemove.forEach(selector { clone.querySelectorAll(selector).forEach(el el.remove()); }); // 额外清理移除空段落和仅包含空白符的元素 clone.querySelectorAll(p, div, span).forEach(el { if (el.innerText.trim() el.children.length 0) { el.remove(); } }); return clone.innerHTML; } , blacklist_selectors) return cleaned_html4.3 HTML到Markdown的转换与后处理清洗后得到的是纯净的HTML片段我们需要将其转换为易读易用的Markdown格式。这里我选择了html2text库因为它对中文排版、复杂列表和代码块的处理比markdownify更成熟。def _html_to_markdown(self, html_content): 将HTML内容转换为Markdown格式。 使用html2text进行转换并做一些后处理。 if not html_content: return # 配置html2text转换器 h html2text.HTML2Text() h.ignore_links False # 保留链接 h.ignore_images False # 保留图片链接形式 h.ignore_tables False # 保留表格 h.bypass_tables False h.body_width 0 # 不自动换行 h.protect_links True # 保护链接不被破坏 h.mark_code True # 用反引号标记代码 # 针对中文优化避免在中文间插入不必要的空格 h.unicode_snob True markdown_text h.handle(html_content) # 后处理清理转换后可能产生的多余空行和空格 lines markdown_text.split(\n) cleaned_lines [] for line in lines: stripped line.rstrip() # 去除行尾空格 if stripped: # 只保留非空行 cleaned_lines.append(stripped) # 如果上一行也是空行则跳过当前空行避免连续多个空行 elif cleaned_lines and cleaned_lines[-1]: cleaned_lines.append() # 确保代码块被正确包裹html2text有时会漏掉 processed_text \n.join(cleaned_lines) # 一个简单的修复如果一行以4个空格或一个制表符开始且包含非空格字符可能是未正确转换的代码行 # 这里我们采用更稳健的方法寻找连续的“以四个空格开头”的行将其转换为代码块 # 由于篇幅此处省略复杂的修复逻辑。实践中html2text对标准precode处理良好。 # 如果遇到问题可以考虑先用pygments等库高亮代码再嵌入Markdown。 return processed_text.strip()4.4 整合与主流程函数现在我们把所有模块串联起来形成完整的提取流程。def extract_from_url(self, url, save_to_fileNone): 从给定URL提取正文并转换为Markdown。 :param url: 目标网页URL :param save_to_file: 可选保存Markdown的文件路径 :return: 提取到的Markdown文本 if not self.page: raise RuntimeError(请先调用 start() 方法启动浏览器或使用 with 语句。) self.logger.info(f正在处理URL: {url}) try: # 1. 导航到页面 self.page.goto(url, timeoutself.timeout, wait_untilnetworkidle) # 等待网络空闲 # 可选滚动页面以触发懒加载 self.page.evaluate(window.scrollTo(0, document.body.scrollHeight)) self.page.wait_for_timeout(1000) # 等待滚动后可能的内容加载 # 2. 智能定位正文元素 main_element self._find_main_content_element() if not main_element: self.logger.error(无法定位页面主体内容。) return # 3. 清理无关内容 cleaned_html self._clean_content_element(main_element) # 4. 转换为Markdown markdown_output self._html_to_markdown(cleaned_html) # 5. 可选保存到文件 if save_to_file: try: with open(save_to_file, w, encodingutf-8) as f: f.write(f# 提取自: {url}\n\n) f.write(markdown_output) self.logger.info(fMarkdown内容已保存至: {save_to_file}) except IOError as e: self.logger.error(f保存文件失败: {e}) self.logger.info(正文提取完成) return markdown_output except PlaywrightTimeoutError: self.logger.error(f页面加载超时: {url}) return f错误加载页面超时 ({url}) except Exception as e: self.logger.error(f提取过程中发生未知错误: {e}) return f错误处理页面时发生异常 - {str(e)}最后我们提供一个便捷的使用示例# 使用示例 if __name__ __main__: # 示例URL - 一个技术博客文章 test_url https://example.com/some-tech-article # 方式1使用with语句自动管理资源推荐 with WebTextExtractor(headlessTrue) as extractor: md_content extractor.extract_from_url(test_url, save_to_fileoutput.md) if md_content and not md_content.startswith(错误): print(提取成功前500字符预览) print(md_content[:500]) else: print(提取失败。) # 方式2手动管理生命周期 # extractor WebTextExtractor(headlessTrue) # try: # extractor.start() # md_content extractor.extract_from_url(test_url) # print(md_content) # finally: # extractor.stop()5. 实战测试、常见问题与优化技巧工具写好了是骡子是马得拉出来遛遛。我找了几类典型的网站进行测试技术博客如某框架官方文档、新闻门户、社区论坛如某乎以及使用现代前端框架如React/Vue构建的单页面应用。5.1 不同场景下的测试结果与调优标准技术博客/文档网站这类网站结构清晰通常有article或明确的.post-content类提取效果最好准确率可达95%以上。我们的“语义化标签优先”策略在这里大放异彩。新闻门户网站页面元素复杂广告和侧栏多。我们的“黑名单选择器清洗”策略至关重要。测试中发现有些新闻正文被包裹在多个嵌套的div中没有明显特征。这时“文本密度算法”作为备选方案发挥了作用但偶尔会把“相关阅读”板块也划进来。优化技巧可以动态调整黑名单针对特定域名添加特定的选择器。例如对于某新闻站可以额外添加.related-news, .hot-comment到黑名单。社区论坛/问答页面最大的挑战是区分“问题/文章主体”和“评论区”。我们的策略是优先找article或[role\main\]并在清洗时强力移除.comment相关的元素。对于某乎发现其答案区域类名经常变化但结构相对稳定。一个更鲁棒的方法是在定位到疑似主体后计算其下所有直接子div的文本密度取最长的连续文本块作为正文。这需要更精细的算法但Seed-Evolving生成的初始代码草案里往往会有这类思路的雏形我们可以在此基础上深化。单页面应用 (SPA)这是Playwright的强项。由于它能完整执行JavaScript等页面动态加载完毕后再抓取因此对于Vue、React构建的博客如基于VitePress、Next.js的站点效果非常好。关键点page.goto(url, wait_untilnetworkidle)中的networkidle参数很重要它等待页面网络活动停止确保动态内容加载完成。对于某些加载特别慢的SPA可能需要结合page.wait_for_selector(article)来显式等待特定内容出现。5.2 常见问题排查速查表在实际操作中你可能会遇到以下问题。这里提供一个快速排查指南问题现象可能原因解决方案提取内容为空或只有少量文本1. 页面是SPA内容未加载。2. 定位策略全部失败回退到body但被黑名单全删了。3. 网络超时。1. 增加timeout或使用page.wait_for_selector等待特定元素。2. 临时注释掉清洗黑名单查看原始body内容调整定位策略或黑名单。3. 检查网络或捕获PlaywrightTimeoutError异常。提取结果包含大量导航或广告黑名单选择器不够全面或正文定位不准确。1. 打开浏览器开发者工具手动检查残留无关元素的CSS选择器将其加入黑名单。2. 优化_find_main_content_element函数提高文本密度算法的权重或调整评分规则。代码块格式丢失或错乱html2text对非标准的代码块处理不佳。1. 在清洗前优先使用page.locator(pre code).all()等方式将代码块内容先提取并替换为占位符转换后再还原。2. 考虑使用pygments库进行代码高亮然后生成带语言标识的python格式。被网站识别为爬虫并屏蔽Playwright的自动化特征被检测到。1. 启动浏览器时添加更多反检测参数args[--disable-blink-featuresAutomationControlled, --disable-dev-shm-usage]。2. 使用context.add_init_script注入脚本覆盖navigator.webdriver等属性。3. 模拟更真实的人类行为如随机延迟、鼠标移动Playwright支持。4. 考虑使用代理IP轮换对于大规模抓取。内存占用过高或浏览器崩溃同时处理页面过多或页面本身资源过大。1. 确保每个WebTextExtractor实例在使用后调用stop()或使用with语句。2. 对于批量处理考虑每个URL处理完后关闭当前page和context只保留browser。3. 设置headlessTrue可以减少一些资源开销。5.3 高级优化与扩展思路这个基础工具已经能解决80%的需求但如果你想让它更强大这里有一些进阶思路可配置化将黑名单选择器、语义化标签优先级、文本密度算法参数等提取到配置文件如config.yaml或类初始化参数中使其更容易针对不同网站定制。机器学习辅助对于极端复杂的页面可以尝试用简单的机器学习模型如基于特征工程的传统模型来判定正文区域。特征可以包括标签名、类名、文本长度、标点符号密度、链接密度、图片数量等。虽然Seed-Evolving可能提到这个概念但实现起来需要数据集和训练更适合作为一个长期研究方向。增量更新与自学习设计一个反馈机制。当用户手动纠正了一次提取结果比如删除了误提取的部分工具可以记录这个URL和纠正操作并尝试推导出新的规则或选择器用于未来对同域名页面的提取。这能让工具越用越“聪明”。集成到工作流将这个工具封装成命令行工具CLI或FastAPI服务方便与其他工具集成。例如你可以做一个浏览器插件点击一下就把当前标签页文章保存为Markdown或者做一个定时任务自动抓取你关注的几个博客的最新文章。整个项目从构思到实现最深的体会是“Doubao-Seed-Evolving 专家经验”的模式极大地提升了开发效率的上限。AI负责提供广度的思路和基础代码而开发者则负责深度的逻辑审查、细节实现和性能优化。两者结合让构建一个如此复杂的工具变得不再那么遥不可及。最后别忘了工具是死的人是活的。面对千变万化的网页没有一劳永逸的提取规则保持对页面结构的观察力和调整工具的灵活性才是长期可用的关键。
返回列表