并行抓取到 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.cycledf.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/