如何使Python多处理池工作以写入相同的日志文件

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使Python多处理池工作以写入相同的日志文件相关的知识,希望对你有一定的参考价值。

我正在尝试在运行Python多处理池时将日志写入相同的日志文件。但我发现我的主要方法不起作用。任何人都可以检查我的代码?谢谢。

以下主要方法verify_html_file_content未在多处理池中执行。我正在尝试将日志写入同一个日志文件,日志文件是传递给main方法verify_html_file_content的参数:

import codecs
import datetime
import os

import multiprocessing

import requests
import time
from selenium import webdriver
from selenium.webdriver.common.by import By

css_xpath = "//link"
js_xpath = "//script"
img_xpath = "//img"
i = 1


def is_404_error(html_url, attr_url, file_path, log_file):
    try:
        r = requests.head(attr_url, headers={'Connection': 'close'})
        if r.status_code == 404:
            log_file.write(file_path + " : " + html_url + " attr: " + attr_url + " return 404!
")
            log_file.flush()
        else:
            log_file.write(file_path + " : " + html_url + " attr: " + attr_url + " correct!
")
            log_file.flush()
    except requests.ConnectionError:
        print("ConnectionError: " + attr_url)


def verify_html_file_link(driver, html_url, file_path, log_file):
    driver.get(html_url)
    global i
    print("html_file No.: ", i)
    driver.implicitly_wait(15)
    xpath_list = [css_xpath, js_xpath, img_xpath]
    for xpath in xpath_list:
        try:
            des = driver.find_elements(By.XPATH, xpath)

            if des != None:
                for node in des:
                    if node.get_attribute("href") != None:
                        is_404_error(html_url, node.get_attribute("href"), file_path, log_file)
                    else:
                        if node.get_attribute("href") != None:
                            is_404_error(html_url, node.get_attribute("src"), file_path, log_file)

        except Exception:
            print("Something wrong with " + html_url)
            log_file.write("Something wrong with file: " + file_path + " : " + html_url)
        finally:
            i += 1


def verify_html_file_content(url, path, log_file):
    chromedriver = os.environ["chromedriver"]

    chrome_options = webdriver.ChromeOptions()
    chrome_options.add_argument('--headless')
    chrome_options.add_argument('--disable-gpu')
    chrome_options.add_argument('window-size=1920x1080')
    driver = webdriver.Chrome(executable_path=chromedriver, chrome_options=chrome_options)
    try:
        verify_html_file_link(driver, url, path, log_file)

    except:
        raise

    finally:
        driver.close()
        driver.quit()


if __name__ == '__main__':

    start_time = datetime.datetime.now()

    total_html_files_map = {
        "1": "https://www.agoda.com/zh-cn/country/china.html?cid=-38",
        "2": "https://www.booking.com/searchresults.html?aid=397647;label=bai408jc-index-XX-XX-XX-unspec-cn-com-L%3Axu-O%3AwindowsS10-B%3Achrome-N%3AXX-S%3Abo-U%3Ac-H%3As;sid=e066c0dc5a7db02a31ae39a76587ff80;city=-246227&;ilp=1;d_dcp=1",
        "3": "https://secure.booking.com/myreferrals.html?aid=397647;label=bai408jc-index-XX-XX-XX-unspec-cn-com-L%3Axu-O%3AwindowsS10-B%3Achrome-N%3AXX-S%3Abo-U%3Ac-H%3As;sid=e066c0dc5a7db02a31ae39a76587ff80;rafftl=1;source=34;",
        "4": "https://www.booking.com/hotel/jp/oakwood-premier-tokyo.html?aid=397647;label=bai408jc-index-XX-XX-XX-unspec-cn-com-L%3Axu-O%3AwindowsS10-B%3Achrome-N%3AXX-S%3Abo-U%3Ac-H%3As;sid=e066c0dc5a7db02a31ae39a76587ff80;dest_id=-246227;dest_type=city;dist=0;hapos=5;hpos=5;room1=A%2CA;sb_price_type=total;srepoch=1513924549;srfid=5eb7da030c321979554842f24c64bf54e3e3fcbfX5;srpvid=90612e624185032f;type=total;ucfs=1&#hotelTmpl",
        "5": "https://www.booking.com/hotel/jp/apa-hotel-shinjuku-gyoenmae.html?aid=397647;label=bai408jc-index-XX-XX-XX-unspec-cn-com-L%3Axu-O%3AwindowsS10-B%3Achrome-N%3AXX-S%3Abo-U%3Ac-H%3As;sid=e066c0dc5a7db02a31ae39a76587ff80;dest_id=-246227;dest_type=city;dist=0;hapos=6;hpos=6;room1=A%2CA;sb_price_type=total;srepoch=1513924549;srfid=5eb7da030c321979554842f24c64bf54e3e3fcbfX6;srpvid=90612e624185032f;type=total;ucfs=1&#hotelTmpl",
        "6": "https://www.booking.com/hotel/jp/hotel-unizo-ginza-itchome.html?aid=397647;label=bai408jc-index-XX-XX-XX-unspec-cn-com-L%3Axu-O%3AwindowsS10-B%3Achrome-N%3AXX-S%3Abo-U%3Ac-H%3As;sid=e066c0dc5a7db02a31ae39a76587ff80;dest_id=-246227;dest_type=city;dist=0;hapos=7;hpos=7;room1=A%2CA;sb_price_type=total;srepoch=1513924549;srfid=5eb7da030c321979554842f24c64bf54e3e3fcbfX7;srpvid=90612e624185032f;type=total;ucfs=1&#hotelTmpl",
        "7": "https://www.booking.com/hotel/jp/apa-sugamo-ekimae.html?aid=397647;label=bai408jc-index-XX-XX-XX-unspec-cn-com-L%3Axu-O%3AwindowsS10-B%3Achrome-N%3AXX-S%3Abo-U%3Ac-H%3As;sid=e066c0dc5a7db02a31ae39a76587ff80;dest_id=-246227;dest_type=city;dist=0;hapos=8;hpos=8;room1=A%2CA;sb_price_type=total;srepoch=1513924549;srfid=5eb7da030c321979554842f24c64bf54e3e3fcbfX8;srpvid=90612e624185032f;type=total;ucfs=1&#hotelTmpl",
    }

    thread_pool = multiprocessing.Pool(5)
    log_file = codecs.open("html_files_verification.log", 'w', 'utf-8')
    try:

        for key in total_html_files_map.keys():
            thread_pool.apply_async(verify_html_file_content, args=(total_html_files_map[key], key, log_file,))
        print('Waiting for all subprocesses done...')
        thread_pool.close()
        thread_pool.join()
    except Exception as e:
        print(e)
    finally:
        log_file.close()

    end_time = datetime.datetime.now()
    print("Duration: ", time.strftime('%H:%M:%S', time.gmtime((end_time - start_time).total_seconds())))
答案

通常,尝试从多个进程同时写入同一文件不是一个好主意。它可能会也可能不会起作用,具体取决于操作系统和实现细节。

在最好的情况下它是有效的。在最糟糕的情况下,您会以有趣的方式交叉来自不同进程的写入。

通过对代码进行最少量更改来解决此问题的方法是使用Lock对象保护日志文件。也就是说,在使用Lock之前创建一个apply_async对象,然后在argsapply_async中传递该锁定。在工作进程中,您在写入日志文件之前acquire Lock。写完后,刷新日志文件(使用reset方法)和release锁。这应该确保一次只有一个进程写入日志文件。

另一答案

在官方Python文档中有从多个进程登录到单个文件的配方:

https://docs.python.org/3/howto/logging-cookbook.html#logging-to-a-single-file-from-multiple-processes

否则,您可以在系统中安装并激活系统日志记录代理,并从SysLogHandler中使用它。

https://docs.python.org/3/library/logging.handlers.html#logging.handlers.SysLogHandler

另一答案

您可以考虑的另一个选项是每个线程的日志收集,然后将它们附加到一个中。我在基于python的UI测试框架中使用这种方法。我将所有线程事件保存在列表中,最后只将日志转储到文件中。所以基本上它是一个简单的整个执行列表。专家说

Python的内置结构本质上是线程安全的,锁定几乎不会增加任何开销,让您高枕无忧。

但即使我的跑步分布在20个线程中,我也从来没有遇到任何问题。性能相同,如果以所述方式使用这些集合,我没有发现任何显着的降级。

以上是关于如何使Python多处理池工作以写入相同的日志文件的主要内容,如果未能解决你的问题,请参考以下文章

Python中的分布式多处理池

python多处理池:我怎么知道池中的所有工作人员何时完成?

内存泄漏在哪里? python - 如何在python中的多处理期间使线程超时?

如何初始化具有共享状态的python多处理工作者池?

Python 多处理写入 csv 数据以获取大量文件

Python 多处理:最大。池工作进程的数量?