【问题标题】:Script throws an error when it is made to run using multiprocessing使用多处理运行脚本时会引发错误
【发布时间】:2018-11-21 16:43:13
【问题描述】:

我用 Python 结合 BeautifulSoup 编写了一个脚本来提取书名,这些书名是在亚马逊搜索框中提供一些 ISBN 号后填充的。我从名为amazon.xlsx 的excel 文件中提供这些ISBN 编号。当我尝试使用以下脚本时,它会相应地解析标题并按预期写回 excel 文件。

The link where I put isbn numbers to populate the results.

import requests
from bs4 import BeautifulSoup
from openpyxl import load_workbook

wb = load_workbook('amazon.xlsx')
ws = wb['content']

def get_info(num):
    params = {
        'url': 'search-alias=aps',
        'field-keywords': num
    }
    res = requests.get("https://www.amazon.com/s/ref=nb_sb_noss?",params=params)
    soup = BeautifulSoup(res.text,"lxml")
    itemlink = soup.select_one("a.s-access-detail-page")
    if itemlink:
        get_data(itemlink['href'])

def get_data(link):
    res = requests.get(link)
    soup = BeautifulSoup(res.text,"lxml")
    try:
        itmtitle = soup.select_one("#productTitle").get_text(strip=True)
    except AttributeError: itmtitle = "N\A"

    print(itmtitle)

    ws.cell(row=row, column=2).value = itmtitle
    wb.save("amazon.xlsx")

if __name__ == '__main__':
    for row in range(2, ws.max_row + 1):
        if ws.cell(row=row,column=1).value==None:break
        val = ws["A" + str(row)].value
        get_info(val)

但是,当我尝试使用 multiprocessing 执行相同操作时,我收到以下错误:

ws.cell(row=row, column=2).value = itmtitle
NameError: name 'row' is not defined

对于multiprocessing,我在脚本中带来的更改是:

from multiprocessing import Pool

if __name__ == '__main__':
    isbnlist = []
    for row in range(2, ws.max_row + 1):
        if ws.cell(row=row,column=1).value==None:break
        val = ws["A" + str(row)].value
        isbnlist.append(val)

    with Pool(10) as p:
        p.map(get_info,isbnlist)
        p.terminate()
        p.join()

我尝试过的几个 ISBN:

9781584806844
9780917360664
9780134715308
9781285858265
9780986615108
9780393646399
9780134612966
9781285857589
9781453385982
9780134683461

如何使用multiprocessing 消除该错误并获得所需的结果?

【问题讨论】:

    标签: python python-3.x web-scraping multiprocessing openpyxl


    【解决方案1】:

    get_data()中引用全局变量row是没有意义的,因为

    1. 它是全局的,不会在多处理池中的每个“线程”之间共享,因为它们实际上是不共享全局的独立 python 进程。

    2. 即使他们这样做了,因为您在执行 get_info() 之前构建了整个 ISBN 列表,所以 row 的值将始终为 ws.max_row + 1,因为循环已完成。

    因此,您需要提供行值作为传递给p.map() 的第二个参数的数据的一部分。但即使你要这样做,由于 Windows 文件锁定、race conditions 等,从多个进程写入和保存电子表格也是一个坏主意。你最好只使用多处理构建标题列表,然后完成后将它们写出一次,如下所示:

    import requests
    from bs4 import BeautifulSoup
    from openpyxl import load_workbook
    from multiprocessing import Pool
    
    
    def get_info(isbn):
        params = {
            'url': 'search-alias=aps',
            'field-keywords': isbn
        }
        res = requests.get("https://www.amazon.com/s/ref=nb_sb_noss?", params=params)
        soup = BeautifulSoup(res.text, "lxml")
        itemlink = soup.select_one("a.s-access-detail-page")
        if itemlink:
            return get_data(itemlink['href'])
    
    
    def get_data(link):
        res = requests.get(link)
        soup = BeautifulSoup(res.text, "lxml")
        try:
            itmtitle = soup.select_one("#productTitle").get_text(strip=True)
        except AttributeError:
            itmtitle = "N\A"
    
        return itmtitle
    
    
    def main():
        wb = load_workbook('amazon.xlsx')
        ws = wb['content']
    
        isbnlist = []
        for row in range(2, ws.max_row + 1):
            if ws.cell(row=row, column=1).value is None:
                break
            val = ws["A" + str(row)].value
            isbnlist.append(val)
    
        with Pool(10) as p:
            titles = p.map(get_info, isbnlist)
            p.terminate()
            p.join()
    
        for row in range(2, ws.max_row + 1):
            ws.cell(row=row, column=2).value = titles[row - 2]
    
        wb.save("amazon.xlsx")
    
    
    if __name__ == '__main__':
        main()
    

    【讨论】:

    • 感谢您的解决方案@cody。
    • 我看不懂这行return get_data(itemlink['href'])@cody?你为什么用return?通常这应该 get_data(itemlink['href']) 完成这项工作。
    • 请注意p.map(get_info, isbnlist) 的返回值现在被titles 捕获。 Pool.map 函数通常遵循标准 Python map 函数的语义并接受两个参数:一个函数(在本例中为 get_info)和一个值列表(isbnlist)。它返回一个新列表,其中每个值都是通过传递函数传递原始值的结果。所以get_info 需要返回一个值。查看此页面以获取有关 map() 的更多信息:book.pythontips.com/en/latest/map_filter.html
    猜你喜欢
    • 1970-01-01
    • 2021-01-24
    • 2015-12-23
    • 2013-01-21
    • 2016-03-27
    • 2021-10-19
    • 1970-01-01
    • 1970-01-01
    • 2016-12-03
    相关资源
    最近更新 更多