Python-BS4-仅使用表头+另存为字典从Wikipedia表中提取子表

时间:2018-06-23 04:38:30

标签: python beautifulsoup wikipedia

我正在尝试定义一个函数,该函数提取网站https://de.wikipedia.org/wiki/Stuttgart上“ Basisdaten”表的所有行,并返回一个字典,该字典的键和值分别对应于表中每一行的第一和第二个单元格。

“ Basisdaten”表是更大的表的一部分,如以下代码的结果所示:

from bs4 import BeautifulSoup 
import requests
r=requests.get("https://de.wikipedia.org/wiki/Stuttgart")
soup=BeautifulSoup(r.text,"html.parser")
soup.find('th', text=re.compile('Basisdaten')).find_parent('table')

不幸的是,没有唯一的ID,我只能用来选择组成“ Basisdaten”表的那些行。这些是我希望以HTML格式提取的行:

<tr>
<th colspan="2">Basisdaten
</th></tr>
<tr class="hintergrundfarbe2">
<td><a href="/wiki/Land_(Deutschland)" title="Land (Deutschland)">Bundesland</a>:</td>
<td><a href="/wiki/Baden-W%C3%BCrttemberg" title="Baden-Württemberg">Baden-Württemberg</a>
</td></tr>
<tr class="hintergrundfarbe2">
<td><a href="/wiki/Regierungsbezirk" title="Regierungsbezirk">Regierungsbezirk</a>:
</td>
<td><a href="/wiki/Regierungsbezirk_Stuttgart" title="Regierungsbezirk Stuttgart">Stuttgart</a>
</td></tr>
<tr class="hintergrundfarbe2">
<td><a href="/wiki/H%C3%B6he_%C3%BCber_dem_Meeresspiegel" title="Höhe über dem Meeresspiegel">Höhe</a>:
</td>
<td>247 m ü. <a href="/wiki/Normalh%C3%B6hennull" title="Normalhöhennull">NHN</a>
</td></tr>
<tr class="hintergrundfarbe2">
<td><a href="/wiki/Katasterfl%C3%A4che" title="Katasterfläche">Fläche</a>:
</td>
<td>207,35 km<sup>2</sup>
</td></tr>
<tr class="hintergrundfarbe2">
<td>Einwohner:
</td>
<td style="line-height: 1.2em;">628.032 <small><i>(31. Dez. 2016)</i></small><sup class="reference" id="cite_ref-Metadaten_Einwohnerzahl_DE-BW_1-0"><a href="#cite_note-Metadaten_Einwohnerzahl_DE-BW-1">[1]</a></sup>
</td></tr>
<tr class="hintergrundfarbe2">
<td><a href="/wiki/Bev%C3%B6lkerungsdichte" title="Bevölkerungsdichte">Bevölkerungsdichte</a>:
</td>
<td>3029 Einwohner je km<sup>2</sup>
</td></tr>
<tr class="hintergrundfarbe2">
<td style="vertical-align: top;"><a href="/wiki/Postleitzahl_(Deutschland)" title="Postleitzahl (Deutschland)">Postleitzahlen</a>:
</td>
<td>70173–70619
</td></tr>
<tr class="hintergrundfarbe2">
<td style="vertical-align: top;"><a href="/wiki/Telefonvorwahl_(Deutschland)" title="Telefonvorwahl (Deutschland)">Vorwahl</a>:
</td>
<td>0711
</td></tr>
<tr class="hintergrundfarbe2">
<td style="vertical-align: top;"><a href="/wiki/Kfz-Kennzeichen_(Deutschland)" title="Kfz-Kennzeichen (Deutschland)">Kfz-Kennzeichen</a>:
</td>
<td>S
</td></tr>
<tr class="hintergrundfarbe2">
<td style="vertical-align: top;"><a href="/wiki/Amtlicher_Gemeindeschl%C3%BCssel" title="Amtlicher Gemeindeschlüssel">Gemeindeschlüssel</a>:
</td>
<td>08 1 11 000
</td></tr>
<tr class="hintergrundfarbe2 metadata">
<td><a href="/wiki/UN/LOCODE" title="UN/LOCODE">LOCODE</a>:
</td>
<td>DE STR
</td></tr>
<tr class="hintergrundfarbe2 metadata">
<td><a href="/wiki/NUTS" title="NUTS">NUTS</a>:
</td>
<td>DE111
</td></tr>
<tr class="hintergrundfarbe2">
<td style="vertical-align: top;">Stadtgliederung:
</td>
<td>23 <a href="/wiki/Liste_der_Stadtbezirke_und_Stadtteile_von_Stuttgart" title="Liste der Stadtbezirke und Stadtteile von Stuttgart">Stadtbezirke</a><br/>mit 152 Stadtteilen
</td></tr>
<tr class="hintergrundfarbe2">
<td style="vertical-align: top;">Adresse der<br/>Stadtverwaltung:
</td>
<td>Marktplatz 1<br/>70173 Stuttgart
</td></tr>
<tr class="hintergrundfarbe2" style="vertical-align: top;">
<td>Webpräsenz:
</td>
<td style="max-width: 10em; overflow: hidden; word-wrap: break-word;"><a class="external text" href="//www.stuttgart.de/" rel="nofollow">www.stuttgart.de</a>
</td></tr>
<tr class="hintergrundfarbe2">
<td style="vertical-align: top;"><a href="/wiki/Oberb%C3%BCrgermeister" title="Oberbürgermeister">Oberbürgermeister</a>:
</td>
<td><a href="/wiki/Fritz_Kuhn" title="Fritz Kuhn">Fritz Kuhn</a> (<a href="/wiki/B%C3%BCndnis_90/Die_Gr%C3%BCnen" title="Bündnis 90/Die Grünen">Bündnis 90/Die Grünen</a>)
</td></tr>

