批量修改复杂的文本文件

时间:2019-06-14 19:44:48

标签: javascript php python html

上下文:

我继承了一个包含2.5个MLOC的旧版应用程序,该MLOC分布在大约2,000个文件中(当前PHP 5.5.9,jQuery,HTML)。在此代码库中,有许多安全漏洞,代码气味和性能问题。我的任务是改善信息安全,并将此代码库带入21世纪。我正在使用Python进行这些批量编辑。

问题:

我需要编辑每个文件并修改代码。我已经通过基本的字符串比较解决了单行编辑问题。

这是典型的脚本结构:

<?php

// some code here

?>

<html><head>
...
</head>
<body>
...
</body>
<script>
...
</script>
</html>

现在,我需要开始修改HTML的整个块,使用混合引号(单行和双引号)的内联javascript,并从脚本中删除方法。

例如,我有一个HTML的开头部分,如下所示:

<head>
    <meta http-equiv="X-UA-Compatible" content="IE=9" />
    <script type="text/javascript" src="jquery.form.js"></script>

    <title>TITLE</title>
    <!-- TemplateIB -->
    <link rel="Stylesheet" href="../Lib/jquery-ui-1.10.3.custom.min.css" />
    <link rel="Stylesheet" href="../Lib/3CLStyle.css" />
    <script language="JavaScript" type="text/javascript" src="../Lib/jquery-1.9.1-Combined.min.js"></script>

    <link rel="stylesheet" type="text/css" href="../Lib/jquery.datetimepicker.css" />
    <script src="../Lib/jquery.datetimepicker.full.js"></script>
</head>

我需要将其修改为:

<head>
    <meta http-equiv="X-UA-Compatible" content="IE=9" />
    <title>TITLE</title>

    <!-- Stylesheets -->
    <link rel="stylesheet" href="../Lib/jquery-ui-1.10.3.custom.min.css" />
    <link rel="stylesheet" type="text/css" href="../Lib/jquery.datetimepicker.css" />
    <link rel="stylesheet" href="../Lib/3CLStyle.css" />

    <!-- Boilerplate JS -->
    <script type="text/javascript" src="../Lib/jquery-1.9.1-Combined.min.js"></script>
    <script type="text/javascript" src="jquery.form.js"></script>
    <script type="text/javascript" src="../Lib/jquery.datetimepicker.full.js"></script>

    <!-- Custom JS -->
    <script type="text/javascript" src="../js/contactHistory.js"></script>
</head>

这里最大的障碍是,换行符并不是都统一的-该代码库已有十多年的历史,并且有很多人在摸索它。因此不存在统一性。例如,某些文件如下所示:

<head><meta http-equiv="X-UA-Compatible" content="IE=9" />
<title>
...

其他类似上面的示例。一些使用制表符缩进,另一些使用空格。空白的应用方式有很多差异。

我看到的另一个障碍的另一个例子是这样的情况:

<input type="button" class="submit" value="Check History" onclick="CreateHistoryTable()" />

我需要将其修改为:

<input type="button" class="submit" value="Check History" onclick="createHistoryTable(' <?= $_GET['phone'] ? $_GET['phone'] : ''; ?>', '<?= $_GET['project'] ? $_GET['project'] : ''; ?>' )" />

潜在的解决方案:

就像我说的那样,我已经有效地解决了单行编辑问题。对于这些更复杂的场景,我已经考虑过RegEx,但这给本来就很困难的情况又增加了另一层复杂性。

根据我的阅读,看来Beautiful Soup可能满足我的需求。有人可以建议吗?元素树也是另一种选择,我只是没有深入研究这两种选择,以了解它们是否会促进我需要做的重组和深度编辑。

现在,解决空格问题的一种方法是美化HTML,并使其统一。看来lxml有一个基于Beautiful Soup的很好的实现,它并不关心白间距。

下一步是块编辑-这确实是目前最大的障碍。 lxml可能是实现此目的的关键,但是我很少能找到能实现我所要实现的示例代码。

我不是在寻找任何人来编写代码,我只是在寻找有关如何处理这些更复杂案例的输入。谢谢您的协助。

1 个答案:

答案 0 :(得分:1)

