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)
但这似乎是错误的,我无法将其扩展到多列输出。有什么想法可以按照本来的方式解决这个问题吗?
问题。我们有一个 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)