Есть ли способы оптимизировать разбор Beautiful Soup для большого количества файлов? - PullRequest
0 голосов
/ 28 апреля 2020

Я загрузил весь Центральный архив PubMed для интеллектуального анализа текста и для предварительной обработки. Я анализирую его до JSON, чтобы уменьшить его размер и исключить любую информацию, которая не имеет отношения к моему делу или является трудной для меня, например, библиография. Однако размер всего документа составляет 25 ГБ, и в настоящее время ETA составляет около 50 часов. Ниже мой Python скрипт для этого. Я уже пробовал многопроцессорность, которая увеличивала скорость примерно в 3 раза. Кроме того, я проверил со временем и обнаружил, что узкое место (около 90% времени выполнения) относится к строке tree = BS(f.read(), features='lxml-xml'), поэтому я не думаю, что регулярное выражение является проблемой. Есть ли другие способы увеличить скорость?

import glob
import json
import multiprocessing as mp
import os
import re

from bs4 import BeautifulSoup as BS
from tqdm import tqdm

skipped = 0
files = tuple(glob.iglob(r'*\*.nxml'))
pbar = tqdm(total=len(files))

def xml2json(filename, logging=False):
    if logging:
        tqdm.write("Now parsing {}".format(filename))

    with open(filename, 'r', encoding='utf-8') as f:
        # start = time.time()
        tree = BS(f.read(), features='lxml-xml')
        # print("elapsed time " + str(time.time() - start))

    dic = {
        'publisher': {
            'name': "",  # tree.find('publisher-name').text,
            'loc': "",  # tree.find('publisher-loc').text
        },
        "id": tree.find('article-id').text,
        'title': tree.find('article-title').text.strip(),
        'contributors': [],
        "pub": {
            "volume": "",
            "issue": "",
            "day": "",
            "month": "",
            "year": "",
        },
        "abstract": "",
        "body": "",
        "body_headings": ""
    }

    # start = time.time()

    for tag in ("volume", "issue", "day", "month", "year"):
        node = tree.find(tag)
        if node:
            dic["pub"][tag] = node.text

    node = tree.find('publisher-name')
    if node:
        dic["publisher"]["publisher-name"] = node.text

    node = tree.find('publisher-loc')
    if node:
        dic["publisher"]["publisher-loc"] = node.text

    contributors = []
    branch = tree.find("contrib-group")
    if branch:
        for node in branch.findAll("contrib"):
            contributors.append("{}, {}".format(node.find("surname").text, node.find("given-names").text))
        dic["contributors"] = contributors

    abstract = ""
    branch = tree.find("abstract")
    if not branch:
        return None

    for node in branch.find_all(["p"]):
        if node.text == "Supporting Information":
            break
        text = "\n" + node.text.replace("\n", "").strip()
        text = re.sub("[\(\[].*?[\)\]]", "", text)
        text = re.sub(" {2,}", " ", text)
        text = re.sub(" \.", ".", text)
        abstract += text
    dic["abstract"] = abstract

    body = ""
    body_headings = ""
    branch = tree.find("body")
    if not branch:
        return None
    for node in branch.find_all(["title", "p"]):
        if node.text == "Supporting Information":
            break
        if node.name == "title":
            text = "\n"
        else:
            text = ""
        text += "\n" + node.text.replace("\n", "").strip()
        text = re.sub("[\(\[].*?[\)\]]", "", text)
        text = re.sub(" {2,}", " ", text)
        text = re.sub(" (\.|\,)", "\g<1>", text)
        body_headings += text
        if node.name == "p":
            body += text

    dic["body"] = body
    dic["body_headings"] = body_headings

    # print(time.time() - start)

    return dic

def parse(file):
    _, name = os.path.split(file)
    name, _ = os.path.splitext(name)
    with open("json/{}.json".format(name[3:]), "w") as f:
        dic = xml2json(file, logging=False)
        if dic:
            json.dump(dic, f)
        else:
            global skipped
            skipped += 1
            # tqdm.write("Skipping!")

def callback(m):
    # print(m)
    pbar.update(1)

def error_callback(e):
    print(e)


if __name__ == '__main__':
    tqdm.write("Found {} files...".format(len(files)))
    pool = mp.Pool()

    for filepath in files:
        pool.apply_async(parse, (filepath,), callback=callback, error_callback=error_callback)

    pool.close()
    pool.join()
    pbar.close()

    print("Done, skipped {}".format(skipped))

1 Ответ

0 голосов
/ 29 апреля 2020

BeautifulSoup выполняет анализ в python, что не так эффективно, как C. Кроме того, html является более грязным, чем XML, что увеличивает нагрузку на синтаксический анализ. Я считаю, что n XML файлов полностью соответствуют XML, поэтому парсер l xml на основе C должен быть намного быстрее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...