首先,不要编写一个一次性完成所有任务的大型脚本。编写许多较小的脚本来修复一些问题。较小的将更易于编码,调试和维护。

这是使用BeautifulSoup的方法。

from bs4 import BeautifulSoup, Comment, NavigableString
from collections import defaultdict

html = '''
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=9" />
    <script type="text/javascript" src="jquery.form.js"></script>
    <script type="text/javascript" src="../js/contactHistory.js"></script>

    <title>TITLE</title>
    <!-- TemplateIB -->
    <link rel="Stylesheet" href="../Lib/jquery-ui-1.10.3.custom.min.css" />
    <link rel="Stylesheet" href="../Lib/3CLStyle.css" />
    <script language="JavaScript" type="text/javascript" src="../Lib/jquery-1.9.1-Combined.min.js"></script>

    <link rel="stylesheet" type="text/css" href="../Lib/jquery.datetimepicker.css" />
    <script src="../Lib/jquery.datetimepicker.full.js"></script>
</head>
'''

soup = BeautifulSoup(html)

# some way to distinguish the js libraries
boilerplate_js = [
    "../Lib/jquery-1.9.1-Combined.min.js",
    "jquery.form.js",
    "../Lib/jquery.datetimepicker.full.js"
 ]

tags = defaultdict(list)

# extract all the tags in the <head>...</head> element and
# put them under various keys in the tags dictionary
for tag in list(soup.html.head):
    if tag.name is not None:
        tag = tag.extract()

        # differentiate 'custom' scripts from 'boilerplate'
        if tag.name == 'script' and tag['src'] not in boilerplate_js:
            tags['custom_js'].append(tag)

        else:
            tags[tag.name].append(tag)

    # comments (and some other things) don't have tag names
    elif isinstance(tag, Comment):
        tags['Comment'].append(tag)

    # elif .... ignoring other possible tags

#create a new <head>...</head> element to be populated below        
new_head = soup.new_tag('head')

# for each kind of tag in the tags dictionary
# we append them to the new_head element
# appending '\n's and ' 's is just for formatting
#obviously, this can be refactored alot
if 'meta' in tags:
    for tag in tags.pop('meta'):
        new_head.append('\n  ')
        new_head.append(tag)

if 'title' in tags:
    new_head.append('\n  ')
    for tag in tags.pop('title'):
        new_head.append('\n  ')
        new_head.append(tag)

if 'link' in tags:
    new_head.append('\n\n  ')
    new_head.append(Comment(' Stylesheets '))
    for tag in tags.pop('link'):
        new_head.append('\n  ')
        new_head.append(tag)

if 'script' in tags:
    new_head.append('\n\n  ')
    new_head.append(Comment(' Boilerplate JS '))
    for tag in tags.pop('script'):
        new_head.append('\n  ')
        new_head.append(tag)

if 'custom_js' in tags:
    new_head.append('\n\n  ')
    new_head.append(Comment(' Custom JS '))
    for tag in tags.pop('custom_js'):
        new_head.append('\n  ')
        new_head.append(tag)

if len(tags):
    new_head.append('\n\n  ')
    new_head.append(Comment(' Other stuff '))
    for key in list(tags.keys()):
        for tag in tags.pop(key):
            new_head.append('\n  ')
            new_head.append(tag)

new_head.append('\n')

soup.html.head.replace_with(new_head)
print(soup)

输出:

<html><head>
  <meta content="IE=9" http-equiv="X-UA-Compatible"/>

  <title>TITLE</title>

  <!-- Stylesheets -->
  <link href="../Lib/jquery-ui-1.10.3.custom.min.css" rel="Stylesheet"/>
  <link href="../Lib/3CLStyle.css" rel="Stylesheet"/>
  <link href="../Lib/jquery.datetimepicker.css" rel="stylesheet" type="text/css"/>

  <!-- Boilerplate JS -->
  <script src="jquery.form.js" type="text/javascript"></script>
  <script language="JavaScript" src="../Lib/jquery-1.9.1-Combined.min.js" type="text/javascript"></script>
  <script src="../Lib/jquery.datetimepicker.full.js"></script>

  <!-- Custom JS -->
  <script src="../js/contactHistory.js" type="text/javascript"></script>

  <!-- Other stuff -->
  <!-- TemplateIB -->
</head>
</html>