【BeautifulSoup爬虫技巧】:get_text分隔符的5种高级用法,99%的人都忽略了

第一章:get_text分隔符的核心作用与基础认知

在网页内容提取过程中, get_text 方法是解析 HTML 元素文本内容的关键工具。其核心功能在于将嵌套的 DOM 节点转换为纯文本输出,并通过分隔符控制不同子元素之间的文本连接方式。若未正确理解分隔符的行为机制,可能导致信息丢失或文本粘连。

分隔符的基本行为

当调用 get_text 时,传入的 separator 参数决定了相邻文本节点间的连接字符。默认情况下,不使用分隔符,所有文本直接拼接。

from bs4 import BeautifulSoup

html = """

  
  

第一段

第二段 链接文本
""" soup = BeautifulSoup(html, 'html.parser') text_with_separator = soup.get_text(separator=' | ') print(text_with_separator) # 输出:第一段 | 第二段 | 链接文本
上述代码中, separator=' | ' 明确指定各标签文本间以竖线分隔,提升可读性。

分隔符的实际应用场景

  • 日志采集时保持字段边界清晰
  • 爬虫数据清洗阶段区分结构化文本块
  • 构建摘要信息时避免语义混淆
参数配置输出效果
separator=""第一段第二段链接文本
separator=" "第一段 第二段 链接文本
separator="\n"第一段\n第二段\n链接文本
合理设置分隔符不仅能增强文本可解析性,还为后续 NLP 处理提供良好输入基础。

第二章:分隔符在文本提取中的五种典型应用场景

2.1 理论解析:分隔符如何影响HTML文本的结构化输出

在HTML中,分隔符(如空格、换行、制表符)虽不可见,却深刻影响文本的渲染与结构化输出。浏览器依据这些分隔符决定文本节点的边界和布局流。
空白字符的处理机制
HTML规范将多个连续空白字符合并为单个空格,除非处于
等保留格式的标签内。这直接影响文本内容的视觉排版。

代码示例:分隔符在标签间的实际作用
<p>Hello<span>World</span>!</p>
上述代码中,无显式分隔符导致“HelloWorld!”连写。若需自然间隔,必须显式插入空格:
<p>Hello <span>World</span>!</p>
此处空格作为分隔符,使文本节点正确断开,确保语义清晰与可读性。
常见分隔符类型对照
类型HTML表示作用
空格&nbsp; 或 ' '分隔单词
换行<br>强制换行
段落<p>块级分隔

2.2 实战演示:从新闻页面提取段落时使用换行符分隔

在网页内容抓取中,新闻文章通常由多个段落组成,保留原始结构对可读性至关重要。本节演示如何提取段落并以换行符分隔。
HTML 结构分析
典型新闻页面的段落由 <p> 标签包裹,位于内容容器内,如:
<div class="article-content">
  <p>第一段内容。</p>
  <p>第二段内容。</p>
  <p>第三段内容。</p>
</div>
通过选择器定位所有 p 元素,可依次获取文本。
使用 Python 提取并拼接
利用 BeautifulSoup 解析 HTML 并用换行符连接段落:
from bs4 import BeautifulSoup

html = """上面的HTML示例"""
soup = BeautifulSoup(html, 'html.parser')
paragraphs = soup.select('.article-content p')
text = '\n'.join(p.get_text(strip=True) for p in paragraphs)
print(text)
soup.select() 返回匹配元素列表,get_text(strip=True) 清理空白,'\n'.join() 确保段落间换行。
输出效果对比
处理方式输出结果
直接拼接第一段内容。第二段内容。第三段内容。
换行符分隔第一段内容。\n第二段内容。\n第三段内容。

2.3 理论进阶:区分strip与separator的协同处理机制

在字符串处理中,stripseparator 虽常共现,但职责分明。前者用于清除首尾特定字符,后者则负责分割字符串为子片段。
功能职责划分
  • strip:移除前导和尾随字符(如空格、换行)
  • separator:定义切分边界,生成子串数组
协同处理示例
input := "  apple, banana, cherry  "
trimmed := strings.TrimSpace(input)           // strip 阶段
parts := strings.Split(trimmed, ", ")         // separator 阶段
// 输出: ["apple" "banana" "cherry"]
上述代码先通过 TrimSpace 清理外围空白,再以 ", " 作为分隔符拆分。若省略 strip 步骤,结果将包含首尾空元素,影响数据纯净度。二者顺序不可逆,体现预处理与结构化解析的层次关系。

