5

いくつかの不安定なHTMLテーブルをBeautifulSoupのリストに解析するのに苦労しています。問題のテーブルには</td>タグがありません。

次のコードを使用します(私が解析している実際のテーブルではありませんが、機能的には似ています):

import bs4
test = "<table> <tr><td>1<td>2<td>3</tr> <tr><td>1<td>2<td>3</tr> </table>"
def walk_table2(text):
    "Take an HTML table and spit out a list of lists (of entries in a row)."
    soup = bs4.BeautifulSoup(text)
    return [[x for x in row.findAll('td')] for row in soup.findAll('tr')]

print walk_table2(test)

私に与える:

[[<td>1<td>2<td>3</td></td></td>, <td>2<td>3</td></td>, <td>3</td>], [<td>4<td>5<td>6</td></td></td>, <td>5<td>6</td></td>, <td>6</td>]]

予想よりも:

[[<td>1</td>, <td>2</td>, <td>3</td>], [<td>1</td>, <td>2</td>, <td>3</td>]]

Beautiful Soupが使用しているlxmlパーサーは、<td>の次のインスタンスではなく、</tr>の次のインスタンスの前に</td>タグを追加することを決定したようです。

この時点で、パーサーが終了tdタグを正しい場所に配置するための適切なオプションがあるかどうか、または文字列をBeautifulSoupに投げる前に正規表現を使用して手動で配置する方が簡単かどうか疑問に思っています。 。 何かご意見は?前もって感謝します!

4

3 に答える 3

4

Pythonの組み込みHTMLパーサーによって行われた決定が表示されています。パーサーの動作が気に入らない場合は、BeautifulSoupに別のパーサーを使用するように指示できます。html5libパーサーとlxmlパーサーはどちらも、必要な結果を提供します。

>>> soup = bs4.BeautifulSoup(test, "lxml")
>>> [[x for x in row.findAll('td')] for row in soup.findAll('tr')]
[[<td>1</td>, <td>2</td>, <td>3</td>], [<td>1</td>, <td>2</td>, <td>3</td>]]

>>> soup = bs4.BeautifulSoup(test, "html5lib")
>>> [[x for x in row.findAll('td')] for row in soup.findAll('tr')]
[[<td>1</td>, <td>2</td>, <td>3</td>], [<td>1</td>, <td>2</td>, <td>3</td>]]
于 2012-08-17T19:45:29.073 に答える
2

これは私にはBeautifulSoupのバグのように聞こえます。BS 3.1で3.0.8からのリグレッション(「不正な終了タグ」エラーを含む)がある理由を詳しく説明しているこのページを見つけました。これは、不正なHTMLを解析するために、1つのオプションが複数のバージョンに戻ることであることを示唆しています。とは言うものの、このページには、それが置き換えられ、現在は歴史的な参照のためにのみ存在していると書かれています。ただし、BS4がBS 3.1で導入された問題をどれだけ解決するかは明確ではありません。少なくとも、古いバージョンを試してみても問題はありません。

于 2012-08-17T18:28:14.810 に答える
1

この特定のピンチを乗り越えるためのパッチ修正:

着信データを正規表現でマッサージします(これは非常に脆弱であり、stackoverflowが正規表現とhtmlについてどのように感じているかを知っていますが、C'MON、これは1回だけです...)

import re
r1 = re.compile('(?<!\<tr\>)\<td', re.IGNORECASE)
r2 = re.compile('\<\/tr>', re.IGNORECASE)
test = "<table> <tr><td>1<td>2<td>3</tr> <tr><td>1<td>2<td>3</tr> </table>"
test = r1.sub('</td><td', test)
test = r2.sub('</td></tr>', test)
print test

ああ、そしてtestその後:

<table> <tr><td>1</td><td>2</td><td>3</td></tr> <tr><td>1</td><td>2</td><td>3</td></tr> </table>
于 2012-08-17T18:47:14.857 に答える