BeautifulSoup группирует несколько таблиц в основной таблице - PullRequest
1 голос
/ 14 марта 2019

Я использую BeautifulSoup для анализа документа HTML со следующей структурой:

<table>
  <tr>
    <th>Thread</th>
      <td> (555EEE555)<br/>
        <table>
          <tr>
            <th>Participants</th>
              <td>John Doe<br/>Jane Doe<br/>
              </td>
            </tr>
          </table><br/><br/>
          <table>
            <tr>
              <th>Author</th>
              <td>John Doe<br/></td>
            </tr>
          </table>
          <table>
            <tr>
              <th>Sent</th>
              <td>2017-10-16 19:03:23 UTC<br/>
              </td>
            </tr>
          </table>
          <table>
            <tr>
              <th>Body</th>
              <td>Test message with some body text<br/>
              </td>
            </tr>
          </table><br/>
          <table>
            <tr>
              <th>Author</th>
              <td>Jane Doe<br/></td>
            </tr>
          </table>
          <table>
            <tr>
              <th>Sent</th>
              <td>2017-10-17 08:03:23 UTC<br/>
              </td>
            </tr>
          </table>
          <table>
            <tr>
              <th>Body</th>
              <td>Second test message with some body text<br/>
              </td>
            </tr>
          </table><br/>
        </td>
    </tr>
</table>

Эта структура сообщения повторяется по всему документу.Мне нужно разобрать отдельные сообщения, сгруппировав таблицы Author, Sent и Body.Вот код, который у меня есть:

with open(path) as g:
    soup = BeautifulSoup(g, 'html.parser')

table_parent = soup.find('td')

for idx, i in enumerate(table_parent.find_all('table', recursive=False)):
    for x in i.find_all('table'):
        print 'key: %s | data: %s' % (x.th.get_text(), x.td.get_text())

, который печатает следующее:

key: Current Participants | data: John DoeJane Doe
key: Author | data: John Doe
key: Sent | data: 2017-10-16 19:03:23 UTC
key: Body | data: Test message with some body text

Как мне написать код, который будет проходить по всему документу и группировать каждый Author,Sent и Body соответственно для разбора каждого отдельного сообщения?

Ответы [ 3 ]

1 голос
/ 14 марта 2019

Я предполагаю, что у вас всегда есть главная таблица в качестве родителя

Вы должны быть в состоянии сделать это:

from bs4 import BeautifulSoup as soup
import requests

html = """<table>
  <tr>
    <th>Thread</th>
      <td> (555EEE555)<br/>
        <table>
          <tr>
            <th>Participants</th>
              <td>John Doe<br/>Jane Doe<br/>
              </td>
            </tr>
          </table><br/><br/>
          <table>
            <tr>
              <th>Author</th>
              <td>John Doe<br/></td>
            </tr>
          </table>
          <table>
            <tr>
              <th>Sent</th>
              <td>2017-10-16 19:03:23 UTC<br/>
              </td>
            </tr>
          </table>
          <table>
            <tr>
              <th>Body</th>
              <td>Test message with some body text<br/>
              </td>
            </tr>
          </table><br/>
          <table>
            <tr>
              <th>Author</th>
              <td>Jane Doe<br/></td>
            </tr>
          </table>
          <table>
            <tr>
              <th>Sent</th>
              <td>2017-10-17 08:03:23 UTC<br/>
              </td>
            </tr>
          </table>
          <table>
            <tr>
              <th>Body</th>
              <td>Second test message with some body text<br/>
</td>
            </tr>
          </table><br/>
        </td>
    </tr>
</table>"""

def _get_obj():
    r = {
        'Author': '',
        'Sent': '',
        'Body': ''
    }
    return r

page = soup(html, 'html.parser')

main_table = page.find('table')
result = []
r = _get_obj()

for t in main_table.find_all('table'):
    if t.find('th', text='Author'):
        r['Author'] = t.find('td').get_text()
    if t.find('th', text='Sent'):
        r['Sent'] = t.find('td').get_text()
    if t.find('th', text='Body'):
        r['Body'] = t.find('td').get_text()
        result.append(r)
        r = _get_obj()

print(result)

ВЫХОД:

[
{'Author': 'John Doe', 'Sent': '2017-10-16 19:03:23 UTC\n', 'Body': 'Test message with some body text\n'},
{'Author': 'Jane Doe', 'Sent': '2017-10-17 08:03:23 UTC\n', 'Body': 'Second test message with some body text\n'}
]
0 голосов
/ 14 марта 2019

Я бы предпочел не получать таблицу и сосредоточиться на теге <th> с текстом Author, Sent и Body. Затем вы можете использовать find_next () , чтобы получить следующий td и получить текст внутри него. Затем вы можете использовать функцию zip () для агрегирования данных. Если разметка находится в переменной html_doc, то следующий код должен работать.

from bs4 import BeautifulSoup
soup=BeautifulSoup(html_doc,'html.parser')
authors=[x.find_next('td').text for x in soup.find_all('th',text='Author')]
sent=[x.find_next('td').text.strip() for x in soup.find_all('th',text='Sent')]
body=[x.find_next('td').text.strip() for x in soup.find_all('th',text='Body')]
for item in zip(authors,sent,body):
    print(item)

Выходы:

('John Doe', '2017-10-16 19:03:23 UTC', 'Test message with some body text')
('Jane Doe', '2017-10-17 08:03:23 UTC', 'Second test message with some body text')
0 голосов
/ 14 марта 2019

Вот мое решение, я продолжаю с вашим кодом:

table_parent = soup.find('td')
tables = table_parent.find_all('table', recursive=False)
tables_str = " ".join([str(t) for t in tables[1:]])
soup_tables = BeautifulSoup(tables_str)
trs = soup_tables.find_all("tr")

for i in xrange(0, len(trs), 3):
    print(trs[i].contents[1].text, trs[i].contents[3].text)
    print(trs[i+1].contents[1].text, trs[i+1].contents[3].text)
    print(trs[i+2].contents[1].text, trs[i+2].contents[3].text)
    print("-"*8)

Это напечатает:

(u'Author', u'John Doe')
(u'Sent', u'2017-10-16 19:03:23 UTC\n')
(u'Body', u'Test message with some body text\n')
--------
(u'Author', u'Jane Doe')
(u'Sent', u'2017-10-17 08:03:23 UTC\n')
(u'Body', u'Second test message with some body text\n')
--------

Если вам нужно что-то объяснить, спросите меня

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...