2.4 实战优化:电商商品详情页中多节点文本的空格规范化

在电商商品详情页中,多来源文本(如标题、描述、参数)常因换行或拼接引入多余空格,影响展示效果与SEO。需对DOM节点内容进行统一清洗。
常见空格问题场景
  • 用户输入时粘贴带格式文本
  • 后端模板渲染残留空白字符
  • 前端组件动态拼接产生间隙
JavaScript规范化处理

function normalizeWhitespace(text) {
  return text
    .replace(/\s+/g, ' ')  // 多空格合并为单空格
    .trim();               // 去除首尾空白
}
该函数通过正则匹配所有空白字符(包括全角、制表符等),统一替换为标准空格,并去除首尾冗余字符,确保文本整洁。
批量处理示例
原始文本规范化结果
" 商品 质量 ""商品 质量"
"高端\t\n产品""高端 产品"

2.5 综合应用:批量爬取问答页面并用自定义分隔符构建原始语料

在自然语言处理任务中,高质量的原始语料是模型训练的基础。通过自动化手段从结构化网页中提取问答对,能有效提升数据采集效率。
爬取策略设计
采用异步HTTP请求并发抓取多个问答页面,结合XPath解析DOM节点,精准定位问题与答案文本。为避免服务器压力,设置合理请求间隔。
数据清洗与格式化
提取后的文本需去除HTML标签、冗余空格及无关广告内容。使用自定义分隔符 ||Q||||A|| 标识问答边界,便于后续分割。
import asyncio
import aiohttp
from lxml import html

async def fetch_qa(session, url):
    async with session.get(url) as response:
        content = await response.text()
        tree = html.fromstring(content)
        q = tree.xpath('//div[@class="question"]/text()')[0].strip()
        a = tree.xpath('//div[@class="answer"]/text()')[0].strip()
        return f"||Q||{q}||A||{a}"

# 并发爬取10个页面
urls = [f"https://example.com/qa/{i}" for i in range(1, 11)]
async def main():
    async with aiohttp.ClientSession() as session:
        tasks = [fetch_qa(session, url) for url in urls]
        results = await asyncio.gather(*tasks)
    with open("corpus.txt", "w", encoding="utf-8") as f:
        f.write("\n".join(results))
上述代码利用 aiohttp 实现异步请求,lxml 解析HTML结构,最终将批量获取的问答对以统一格式写入本地文件,形成可用于训练的原始语料库。

第三章:深度控制文本清洗的三种高级分隔策略

3.1 理论剖析:嵌套标签间空白字符的智能合并原理

在HTML渲染过程中,嵌套标签之间的换行与空格常被解析为文本节点,影响布局精度。现代浏览器通过“空白字符合并策略”优化这一行为。
空白字符的识别与归并
仅包含空白符(空格、换行、制表符)的文本节点,在父元素无 white-space: pre 设置时,会被压缩为单个空格。
<div>
  <span>Hello</span>
  <span>World</span>
</div>
上述代码中,两 <span> 间的换行被视为空白字符节点,最终渲染等效于:Hello World
智能合并规则
  • 连续空白符合并为一个空格
  • 行首行尾空白符被忽略
  • 块级元素间的空白节点不渲染
该机制确保结构语义清晰的同时,提升排版鲁棒性。

3.2 实战案例:论坛帖子中用户评论的精准分割与去噪

在处理论坛数据时,用户评论常夹杂广告、乱码和重复内容。为实现精准分割与去噪,首先采用基于标点和换行的规则预分割。
清洗流程设计
  • 去除HTML标签与特殊字符
  • 识别并过滤广告关键词(如“加微信”)
  • 利用正则表达式匹配用户@行为
代码实现与逻辑说明

import re

def clean_comment(text):
    # 去除HTML标签
    text = re.sub(r'<[^>]+>', '', text)
    # 过滤广告信息
    text = re.sub(r'加[微V]?:?\s*\w{5,}', '[AD]', text)
    # 分割有效语句
    sentences = re.split(r'[。!!\n]+', text)
    return [s.strip() for s in sentences if len(s.strip()) > 5]
该函数通过正则逐层清洗,先剥离结构标签,再屏蔽典型广告模式,最后按句号或换行切分,确保输出语义完整且长度合理的句子片段。

3.3 边界挑战:处理script与style标签残留文本的隔离技巧

