用Bs4刮擦标签的子元素

时间:2020-11-08 00:51:50

标签: python-3.x beautifulsoup

我正在尝试从此页面中获取一些info

url = 'https://www.emsc-csem.org/Earthquake/'

response = requests.get(url)
soup = BeautifulSoup(response.content, "html.parser")

info = soup.findAll("tbody")
for elem in info:
    print (elem)

从这里开始,我希望能够获取有关地震的信息。因此,对于每一行,我想迭代到一些众所周知的索引(在这种情况下为td)并获取内容。有什么办法可以在此for循环内完成操作,而无需另外进行soup吗?我认为我希望这样做的方式类似于elem.td[3](我知道这是错误的,但要记住)。

编辑: 我尝试过这样的事情:

from bs4 import BeautifulSoup
import requests

url = 'https://www.emsc-csem.org/Earthquake/'

response = requests.get(url)
soup = BeautifulSoup(response.content, "html.parser")

info = soup.find_all("tr")
i = 0
for tr in info:
    date = tr.find("td", {"class": "tabev6"})
    if date is not None:
        print(date.text)

现在,在查看date之后,现在我不确定在某些情况下None为什么是html的原因。每个tr应该有一个td和一个class = tabev6

2 个答案:

答案 0 :(得分:2)

您可以编写一个自定义函数以从表中的tds中提取信息。在下面的示例中,我提取了超链接,确保lat值在单列中,然后将时间和日期分成不同的列,以便进行其他排序。

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

def get_processed_row(sample_row:object)->list:
    row = []
    lat = lon = ''
    
    for num, td in enumerate(sample_row.select('td')):
        if num == 3:
            link_node = td.select_one('a')
            link = 'https://www.emsc-csem.org/' + link_node['href']
            date, time = re.sub('(\\xa0)+',' ', link_node.text).split(' ')
            ago = td.select_one('.ago').text
            row.extend([link, date, time, ago])
        elif num in [4, 5]:
            lat+=td.text 
        elif num in [6, 7]:
            lon+=td.text
        elif num == 12:
            update_date, update_time = td.text.split(' ')
            row.extend([update_date, update_time])
        else:
            row.extend([td.text.strip()])
    row.extend([lat, lon])
    return row

soup = bs(requests.get('https://www.emsc-csem.org/Earthquake/').content, 'lxml')
rows = [get_processed_row(row) for row in soup.select('#tbody tr')]
df = pd.DataFrame(rows)

df.columns = ['Num_Comments', 'Num_Pictures', 'MacroseismicIntensity', 'Link', 'DateUTC', 'TimeUTC', 'Ago',
              'Depth_km', 'MagType', 'Mag', 'Region', 'LastUpdateDate', 'LastUpdateTime', 'LatDegrees','LonDegrees']

df = df[['Num_Comments', 'Num_Pictures', 'MacroseismicIntensity', 'Link', 'DateUTC', 'TimeUTC', 'Ago',
         'LatDegrees','LonDegrees', 'Depth_km', 'MagType', 'Mag', 'Region', 'LastUpdateDate', 'LastUpdateTime']]

print(df)

您也可以使用pandas来完成整个操作,但是由于某些数据被分散在不同的列中并且还有一些重复的标头需要处理,因此还有一些列需要整理。下面的代码使用read_html和索引来获取正确的表,然后处理上述情况,并将Date,Time和Ago提取到单独的列中。最后对列进行重新排序,清除NaN,然后​​根据您的要求转换为列表。

import pandas as pd

t = pd.read_html('https://www.emsc-csem.org/Earthquake/')[3]
t = t.iloc[:-2, :] #remove last row
t.columns = [i for i in range(len(t.columns))] # rename columns as some headers are repeated
t['LatDegrees'] = t[4] + t[5] #join cols 
t['LonDegrees'] = t[6] + t[7] #join cols
t['DateUtc'] = t[3].apply(lambda x: str(x)[:10]) #subset for new col
t['TimeUtc'] = t[3].apply(lambda x: str(x)[12:21])
t['Ago'] = t[3].apply(lambda x: str(x)[22:])
t.drop(t.iloc[:,3:8], axis=1, inplace=True) #get rid of columns used to generate new cols

t.columns = ['Num_Comments', 'Num_Pictures', 'MacroseismicIntensity', 'Depth_km', 'MagType', 'Mag', 'Region', 
             'LastUpdateDateTime', 'LatDegrees','LonDegrees', 'DateUTC', 'TimeUTC', 'Ago'] #add headers

t = t[['Num_Comments', 'Num_Pictures', 'MacroseismicIntensity', 'LatDegrees','LonDegrees',  'Depth_km', 'MagType', 'Mag', 'Region', 
       'DateUTC', 'TimeUTC', 'Ago', 'LastUpdateDateTime']] #re-order columns

t['MagType'] = t['MagType'].str.upper() # ensure case consistency
t = t.dropna(how='all').fillna('') # remove nans
rows = t.values.tolist() # convert to requested list (list of lists)
print(rows)
#for row in rows:
    #print(' '.join(row))

在两种情况下,我都不喜欢对源表中的定位保持恒定的依赖,但这需要作为上述假设。标题名称可能会按列顺序更改,此外还会出现重复的标题问题。

答案 1 :(得分:1)

只需添加text并筛选出所需的数据。 注意,您可以添加另一个循环并使用elem.text.split("")

进行迭代
from bs4 import BeautifulSoup
import requests

url = 'https://www.emsc-csem.org/Earthquake/'

response = requests.get(url)
soup = BeautifulSoup(response.content, "html.parser")

info = soup.findAll("tbody")
for elem in info:
  print (elem.text)

如果您希望在实际操作中看到它,请访问我的colab笔记本。

https://colab.research.google.com/drive/1uMosEG-owTdzixo-vaDRQW_aYfseLG8Q?usp=sharing

EathQuakeInfo