并行抓取到 dataframe/csv 文件的最佳方式
Optimal way to parallel scraping into a dataframe/csv file
假设我有一个充满数据的数据框,一个包含不同 url 的列,我想在数据框的每个 url 的页面上抓取价格(这是相当大的,超过 15k 行)。我希望这种抓取能够连续 运行(当它到达 url 的末尾时,它会一次又一次地重新开始)。每次抓取价格时,数据框的最后一列(价格)都会更新。
这是玩具数据框的可视化示例:
Col 1 ... Col N URL Price
XXXX ... XXXXX http://www.some-website1.com/ 23,5$
XXXX ... XXXXX http://www.some-website2.com/ 233,5$
XXXX ... XXXXX http://www.some-website3.com/ 5$
XXXX ... XXXXX http://www.some-website4.com/ 2$
.
.
.
我的问题是:使用并行方法(多线程...)抓取这些 URL 的最有效方法是什么,我知道我可以使用 request/selenium/bs4 来实现解决方案...(我可以几乎可以学到任何东西)所以我想要一个理论上的答案,而不是一些代码行,但是如果你有一个块要发送,请不要犹豫:)
谢谢
您可以使用下一个示例如何定期检查 URL。它使用 itertools.cycle
和 df.iterrows
。然后在 Pool.imap_unordered
中使用此生成器来获取数据:
import requests
from time import sleep
from itertools import cycle
from bs4 import BeautifulSoup
from multiprocessing import Pool
headers = {
"User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:92.0) Gecko/20100101 Firefox/92.0"
}
def get_data(tpl):
idx, row = tpl
r = requests.get(row["url"], headers=headers)
soup = BeautifulSoup(r.content, "html.parser")
sleep(1)
return (
idx,
soup.find(class_="Trsdu(0.3s) Fw(b) Fz(36px) Mb(-4px) D(ib)").text,
)
if __name__ == "__main__":
c = cycle(df.iterrows())
with Pool(processes=2) as p:
for i, (idx, new_price) in enumerate(p.imap_unordered(get_data, c)):
df.loc[idx, "Price"] = new_price
# print the dataframe only every 10th iteration:
if i % 10 == 0:
print()
print(df)
else:
print(".", end="")
打印:
...
url Price
0 https://finance.yahoo.com/quote/AAPL/ 139.14
1 https://finance.yahoo.com/quote/INTC/ 53.47
.........
...and so on
df
使用:
url
0 https://finance.yahoo.com/quote/AAPL/
1 https://finance.yahoo.com/quote/INTC/
假设我有一个充满数据的数据框,一个包含不同 url 的列,我想在数据框的每个 url 的页面上抓取价格(这是相当大的,超过 15k 行)。我希望这种抓取能够连续 运行(当它到达 url 的末尾时,它会一次又一次地重新开始)。每次抓取价格时,数据框的最后一列(价格)都会更新。
这是玩具数据框的可视化示例:
Col 1 ... Col N URL Price
XXXX ... XXXXX http://www.some-website1.com/ 23,5$
XXXX ... XXXXX http://www.some-website2.com/ 233,5$
XXXX ... XXXXX http://www.some-website3.com/ 5$
XXXX ... XXXXX http://www.some-website4.com/ 2$
.
.
.
我的问题是:使用并行方法(多线程...)抓取这些 URL 的最有效方法是什么,我知道我可以使用 request/selenium/bs4 来实现解决方案...(我可以几乎可以学到任何东西)所以我想要一个理论上的答案,而不是一些代码行,但是如果你有一个块要发送,请不要犹豫:)
谢谢
您可以使用下一个示例如何定期检查 URL。它使用 itertools.cycle
和 df.iterrows
。然后在 Pool.imap_unordered
中使用此生成器来获取数据:
import requests
from time import sleep
from itertools import cycle
from bs4 import BeautifulSoup
from multiprocessing import Pool
headers = {
"User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:92.0) Gecko/20100101 Firefox/92.0"
}
def get_data(tpl):
idx, row = tpl
r = requests.get(row["url"], headers=headers)
soup = BeautifulSoup(r.content, "html.parser")
sleep(1)
return (
idx,
soup.find(class_="Trsdu(0.3s) Fw(b) Fz(36px) Mb(-4px) D(ib)").text,
)
if __name__ == "__main__":
c = cycle(df.iterrows())
with Pool(processes=2) as p:
for i, (idx, new_price) in enumerate(p.imap_unordered(get_data, c)):
df.loc[idx, "Price"] = new_price
# print the dataframe only every 10th iteration:
if i % 10 == 0:
print()
print(df)
else:
print(".", end="")
打印:
...
url Price
0 https://finance.yahoo.com/quote/AAPL/ 139.14
1 https://finance.yahoo.com/quote/INTC/ 53.47
.........
...and so on
df
使用:
url
0 https://finance.yahoo.com/quote/AAPL/
1 https://finance.yahoo.com/quote/INTC/