从具有复杂逻辑的单列创建多列

thr*_*eam 1 python beautifulsoup dataframe web-scraping pandas

我对数据框和/或系列的所有 apply、applymap、map 内容感到有点困惑。我想通过执行一些网络抓取操作的函数创建从数据框中的一列派生的多个列。

我的数据框看起来像这样

>>> df
          row1        url    row3
0        data1  http://...    123
1        data2  http://...    325
2        data3  http://...    346
Run Code Online (Sandbox Code Playgroud)

网页抓取功能是这样的

def get_stuff_from_url(url: str):
    response = requests.get(url)
    soup = BeautifulSoup(response.text, 'html.parser')
    data1 = soup.find('div', {'class': 'stuff1'})
    data2 = soup.find('span', {'class', 'stuff2'}).text
    data3 = soup.find('p', {'class', 'stuff3'}).text

    return data1, data2, data3
Run Code Online (Sandbox Code Playgroud)

结果应该是

>>> df_new
          row1        url    row3       row4       row5       row6
0        data1  http://...    123  newdata1a  newdata2a  newdata3a
1        data2  http://...    325  newdata1b  newdata2b  newdata3b
2        data3  http://...    346  newdata1c  newdata2c  newdata3c
Run Code Online (Sandbox Code Playgroud)

其中 newdata1 来自 data1 等等。

我之前的尝试(get_stuff_from_url仅返回一个值)是

df_new = df_old['url'].apply(lambda row: get_stuff_from_url(row))
Run Code Online (Sandbox Code Playgroud)

但这似乎是错误的,我无法将其扩展到多列输出。有什么想法可以按照本来的方式解决这个问题吗?

our*_*os1 5

问题。我们有一个 df,其中包含一个带有 url 的列。我们希望为每个 url 创建一个 soup,然后从创建的 soup 中返回 3 个值,并使用返回的值填充 3 个新列的行。

解决方案。这是您的功能的简化:

def get_stuff_from_url(url: str):
    # response = requests.get(url)
    # soup = BeautifulSoup(response.text, 'html.parser')
    data1 = '<div class="stuff1"><p>Stuff</p></div>'
    data2 = "Hello world"
    data3 = "Right back at you, sir!"

    return data1, data2, data3
Run Code Online (Sandbox Code Playgroud)

该函数返回多个值。如果我们将它分配给一个变量,该变量现在将包含一个元组。假设我们写道:

df_new = pd.DataFrame(df['url'].apply(lambda row: get_stuff_from_url(row)))
Run Code Online (Sandbox Code Playgroud)

然后我们最终会得到一个只有 1 列的 df,每行包含相同的元组:('<div class="stuff1"><p>Stuff</p></div>', 'Hello world', 'Right back at you, sir!')

如果我们想用元组中的 elem 填充多个列,我们可以使用zip(*iterables),其中我们使用*运算符来解压缩传递给 的元组zip()

要使用此方法创建新的 df,您可以执行以下操作:

df_new = pd.DataFrame(zip(*df['url'].apply(lambda row: get_stuff_from_url(row)))).T
Run Code Online (Sandbox Code Playgroud)

结果:

                                        0            1                        2
0  <div class="stuff1"><p>Stuff</p></div>  Hello world  Right back at you, sir!
1  <div class="stuff1"><p>Stuff</p></div>  Hello world  Right back at you, sir!
2  <div class="stuff1"><p>Stuff</p></div>  Hello world  Right back at you, sir!
Run Code Online (Sandbox Code Playgroud)

如果您只是想将数据添加到现有的 df 中,您可以这样做:

df['data1'], df['data2'], df['data3'] = zip(*df['url'].apply(lambda row: get_stuff_from_url(row)))
Run Code Online (Sandbox Code Playgroud)

让我们打印第一行看看我们最终会得到什么(print(df.iloc[0])):

row1                                      data1
url                                  http://...
row3                                        123
data1    <div class="stuff1"><p>Stuff</p></div>
data2                               Hello world
data3                   Right back at you, sir!
Name: 0, dtype: object
Run Code Online (Sandbox Code Playgroud)