【问题标题】:Optimize Python Script to parse xml优化 Python Script 解析 xml
【发布时间】:2015-10-20 03:59:09
【问题描述】:

我正在使用 Python 和 Beautifulsoup 解析美国专利 XML 文件(从 Google patent dumps 下载);解析后的数据导出到MYSQL数据库。

每年的数据包含接近 200-300K 的专利 - 这意味着解析 200-300K xml 文件。

我运行 python 脚本的服务器非常强大 - 16 个内核、160 GB 的 RAM 等,但解析一年的数据仍然需要将近 3 天的时间。

自 2 年以来我一直在学习和使用 python - 所以我可以完成一些事情,但不知道如何以最有效的方式完成它。我正在阅读它。

如何优化以下脚本以使其高效?

任何指导将不胜感激。

下面是代码:

from bs4 import BeautifulSoup
import pandas as pd
from pandas.core.frame import DataFrame
import MySQLdb as db
import os

cnxn = db.connect('xx.xx.xx.xx','xxxxx','xxxxx','xxxx',charset='utf8',use_unicode=True)

def separated_xml(infile):
    file = open(infile, "r")
    buffer = [file.readline()]
    for line in file:
        if line.startswith("<?xml "):
            yield "".join(buffer)
            buffer = []
        buffer.append(line)
    yield "".join(buffer)
    file.close()

def get_data(soup):
    df = pd.DataFrame(columns = ['doc_id','patcit_num','patcit_document_id_country', 'patcit_document_id_doc_number','patcit_document_id_kind','patcit_document_id_name','patcit_document_id_date','category'])
    if soup.findAll('us-citation'):
        cit = soup.findAll('us-citation')
    else:
        cit = soup.findAll('citation')
    doc_id = soup.findAll('publication-reference')[0].find('doc-number').text
    for x in cit:
        try:
            patcit_num = x.find('patcit')['num']
        except:
            patcit_num = None
        try:
            patcit_document_id_country = x.find('country').text
        except:
            patcit_document_id_country = None   
        try:     
            patcit_document_id_doc_number = x.find('doc-number').text
        except: 
            patcit_document_id_doc_number = None
        try:
            patcit_document_id_kind = x.find('kind').text
        except:
            patcit_document_id_kind = None
        try:
            patcit_document_id_name = x.find('name').text
        except:
            patcit_document_id_name = None
        try: 
            patcit_document_id_date = x.find('date').text
        except:
            patcit_document_id_date = None
        try:
            category = x.find('category').text
        except:
            category = None
        print doc_id
        val = {'doc_id':doc_id,'patcit_num':patcit_num, 'patcit_document_id_country':patcit_document_id_country,'patcit_document_id_doc_number':patcit_document_id_doc_number, 'patcit_document_id_kind':patcit_document_id_kind,'patcit_document_id_name':patcit_document_id_name,'patcit_document_id_date':patcit_document_id_date,'category':category}    
        df = df.append(val, ignore_index=True)
    df.to_sql(name = 'table_name', con = cnxn, flavor='mysql', if_exists='append')
    print '1 doc exported'

i=0

l = os.listdir('/path/')
for item in l:
    f = '/path/'+item
    print 'Currently parsing - ',item
    for xml_string in separated_xml(f):
        soup = BeautifulSoup(xml_string,'xml')
        if soup.find('us-patent-grant'):
            print item, i, xml_string[177:204]          
            get_data(soup)
        else:
            print item, i, xml_string[177:204],'***********************************soup not found********************************************'
        i+=1
print 'DONE!!!'

【问题讨论】:

  • 使用分析器在小型数据集上运行它,以查看您的问题所在。 docs.python.org/2/library/profile.html 另外,删除所有 try/except 子句并正确检查。捕捉错误是昂贵的。那东西多久打印一次控制台?每个文件一次? Ún缓冲打印也很昂贵,如果可能的话,打印更少。您还可以并行化程序以在更多线程上运行。这只会在一个核心、一个线程上运行。
  • 是的,我正在研究如何让它使用所有的 CPU。我正在使用 nohup 运行脚本,以便将输出写入“nohup.out”文件。另外,我需要检查该特定值是否存在,因为有数百万条记录,如果该值不存在,脚本可能会崩溃;除非有更好的检查方法
  • 也许切换到lxml.html?有人体验到BeautifulSouplxml.html 慢很多,f.e : blog.dispatched.ch/2010/08/16/beautifulsoup-vs-lxml-performance

标签: python mysql xml optimization beautifulsoup


【解决方案1】:

这是tutorial on multi-threading,因为目前该代码将在 1 个线程、1 个内核上运行。

删除所有 try/except 语句并正确处理代码。例外是昂贵的。

运行profiler 来查找阻塞点,然后多线程处理这些阻塞点或找到一种方法来减少它们的次数。

【讨论】:

  • 16 个线程(或 16 个单独的作业,每个作业执行 1/16 的文件)的运行速度将提高近 16 倍——可能每“年”5 小时。
  • 我已经阅读了多线程教程,但我仍然对如何将它应用到我的场景感到困惑。 @rickjames - 我可以获取文件夹中的文件列表并将它们分成 16 个线程进行解析;如何告诉程序并行运行这些线程?
  • 它们是线程,如果有足够的可用资源,它们总是并行运行。只要他们没有被阻止等待相同的资源。另一件事。我不知道你的文件有多大,但你可能想读入,比如说,160 个文件并在它们上运行,然后再读入另外 160 个文件。磁盘访问很费时间
  • 总共 53 个文件 - 每个包含近 4000 个单独的 xml 文件的连接文本 - 基本上程序读取每个大文件,将其拆分为单独的 xml 文件并解析 xml。如果我将整个任务分成 10 个线程,我会为每个线程分配 5 个文件。
  • 只有 53 个? (对比 16。)简单地产生 53 个线程(或进程)并让它们竞争资源可能更简单。这种方法的关键资源是 RAM。 “交换”会减慢速度并使这种方法不好。 (CPU 和/或 I/O 饱和不会成为问题。)
猜你喜欢
  • 2019-10-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多