将 h4 映射到数据帧 Beautifulsoup python 中的 div 个兄弟姐妹

Map h4 to div siblings in dataframe Beautifulsoup python

我正在抓取网页,但无法将信息映射到数据框中。 HTML 中没有表格。这是 HTML:

的示例
html= [
<h2>Event Title<h2>
<div class="row">
    <h4>Category 1<h4>
    <div>A<div>
    <h4>Category 2<h4>
    <div>B<div>
    <h4>Category 3<h4>
    <div>C<div>
    <h4>Category 4<h4>
    <div>D<div>
]

这是我在 python 中使用请求和 Beautifulsoup 的代码:

data = []
event = soup.find('h2')
for i in soup.find_all('div', {'class': 'row'}):
    categories = [x.text for x in i.findAll('h4')]
    info = [x.text for x in i.findAll('div')]

    datum = {'event': event.get_text().replace('\n', '').replace('\r', ''), 
             'categories ': categories , 
             'info ': info }

    data.append(datum)

df = pd.DataFrame(data)
df

数据框最终看起来像一个事件标题和两个列表:

index - event - categories - info
1 - Event Title - ['Category 1','Category 2','Category 3','Category 4'] - ["Category 1 \n A\n Category 2\n B\n Category 3\n C\n Category 4\n D\n"]

我希望它以某种方式映射以结束,以便 h4 类别 1 与 div A 相关。

index - event - categories - info
1 - Event Title - Category 1 - A
2 - Event Title - Category 2 - B
3 - Event Title - Category 3 - C
4 - Event Title - Category 4 - D

由于 h4 和 div 是兄弟姐妹而不是 parent-child ,可以在我的网络抓取代码中将其分开吗?我有多个事件标题不同的页面,而且数据太大,无法手动完成。

我也试过,其中:

data = []

event = soup.find('h2').get_text()

for i in soup.find_all('div', {'class': 'row'}):
    categories = [x.text for x in soup.findAll('h4')]
    cats = soup.find_all('h4')
    cat = cats[3]
    info = cat.findNextSiblings('div')

    datum = {'event': event, 'categories ': categories , 'info': info} 
    data.append(datum)

    df1 = pd.DataFrame(data)
df1

这个结果给了我一个 df:

index - event - categories - info
1 - Event Title - ['Category 1','Category 2','Category 3','Category 4'] - [<div>A<div>, <div>B<div>, <div>C<div>, <div>D<div>]

这是检查元素的网页链接: https://www.ibjjfdb.com/ChampionshipResults/926/PublicResults

任何想法都会有所帮助。谢谢!

类型、类别和信息在您的链接 example 中都处于同一级别,因此您必须遍历它们并在遇到新类型或类别时立即更新类型和类别(请注意——我不得不为结果类型引入一个新的列类型。

关于 pandas 数据框:如果您首先将所有数据收集到一个列表中,然后才在最后从该列表中创建一个数据框,那么它在性能方面要好得多,并且在代码中也更容易阅读.

import pandas as pd
import requests
from bs4 import BeautifulSoup
import re

data = []
r = requests.get("https://www.ibjjfdb.com/ChampionshipResults/926/PublicResults")
soup = BeautifulSoup(r.content)

event = soup.find('h2').get_text(strip=True)
for i in soup.find_all('div', {'class': 'col-xs-12'}):
    for s in i.find_all(['h3','h4','div'],recursive=False):
        if s.name == 'h3':
            typ = re.sub('\s+', ' ', s.get_text(strip=True))
        elif s.name == 'h4':
            cat = re.sub('\s+', ' ', s.get_text(strip=True))
        elif s.name == 'div':
            divs = s.find_all('div')
            if len(divs) > 0:
                for di in divs:
                    info = re.sub('\s+', ' ', di.get_text(strip=True))
            else:
                info = re.sub('\s+', ' ', s.get_text(strip=True))
            data.append((event,typ,cat,info))

df = pd.DataFrame(data, columns=['Event','Type','Category','Info'])

这会产生一个具有 452 行和 4 列的数据帧,示例输出 df.iloc[0]:

Event       World Jiu-Jitsu IBJJF Championship 2018
Type                           Results of Academies
Category                                 Adult Male
Info                    10 - Ribeiro Jiu-Jitsu - 15