【问题标题】:Multi threading not processing full list多线程不处理完整列表
【发布时间】:2021-03-29 12:08:41
【问题描述】:

我正在使用多线程访问从 csv 读取的链接,奇怪的是,不管 max-workers 是什么,甚至当我删除多线程部分时,代码运行的 url 数量也比列表中的任意少。我打印列表以验证计数。例如,如果列表有 5000 个 url,则代码在 4084 处停止,如果链接为 13,000,它将在 9200 处停止,即使只有 130 个链接,它也会在 80 处停止。我在这里做错了什么?

import requests
import xlrd
import concurrent.futures
from bs4 import BeautifulSoup
import csv


header_added = False
file_location = "Urls.xlsx"
workbook = xlrd.open_workbook(file_location)
sheet = workbook.sheet_by_index(0)
all_links = []
for row in range(1, 11000):
     all_links.append(sheet.cell_value(row,0))

print(len(all_links))
i = 0
def get_solution(url):
    global header_added, i
    page = requests.get(url).text
    soup = BeautifulSoup(page, 'html.parser')
    ques_div = soup.find('p', class_='header-description')
    ques = ques_div.find('span').text
    ans_divs = soup.findAll('div', class_='puzzle-solution')
    ans = ans_divs[0].text
    print("Solution ", i)
    i += 1
    dict1 ={"Words": ques, "Solution": ans}
    with open('Results10k.csv', 'a+', encoding='utf-8') as f:
        w = csv.DictWriter(f, dict1.keys())
        if not header_added:
            w.writeheader()
            header_added = True
        w.writerow(dict1)

with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
    result = executor.map(get_solution, all_links)

【问题讨论】:

  • 这可能不是问题,但所有工作人员都写入同一个文件。我不确定 open() 是否是线程安全的。如果 open() 不是线程安全的,您可以添加互斥锁或写入单个文件并稍后加入它们。
  • @user_na 那将是数千个文件,有什么办法吗?
  • 但这种情况发生了,即使我删除了多线程并在 for 循环中运行它,就像一个简单的循环一样。
  • 可能是某些 URL 的 get_solution() 崩溃。您可以在函数主体中添加 try/except 并将所有崩溃的 URL 写入不同的文件。如果这是问题,则数字加起来应该是总数
  • max_workers=600 听起来是个糟糕的主意,顺便说一句,如果你没有 600 个内核可用的话。

标签: python multithreading list web-scraping


【解决方案1】:

这是对您的代码进行的一种不需要锁的修改——相反,只有一个进程写入文件。

此外,由于 GIL,使用 ThreadPool 会比使用进程支持的 Pool 慢。

import csv
import multiprocessing

import requests
import xlrd
from bs4 import BeautifulSoup

sess = requests.Session()


def get_solution(url):
    try:
        resp = sess.get(url)
        resp.raise_for_status()
        page = resp.text
        soup = BeautifulSoup(page, "html.parser")
        ques_div = soup.find("p", class_="header-description")
        ques = ques_div.find("span").text.strip()
        ans_divs = soup.findAll("div", class_="puzzle-solution")
        ans = ans_divs[0].text.strip()
        return {"URL": url, "Words": ques, "Solution": ans, "Error": ""}
    except Exception as exc:
        print(url, "Error:", exc)
        return {"URL": url, "Words": "", "Solution": "", "Error": str(exc)}


def read_links(file_location):
    workbook = xlrd.open_workbook(file_location)
    sheet = workbook.sheet_by_index(0)
    all_links = []
    for row in range(1, 11000):
        all_links.append(sheet.cell_value(row, 0))
    return all_links


def main():
    links = read_links("./Urls.xlsx")
    with open("Results10k.csv", "w", encoding="utf-8") as f:
        with multiprocessing.Pool() as p:  # (or multiprocessing.pool.ThreadPool)
            for i, result in enumerate(p.imap_unordered(get_solution, links, chunksize=16)):
                if i == 0:
                    writer = csv.DictWriter(f, result.keys())
                    writer.writeheader()
                writer.writerow(result)
                f.flush()  # Ensure changes are written immediately
                if i % 100 == 0:  # Progress indicator
                    print(i)


if __name__ == "__main__":
    main()

【讨论】:

  • 它说名字ques没有定义?
  • 怎么说?涉及ques的代码是你写的。
  • multiprocessing.pool.RemoteTraceback: """ Traceback (most recent call last): File "C:\Users\user\AppData\Local\Programs\Python\Python37\lib\multiprocessing\pool.py", line 121, in worker result = (True, func(*args, **kwds)) File "C:\Users\user\AppData\Local\Programs\Python\Python37\lib\multiprocessing\pool.py", line 44, in mapstar return list(map(*args)) File "C:\Users\user\PycharmProjects\Freelance\Scripts_ALL\instant.py", line 33, in get_solution return {"Words": ques, "Solution": ans} NameError: name 'ques' is not defined
  • 我会再检查一下我是否搞砸了。
  • 它不会写入 csv 。我想不通。 codeshare.io/5M69OR.
【解决方案2】:

可能是,get_solution() 对于某些 URL 崩溃。您可以在函数体中添加 try/except 并将所有崩溃的 URL 写入不同的文件。

def get_solution(url):
    try:
        ...
    except:
        with open('errors.txt','a+') as f:
            f.write(url+'\n')

如果这是问题,则数字应加起来为总数。 open() 也可能不是线程安全的。

file_lock = threading.Lock()
def get_solution(url):
    with file_lock:
        with open('Results10k.csv', 'a+', encoding='utf-8') as f:
            w = csv.DictWriter(f, dict1.keys())
            ...

【讨论】:

  • 这会锁定线程直到前一个线程完成吗?我现在正在尝试。
  • 当另一个线程在块内时调用 file_lock 时,它将等待它完成后再继续。
  • 我有 2 名工人,这似乎工作到现在。
  • 尽量不要有太多工人。如果数量太多,开销就会吃掉所有的好处。
  • 只有2个,太慢了,有十万多个链接。
猜你喜欢
  • 2012-11-12
  • 2013-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多