在解析HTML内容时,<script><style>标签内部的文本常被误识别为可提取内容,导致数据污染。
问题本质
这类标签内的文本属于执行或样式逻辑,不应参与正文提取。若未有效隔离,将混入语义文本流。
隔离策略实现
采用预清理方式,在DOM遍历前移除或标记特殊标签内容:

function isolateScriptAndStyle(node) {
  const scripts = node.querySelectorAll('script, style');
  scripts.forEach(el => {
    el.replaceWith(Object.assign(document.createComment('cleaned'), {
      dataset: { type: el.tagName }
    }));
  });
}
该函数遍历节点下所有scriptstyle元素,将其替换为带类型标记的注释节点,既保留结构完整性,又阻断文本提取。
处理效果对比
处理阶段残留文本存在正文纯净度
未处理
已隔离

第四章:结合实际项目的四类复杂场景应对方案

4.1 表格数据提取:以制表符分隔单元格内容实现类CSV输出

在处理日志或原始文本数据时,常需将表格化内容转换为结构化格式。使用制表符(Tab)作为分隔符,可有效保留字段边界,避免逗号干扰。
数据解析流程
逐行读取输入,通过字符串分割提取字段。每行按 `\t` 拆分为数组,再以 `\t` 重新拼接,生成类CSV输出。
package main

import (
    "bufio"
    "os"
    "strings"
)

func main() {
    file, _ := os.Open("data.txt")
    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
        fields := strings.Split(scanner.Text(), "\t")
        line := strings.Join(fields, "\t")
        println(line) // 输出类CSV行
    }
}
上述代码中,strings.Split 将每行按制表符拆分,strings.Join 重建为标准化输出。适用于ETL预处理阶段的数据清洗任务。

4.2 列表信息聚合:利用分号分隔无序列表项提升可读性

在展示多个相关但独立的信息项时,使用分号分隔的无序列表能有效提升内容的可读性与结构清晰度。
语义化列表结构设计
  • 前端框架配置项:路由模式;状态管理;API拦截器
  • 微服务依赖组件:认证中心;日志网关;消息队列
  • 数据库优化策略:索引重建;查询缓存;连接池调优
上述写法通过分号实现逻辑分组,避免传统列表的视觉割裂感,同时保留语义完整性。
代码示例:HTML渲染逻辑
<div class="tag-list">
  {{ range .Items }}
    <span class="tag">{{ . }}</span>;
  {{ end }}
</div>
该模板遍历数据项并生成带分号的标签序列,末尾多余分号可通过CSS伪元素或条件判断去除,提升前端展示美观度。

4.3 多语言页面:通过特殊标记分隔符保留语言边界信息

在构建多语言网页时,保持不同语言内容的语义边界清晰至关重要。使用特殊标记分隔符可有效隔离语言片段,防止解析混淆。
分隔符设计原则
  • 唯一性:确保分隔符不会出现在正常文本中
  • 可读性:便于开发者识别和调试
  • 兼容性:不干扰HTML渲染与DOM结构
实现示例
<span data-lang="en" class="lang-segment">Hello</span>
<span data-lang="zh" class="lang-segment">你好</span>
上述代码通过data-lang属性标注语言类型,结合CSS类lang-segment实现样式隔离。浏览器可根据属性值应用不同的字体、方向或语音合成策略,确保各语言正确呈现。
处理流程
输入文本 → 插入语言标记 → DOM渲染 → 按语言属性差异化处理

4.4 动态渲染内容:配合预处理过滤器构建稳定文本流

在动态内容渲染场景中,原始数据常携带不一致格式或潜在风险字符。为保障前端输出的稳定性与安全性,需引入预处理过滤器对文本流进行标准化处理。
过滤器工作流程
  • 接收原始输入数据
  • 执行转义与清洗逻辑
  • 注入上下文元信息
  • 输出结构化文本流
代码实现示例
func sanitizeContent(input string) string {
    // 使用内建模板引擎转义HTML特殊字符
    escaped := template.HTMLEscapeString(input)
    // 过滤危险关键词(如script标签)
    cleaned := regexp.MustCompile(`(?i)<script.*?>.*?</script>`).ReplaceAllString(escaped, "")
    return cleaned
}
上述函数通过双重防护机制确保输出安全:首先对HTML实体编码,防止XSS攻击;再利用正则表达式移除潜在恶意标签,保障渲染环境的纯净性。
性能优化建议
可将高频过滤规则缓存至内存字典,减少重复编译开销,提升整体处理吞吐量。

