3

これがhtmlです

<table>
<tr>
<td class="break">mono</td>
</tr>
<tr>
<td>c1</td>
<td>c2</td>
<td>c3</td>
</tr>
<tr>
<td>c11</td>
<td>c22</td>
<td>c33</td>
</tr>
<tr>
<td class="break">dono</td>
</tr>
<tr>
<td>d1</td>
<td>d2</td>
<td>d3</td>
</tr>
<tr>
<td>d11</td>
<td>d22</td>
<td>d33</td>
</tr>
</table>

今、私はcsvファイルでこのような出力が欲しいです:

mono c1 c2 c3
mono c11 c22 c33
dono d1 d2 d3
dono d11 d22 d33

しかし、私は次のような出力を取得しています:

mono
c1 c2 c3
c11 c22 c33
dono
d1 d2 d3
d11 d22 d33

これが私のコードです:

import codecs
from bs4 import BeautifulSoup
with codecs.open('dump.csv', "w", encoding="utf-8") as csvfile:


    f = open("input.html","r")

    soup = BeautifulSoup(f)
    t = soup.findAll('table')
    for table in t:
        rows = table.findAll('tr')
        for tr in rows:
            cols = tr.findAll('td')
            for td in cols:
                csvfile.write(str(td.find(text=True)))
                csvfile.write(",")
            csvfile.write("\n")

この問題の解決にご協力ください。ありがとうございます。

編集:

詳細を説明します。ここで、追加する最初のセクション(mono、donoなど)を追加する必要があります。

ここでのルールは、新しい「ブレーク」クラスに遭遇しない限り、そのクラス内のテキストをその下のtrに追加する必要があるということです。

4

4 に答える 4

3

新しい質問は事実上元の質問とはまったく異なる質問なので、ここにまったく異なる答えがあります。

for table in t:
    rows = table.findAll('tr')
    for row in rows:
        cols = row.findAll('td')
        if 'break' in cols[0].get('class', []):
            header = cols[0].text
        else:
            print header, ' '.join(col.text for col in cols)

行は正確に1つの「ブレーク」列、または1つ以上の通常の列になると想定しています。これらの仮定が正しくない場合は、コードを変更できます。

また、join関数のジェネレータ式で混乱が生じた場合は、同じことを明示的なループとして書き直すことができます。ヘッダーを出力します。次に、各列について、その列を印刷します。次に、改行を出力します。

の説明をお願いしたので、'break' in cols[0].get('class', [])分解します。

  • colsは、現在のノードのすべてのノードlistのBS4Tagオブジェクトの1つです。tdtr
  • cols[0]最初のものです。
  • cols[0].get('class', [])ドキュメントTagで説明されているように、オブジェクトをディクショナリとして扱い、使い慣れたメソッドを呼び出します。 get(key, defaultvalue)
    • BS4では(古いバージョンとは異なり)、Tag名前で属性を検索すると常に。が返されますlist。BS3はと'foo bar'のため<td class='foo bar'>に戻りますが、BS4は両方のため'bar'に戻ります。<td class='foo' class='bar'>['foo', 'bar']
  • すべてをまとめるとcols[0].get('class', [])['break']ケース<td class='break'>[]、サンプル入力の他のすべてのケースに当てはまります。

上記のように、行は正確に1つの「ブレーク」列、または1つ以上の通常の列になると想定しています。コードのどこでこれらの仮定を利用しているのかがわかります。しかし、これらの仮定のいずれかが破られた場合、あなたはそれらの場合にあなたが何をしたいのかを知るのに十分なことを私たちに伝えていません。

列のない行がある場合は、明らかに。cols[0]が発生しIndexErrorます。しかし、その場合はどうするかを決める必要があります。それは何もすべきではありませんか?ヘッダーだけを印刷しますか?ヘッダー行が表示されるまで何も出力されない状態に変更しますか?何を決めるにしても、コーディングは簡単なはずです。

ヘッダーの後に通常の行が続く行がある場合、通常の行は無視されます。行の最初の列ではないヘッダーがある場合、それらは通常の値のように扱われます。同じ行に複数のヘッダーがある場合、最初のヘッダーを除くすべてが無視されます。等々。いずれの場合も、これが何であるかどうかはわかりません。ただし、コードを作成する前に、必要なものを決定する必要があります。

于 2012-12-21T01:02:57.913 に答える
2

csvCSVファイルを操作するには、組み込みのモジュールを使用します。手動で行うよりもはるかに簡単です。

問題は、csvfile.write('\n')インデントが大きすぎるために発生しているため、データは表に表示されているとおりに書き込まれます。代わりにジェネレーターを作成すると、機能するはずです。

import csv
from bs4 import BeautifulSoup

def get_fields(soup):
    for td in soup.find_all('td'):
        yield td.get_text().strip()

with open('csvfile.csv', 'w') as csvfile:
    writer = csv.writer(csvfile)

    with open('input.html', 'r') as handle:
        soup = BeautifulSoup(handle.read())

    fields = list(get_fields(soup))

    writer.writerow(fields)
于 2012-12-21T00:12:29.893 に答える
1

テーブル内のすべての行を一緒に実行したい場合は、行を無視しないのはなぜですか?

for table in t:
    cols = table.findAll('td')
    for td in cols:
        csvfile.write(str(td.find(text=True)))
        csvfile.write(",")
    csvfile.write("\n")

厳密なパーサーの代わりにBeautifulSoupを使用する理由の半分は、構造を緩くプレイできるようにするためです(残りの半分は、構造を生成しながら緩くプレイした人々に対処できるようにするためです)。では、列ごとに移動できるのに、なぜ行ごとに移動してから、行ごとに無視しようとするのでしょうか。

csvモジュールを手動でフォーマットするよりもモジュールを使用する方がはるかに良いでしょうが、それは別の問題です。

于 2012-12-21T00:22:59.873 に答える
1

csvfile.write("\n")trループではなく、テーブルループの最後で発生するように、インデントを解除してみましたか?

于 2012-12-21T00:08:42.947 に答える