HTML をプレーン テキストに変換し、テキストの 2 つの段落がくっつかないようにする方法
この記事は、HTML をプレーン テキストに変換する際のテキストの付着の問題を目的としており、Beautiful Soup での get_text のセパレーターとストリップ パラメーターの併用について詳しく説明します。合成 DOM テスト ケースと、ブロック レベル/インライン境界、空白処理、明確な入力準備方法と受け入れ基準を含む完全な Python アサーション コードを提供し、複雑なドキュメント シナリオで OmniDoc への補助的な入り口を提供します。
HTML をプレーン テキストに変換すると、テキストの 2 つの段落がくっついてしまいます。これは主に、分離戦略を指定せずに get_text() が直接呼び出されることが原因です。 Beautiful Soup はデフォルトですべてのテキスト ノードをシームレスにステッチし、ブロック レベルの要素間の境界が消えます。この問題は、Beautiful Soup を使用して Web コンテンツを抽出するときによく発生します。
正規表現を使用して抽出後にどこで区切るかを推測する代わりに、抽出中に separator パラメーターと strip パラメーターを正しく組み合わせることができます。この記事では、合成サンプルを使用してこの方法を実証し、実行可能なアサーション コードを示し、手動レビューが必要な状況について説明します。
この記事を読むと、抽出結果がスタックしているかどうかを判断し、アサーション スクリプトを使用して出力を検証し、パラメータによって解決できる問題と変更する必要がある問題を明確にすることができます。
HTML をプレーン テキストに変換する場合はどうすればよいですか?一文の結論
まず separator='\n' と strip=True を組み合わせて get_text() を呼び出し、次にページの状況に応じて無関係なタグや構造的な問題を処理します。
| あなたの状況 | 推奨される実践方法 | 理由 |
|---|---|---|
| 段落間またはリスト項目間のテキストがくっついている | separator='\n' と strip=True の両方を設定します | セパレータは分離を担当し、ストリップは先頭と末尾の空白を削除します |
| セパレータのみが設定されています | プラス strip=True | そうしないと、意味のないインデントされたスペースが保持されます。 |
| ストリップのみがセットされています | セパレータも追加します | 列がまだくっついている可能性があります |
| ページにスクリプトなどの無関係なタグが含まれています | 電話をかける前にこれらのタグを削除してください | get_text のパラメータはフォーマットのみを解決し、コンテンツのクリーニングは解決しません。 |
| テーブル、キャンバス、またはシャドウ DOM が含まれています | 収集計画を変更するか、不足している情報を受け入れる | このような内容はパラメータ調整では解決できません |
デフォルト動作のトラップとパラメータのセマンティクス
なぜ固着するのでしょうか?
get_text() メソッドは、separator と strip という 2 つの主要なパラメーターを受け入れます。パラメータが渡されない場合は、タグ ツリー内のすべてのテキスト ノードが直接接続されます。
このデフォルトの動作は、div、p、li などのブロックレベル要素にとって非常に問題です。 HTML ソース コード内の改行とインデントは、解析後に空白文字列に変換されるか無視されることが多く、セマンティック境界が失われます。
セパレーターとストリップは何をするのですか?
- separator: テキスト ノード間の挿入を定義します。
'\n'に設定するのが一般的ですが、完璧ではありません。 HTML が深くネストされている場合、グローバル折り返しにより多数の空行が発生する可能性があります。 - strip: 各テキスト断片の先頭と末尾の空白スペースを削除するかどうかを制御します。
strip=Trueの場合、Beautiful Soup は最初にそれぞれの独立した NavigableString に対して.strip()を実行し、次にセパレータを使用してそれらを結合します。
たとえば、<p> Hello </p> は、ダーティ データ出力の \n Hello \n ではなく、クリーンな Hello を取得します。
両方を一緒に使用する必要があります。
- ストリップなしでセパレータのみを使用すると、意味のないインデントされたスペースが残ります。
- ストリップのみでセパレーターなしの場合、単一ラインはきれいになりますが、ライン間に付着が残る場合があります。
パラメータの詳細については、Beautiful Soup 公式ドキュメント: get_text()を参照してください。
合成 DOM 境界テスト ケース
抽出ロジックを検証するには、不安定な実際の Web ページに依存するのではなく、典型的な境界問題を含む合成 HTML サンプルを構築する必要があります。次のテスト ケースは、ブロック レベル要素の接着、インライン要素の間隔、余分な空白の 3 つの状況をカバーします。
入力サンプル(合成例):
<div>
<p>第一段文字</p>
<p>第二段文字<span>内联补充</span></p>
<ul>
<li> 列表项A </li>
<li>列表项B</li>
</ul>
</div>
期待される正しい出力:
第一段文字
第二段文字
内联补充
列表项A
列表项B
separator='\n' は抽出されたテキスト ノードに作用し、ブロック レベルのタグに基づいて二重改行を自動的に生成しないことに注意してください。段落とリスト項目の間には明確な改行が必要であり、リスト項目の前後の余分なスペースは削除する必要があります。 「2番目のテキスト」と「インライン補足」は別のラベルにあり、テキストノードなので区切り文字で区切られます。
エラーと境界入力
入力が <p>End</p><script>alert(1)</script><p>Start</p> になり、スクリプト タグがフィルタリングされていない場合、出力には alert(1) が含まれます。これはセパレータが設定されている場合にも当てはまります。スクリプト ノードがテキストに含まれるかどうかは、パーサーとノードの種類によって異なります。
これを処理する正しい方法は、get_text を呼び出す前に soup.find_all('script') を使用して無関係なタグを削除するか、トラバース時に特定のタグ タイプをスキップすることです。 get_text のパラメータはフォーマットの問題のみを解決し、コンテンツのクリーニングの問題は解決しません。
Python の実装とアサーションのチェック
以下のコードは、メモリ内に上記のサンプルを構築し、決定的な検証を実行します。この例は beautifulsoup4 に依存しており、バージョン 4.9.0 以降が推奨されます。
外部ファイルをテストするには、まず HTML を UTF-8 エンコードとして保存してから、open('file.html', encoding='utf-8') を使用して読み取って、解析例外を引き起こすエンコード エラーを回避してください。コードにはネットワーク リクエストが含まれておらず、完全に自己完結型です。
from bs4 import BeautifulSoup
# 合成示例:模拟常见的HTML文本边界问题
html_content = """
<div>
<p>第一段文字</p>
<p>第二段文字<span>内联补充</span></p>
<ul>
<li> 列表项A </li>
<li>列表项B</li>
</ul>
</div>
"""
soup = BeautifulSoup(html_content, 'html.parser')
# 关键配置:separator定义边界,strip清理碎片空白
text_result = soup.get_text(separator='\n', strip=True)
# 过滤空行以获得干净的语义行
lines = [line for line in text_result.split('\n') if line]
# 验收断言:确保边界清晰且无脏数据
assert lines[0] == '第一段文字', f"段落粘连或内容错误: {lines[0]}"
assert lines[1] == '第二段文字', "块级元素间未正确分隔"
assert lines[2] == '内联补充', "内联元素文本节点被错误合并"
assert lines[3] == '列表项A', f"列表项前置空格未清除: '{lines[3]}'"
assert lines[4] == '列表项B', "列表项间边界丢失"
print("提取结果预览:")
for i, line in enumerate(lines):
print(f"{i}: [{line}]")
このコードの予想される出力は、5 行のクリーン テキストです。ご使用の環境でより多くの空行が出力される場合は、DOM 内に削除されていない純粋な空の NavigableString ノードが存在することを意味します。これは html.parser の通常の動作であり、分割後にリスト内包表記を使用してフィルタリングできます。
このコードは、パラメーターの組み合わせの原理を示すためにのみ使用されており、運用環境の複雑なページで直接使用しないでください。
📊 コアの比較
| プラン | 効果 | 制限事項 |
|---|---|---|
separator='\n'のみを使用してください | ブロック間に改行があります | インデントされたスペースを維持します。ネストが深い場合、多数の空白行が表示される可能性があります。 |
strip=Trueのみを使用してください | 各段落の始まりと終わりをきれいにする | 列がまだくっついている可能性があります |
| 両方を同時に使用する | 分離と空白の削除の両方を考慮する | 無関係なタグは抽出前に削除する必要がある |
| 正規表現を使用して抽出後の改行を推測する | 一見柔軟に見える | 推奨されません。抽出時にパラメータを正しく組み合わせることが優先されます。 |
受け入れ基準と手動レビューの境界
自動抽出では 100% の視覚的な復元を達成することはできません。次の表は、「コードが修復可能」な状況と「人間の介入が必要な」状況を区別する方法を示しています。判断は主観的な感覚ではなく、DOM の構造に基づいて行われます。
| 観察する | 何をするか | 推測すべきではない |
|---|---|---|
| インラインノード分離 | 文が分割されていないか確認する | デリミタはテキストノードに作用します |
| テーブルの平坦化 | 行と列の構造を保持して個別に処理する | get_text だけに依存することはできません |
| コンテンツを動的に描画する | コレクションのギャップを特定する | 静的解析はブラウザのレンダリングと同等ではありません |
明示的な適用外の範囲: HTML に CSS 疑似要素、Canvas でレンダリングされたコンテンツ、または Shadow DOM によって生成された大量のテキストが含まれている場合、Beautiful Soup は有効な情報を取得できません。このとき、パラメータのデバッグを継続するのではなく、取得方法を変更するか、情報の欠落を許容する必要があります。
🎯 どの方法があなたに適していますか?
もしあなたが……
段落とリスト項目のプレーンテキストを抽出する必要がある
separator='\n' と strip=True の両方を設定し、アサーション スクリプトで出力を確認します。
ページ内に多くのスクリプトまたはスタイルタグがあります
get_text を呼び出す前にこれらのタグを削除してください。パラメータによってコンテンツの問題が解決されることは期待できません。
テーブルの行と列の構造を保持する必要がある
get_text だけに依存せず、テーブル構造を個別に処理してください。
ページはブラウザのレンダリングまたは Shadow DOM に依存しています。
回収計画を変更するか、受け入れ時の回収ギャップを明確に記録してください。
❓ よくある質問 (FAQ)
1. セパレータを設定するだけで十分ですか?
足りない。セパレータのみを設定すると、無意味なインデントスペースが保持されるため、strip=True を追加する必要があります。
2. 出力に空白行が含まれるのはなぜですか?
DOM には、削除されていないプレーンな空の NavigableString ノードが存在する可能性があります。これは html.parser の通常の動作であり、分割後にリスト内包表記を使用してフィルタリングできます。
3. script タグが結果に表示されるのはなぜですか?
get_text のパラメータはフォーマットの問題を解決するだけです。呼び出す前に soup.find_all('script') で削除するか、トラバーサル中にスキップする必要があります。
4. 抽出結果が正しいかどうかを確認するにはどうすればよいですか?
5 ~ 10 個の典型的なエッジ ケースからなる合成テスト セットを構築し、アサーション スクリプトを実行します。合格率が予想より低い場合は、DOM を抽出する前に DOM を前処理する必要があるかどうかを確認してください。
📝 概要
癒着の問題の核心は、抽出後の文字列修復ではなく、抽出中のパラメータの組み合わせにあります。まず合成サンプルとアサーションを実行してから、実際のページの無関係なタグと構造上の問題に対処します。