第五章:分隔符技巧的总结与未来爬虫文本处理趋势

分隔符策略的核心价值
在爬虫数据清洗中,合理使用分隔符能显著提升文本结构化效率。例如,面对日志文件中以空格和等号混合分隔的数据:

# 示例:解析含混合分隔符的日志行
log_line = 'user_id=1001 action=view timestamp="2023-04-05 10:30"'
import shlex
parsed = dict(token.split('=') for token in shlex.split(log_line))
print(parsed)  # {'user_id': '1001', 'action': 'view', 'timestamp': '2023-04-05 10:30'}
多分隔符场景的实战方案
当目标文本使用多种符号(如逗号、分号、竖线)作为字段分隔时,正则表达式成为首选工具:
  • 使用 re.split(r'[;,|]+') 处理混合分隔符
  • 预处理阶段统一替换为标准分隔符,便于后续解析
  • 结合 pandas.read_csv(sep=None, engine='python') 自动探测分隔符
未来趋势:语义感知的文本分割
随着NLP技术发展,基于上下文理解的分割方法逐渐兴起。以下对比传统与新兴方法:
方法类型准确率适用场景
正则分隔符匹配78%结构化日志、CSV
BERT+CRF语义分割94%非结构化网页文本
[原始文本] 价格:¥599 | 库存:有货 | 发货地:杭州 ↓ 使用语义标注模型 [ENTITY:price]¥599[/ENTITY] [STATUS:in_stock]有货[/STATUS]
import requests from bs4 import BeautifulSoup import time import json import re def page_request(url, ua): try: response = requests.get(url, headers=ua, timeout=10) response.encoding = 'utf-8' html = response.text return html except Exception as e: print(f"请求页面失败: {e}") return None def page_parse(html): if not html: return [[], []] soup = BeautifulSoup(html, 'html.parser') sentence_list = [] href_list = [] # 查找名句和对应的诗词链接 mingju_divs = soup.find_all('div', class_='cont') for div in mingju_divs: mingju_text = div.get_text(strip=True) if len(mingju_text) > 5 and len(mingju_text) < 200: # 查找诗词链接(包含shiwenv的链接) link = div.find('a', href=re.compile(r'shiwenv')) if link: href = link.get('href') link_text = link.get_text(strip=True) if href: full_url = "https://www.gushiwen.cn" + href if href.startswith('/') else href if not full_url.startswith('http'): full_url = "https://www.gushiwen.cn/" + full_url sentence_list.append(mingju_text) href_list.append(full_url) print(f"找到名句: '{mingju_text}' -> 诗词: {link_text}") print(f"共找到 {len(sentence_list)} 个名句") return [href_list, sentence_list] def save_poem_with_details(poem_data): """保存名句及对应的译文、注释、赏析到文件""" with open('poem.txt', 'a', encoding='utf-8') as txt_file: txt_file.write(f"【名句】: {poem_data['mingju']}\n") if poem_data.get('title'): txt_file.write(f"【诗词标题】: {poem_data['title']}\n") if poem_data.get('author'): txt_file.write(f"【作者信息】: {poem_data['author']}\n") if poem_data.get('content'): txt_file.write(f"【诗词内容】:\n{poem_data['content']}\n") if poem_data.get('translation'): txt_file.write(f"【译文】:\n{poem_data['translation']}\n") if poem_data.get('notes'): txt_file.write(f"【注释】:\n{poem_data['notes']}\n") if poem_data.get('appreciation'): txt_file.write(f"【赏析】:\n{poem_data['appreciation']}\n") txt_file.write("=" * 80 + "\n\n") def extract_translation(soup): """提取译文""" # 方法1: 查找包含"译文"的strong标签 translation_strong = soup.find('strong', string='译文') if translation_strong: # 获取父级p标签的文本,但排除strong标签本身的文本 parent_p = translation_strong.find_parent('p') if parent_p: # 复制父元素,移除strong标签,然后获取文本 parent_copy = parent_p.copy() strong_tag = parent_copy.find('strong') if strong_tag: strong_tag.decompose() return parent_copy.get_text(strip=True) # 方法2: 查找包含"译文"的contyishang div translation_div = soup.find('div', class_='contyishang') if translation_div and '译文' in translation_div.get_text(): # 在div中查找包含"译文"的p标签 for p in translation_div.find_all('p'): if '译文' in p.get_text(): text = p.get_text().replace('译文', '').strip() if text: return text return None def extract_notes(soup): """提取注释""" # 方法1: 查找包含"注释"的strong标签 notes_strong = soup.find('strong', string='注释') if notes_strong: parent_p = notes_strong.find_parent('p') if parent_p: parent_copy = parent_p.copy() strong_tag = parent_copy.find('strong') if strong_tag: strong_tag.decompose() return parent_copy.get_text(strip=True) # 方法2: 查找包含"注释"的contyishang div notes_div = soup.find('div', class_='contyishang') if notes_div and '注释' in notes_div.get_text(): for p in notes_div.find_all('p'): if '注释' in p.get_text(): text = p.get_text().replace('注释', '').strip() if text: return text return None def extract_appreciation(soup): """提取赏析""" # 查找包含"赏析"的contyishang div appreciation_divs = soup.find_all('div', class_='contyishang') for div in appreciation_divs: h2 = div.find('h2') if h2 and '赏析' in h2.get_text(): # 获取所有p标签的内容 p_tags = div.find_all('p') appreciation_text = [] for p in p_tags: text = p.get_text(strip=True) # 跳过包含"展开阅读全文"的p标签 if text and '展开阅读全文' not in text and '赏析' not in text: appreciation_text.append(text) if appreciation_text: return '\n'.join(appreciation_text) return None def get_poem_details(url, ua): """获取诗词详情页的译文、注释和赏析""" try: print(f"正在获取诗词详情: {url}") html = page_request(url, ua) if not html: return None soup = BeautifulSoup(html, 'html.parser') details = {} # 获取诗词标题 title_elem = soup.find('h1') if title_elem: details['title'] = title_elem.get_text(strip=True) print(f"找到标题: {details['title']}") # 获取作者信息 source_elem = soup.find('p', class_='source') if source_elem: details['author'] = source_elem.get_text(strip=True) print(f"找到作者信息: {details['author']}") # 获取诗词内容 content_elem = soup.find('div', class_='contson') if content_elem: details['content'] = content_elem.get_text(strip=True) print(f"找到诗词内容") # 获取译文 translation = extract_translation(soup) details['translation'] = translation if translation: print(f"找到译文") else: print("未找到译文") # 获取注释 notes = extract_notes(soup) details['notes'] = notes if notes: print(f"找到注释") else: print("未找到注释") # 获取赏析 appreciation = extract_appreciation(soup) details['appreciation'] = appreciation if appreciation: print(f"找到赏析") else: print("未找到赏析") # 只有当至少有一种解析内容时才返回 if translation or notes or appreciation: return details else: print("该诗词没有译文、注释或赏析,跳过") return None except Exception as e: print(f"获取诗词详情错误: {e}") return None if __name__ == '__main__': print("******开始爬古诗文网站******") ua = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' } poemCount = 0 # 清空或创建文件 with open('poem.txt', 'w', encoding='utf-8') as f: f.write("古诗文名句及赏析大全\n") f.write("=" * 80 + "\n\n") for i in range(1, 3): # 爬取前2页 url = f'https://www.gushiwen.cn/mingjus/default.aspx?page={i}' print(f"正在爬取: {url}") html = page_request(url, ua) info_list = page_parse(html) if not info_list[0]: print(f"第{i}页解析失败,跳过") continue print(f'开始解析第{i}页子页面') # 对每个名句对应的诗词链接,获取详情 href_list = info_list[0] sentence_list = info_list[1] for j, (href, sentence) in enumerate(zip(href_list, sentence_list)): if j >= 3: # 每页只处理前3个,避免请求过多 break print(f"处理第{j + 1}个名句: {sentence[:30]}...") if href: details = get_poem_details(href, ua) if details: poem_data = { 'mingju': sentence, 'title': details.get('title', ''), 'author': details.get('author', ''), 'content': details.get('content', ''), 'translation': details.get('translation', ''), 'notes': details.get('notes', ''), 'appreciation': details.get('appreciation', '') } save_poem_with_details(poem_data) poemCount += 1 print(f"成功保存第{poemCount}个名句详情") else: print("未获取到详情,跳过") else: print("无有效链接,跳过") time.sleep(1) # 添加延迟,避免请求过快 print(f'第{i}页爬取完成') time.sleep(2) # 页面间延迟 print('******爬取完成******') print(f'共爬取{poemCount}个古诗词名句及详情') 帮我分步骤生成详细的文字描述
最新发布
10-21
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值