我成功编写了这段代码,以字典的形式给了我想要的结果:

data = []
def extractDict(y):
    results = y.find("th", {"colspan" : "2"}).find_parent('table').select('td')[3:35]
    for row in results:
        data.append(row.text.strip().replace('\xa0', '').replace(':', '').replace('[1]', ''))
    return dict(zip(data[::2], data[1::2]))
basisdaten=extractDict(soup)
basisdaten

结果

{'Adresse derStadtverwaltung': 'Marktplatz 170173 Stuttgart',
 'Bevölkerungsdichte': '3029Einwohner je km2',
 'Bundesland': 'Baden-Württemberg',
 'Einwohner': '628.032 (31.Dez.2016)',
 'Fläche': '207,35km2',
 'Gemeindeschlüssel': '08111000',
 'Höhe': '247m ü.NHN',
 'Kfz-Kennzeichen': 'S',
 'LOCODE': 'DE STR',
 'NUTS': 'DE111',
 'Oberbürgermeister': 'Fritz Kuhn (Bündnis 90/Die Grünen)',
 'Postleitzahlen': '70173–70619',
 'Regierungsbezirk': 'Stuttgart',
 'Stadtgliederung': '23 Stadtbezirkemit 152 Stadtteilen',
 'Vorwahl': '0711',
 'Webpräsenz': 'www.stuttgart.de'}

但是,我正在寻找更好的解决方案,该解决方案不涉及简单地从父表中选择第4至35行。我随后打算在其他类似的Wikipedia网址上使用此代码,并且“ Basisdaten”表的行数可能会因网站而异。

所有“ Basisdaten”表之间的相似之处在于它们都嵌入在第一个表中,并且它们都有两列,因此都以“ th colspan =“ 2””开头。父表包含其他子表,例如,在这种情况下,子表“Baden-Württemberg中的Lage der Stadt Stuttgart”在“ Basisdaten”之后。

是否可以编写一个循环来搜索'Basisdaten'子表头并在此之后获取所有行,但是在到达下一个子表头('th colspan =“ 2”')时停止循环?

我只想找到包含Basisdaten表开头的行:

soup.find('th', text=re.compile('Basisdaten'))

希望如此!我对Beautifulsoup和Python还是陌生的,这对我来说是一个非常具有挑战性的问题。

1 个答案:

答案 0 :(得分:0)

这应该做

from bs4 import BeautifulSoup
import requests

data = requests.get("https://de.wikipedia.org/wiki/Stuttgart").text
soup = BeautifulSoup(data, "lxml")
trs = soup.select('table[id*="Infobox"] tr')
is_in_basisdaten = False
data = {}
clean_data = lambda x: x.get_text().strip().replace('\xa0', '').replace(':', '')
for tr in trs:
    if tr.th:
        if "Basisdaten" in tr.th.string:
                is_in_basisdaten = True
        if is_in_basisdaten and "Basisdaten" not in tr.th.string:
            break
    elif is_in_basisdaten:
        key, val = tr.select('td')
        data[clean_data(key)] = clean_data(val)

print(data)