2017-12-19 3 views
8

Ich habe dieses kleine Stück Code:Wie wird HTML verschönert, damit die Tag-Attribute in einer einzigen Zeile bleiben?

text = """<html><head></head><body> 
    <h1 style=" 
    text-align: center; 
">Main site</h1> 
    <div> 
     <p style=" 
    color: blue; 
    text-align: center; 
">text1 
     </p> 
     <p style=" 
    color: blueviolet; 
    text-align: center; 
">text2 
     </p> 
    </div> 
    <div> 
     <p style="text-align:center"> 
      <img src="./foo/test.jpg" alt="Testing static images" style=" 
"> 
     </p> 
    </div> 
</body></html> 
""" 

import sys 
import re 
import bs4 


def prettify(soup, indent_width=4): 
    r = re.compile(r'^(\s*)', re.MULTILINE) 
    return r.sub(r'\1' * indent_width, soup.prettify()) 

soup = bs4.BeautifulSoup(text, "html.parser") 
print(prettify(soup)) 

Die Ausgabe des obigen Schnipsel jetzt ist:

<html> 
    <head> 
    </head> 
    <body> 
     <h1 style=" 
       text-align: center; 
"> 
      Main site 
     </h1> 
     <div> 
      <p style=" 
       color: blue; 
       text-align: center; 
"> 
       text1 
      </p> 
      <p style=" 
       color: blueviolet; 
       text-align: center; 
"> 
       text2 
      </p> 
     </div> 
     <div> 
      <p style="text-align:center"> 
       <img alt="Testing static images" src="./foo/test.jpg" style=" 
"/> 
      </p> 
     </div> 
    </body> 
</html> 

Ich möchte herausfinden, wie die Ausgabe zu formatieren, so dass es das wird statt:

<html> 
    <head> 
    </head> 
    <body> 
     <h1 style="text-align: center;"> 
      Main site 
     </h1> 
     <div> 
      <p style="color: blue;text-align: center;"> 
       text1 
      </p> 
      <p style="color: blueviolet;text-align: center;"> 
       text2 
      </p> 
     </div> 
     <div> 
      <p style="text-align:center"> 
       <img alt="Testing static images" src="./foo/test.jpg" style=""/> 
      </p> 
     </div> 
    </body> 
</html> 

Said sonst, würde Ich mag hTML-Anweisungen wie <tag attrib1=value1 attrib2=value2 ... attribn=valuen> in einer einzigen Zeile, wenn möglich zu halten. Wenn ich "wenn möglich" sage, meine ich, ohne den Wert der Attribute selbst zu verfälschen (Wert1, Wert2, ..., Wert).

Ist das mit beautifulsoup4 möglich? Soweit ich in den Dokumenten gelesen habe, scheint es, dass Sie eine benutzerdefinierte formatter verwenden können, aber ich weiß nicht, wie ich einen benutzerdefinierten Formatierer haben könnte, damit er die beschriebenen Anforderungen erfüllen kann.

EDIT:

@alecxe Lösung ist recht einfach, nicht leider in einigen komplexeren Fällen wie dem unten ein, das heißt:

test1 = """ 
<div id="dialer-capmaign-console" class="fill-vertically" style="flex: 1 1 auto;"> 
    <div id="sessionsGrid" data-columns="[ 
     { field: 'dialerSession.startTime', format:'{0:G}', title:'Start time', width:122 }, 
     { field: 'dialerSession.endTime', format:'{0:G}', title:'End time', width:122, attributes: {class:'tooltip-column'}}, 
     { field: 'conversationStartTime', template: cty.ui.gct.duration_dialerSession_conversationStartTime_endTime, title:'Duration', width:80}, 
     { field: 'dialerSession.caller.lastName',template: cty.ui.gct.person_dialerSession_caller_link, title:'Caller', width:160 }, 
     { field: 'noteType',template:cty.ui.gct.nameDescription_noteType, title:'Note type', width:150, attributes: {class:'tooltip-column'}}, 
     { field: 'note', title:'Note'} 
     ]"> 
</div> 
</div> 
""" 

from bs4 import BeautifulSoup 
import re 


def prettify(soup, indent_width=4, single_lines=True): 
    if single_lines: 
     for tag in soup(): 
      for attr in tag.attrs: 
       print(tag.attrs[attr], tag.attrs[attr].__class__) 
       tag.attrs[attr] = " ".join(
        tag.attrs[attr].replace("\n", " ").split()) 

    r = re.compile(r'^(\s*)', re.MULTILINE) 
    return r.sub(r'\1' * indent_width, soup.prettify()) 


def html_beautify(text): 
    soup = BeautifulSoup(text, "html.parser") 
    return prettify(soup) 

print(html_beautify(test1)) 

TRACEBACK:

dialer-capmaign-console <class 'str'> 
['fill-vertically'] <class 'list'> 
Traceback (most recent call last): 
    File "d:\mcve\x.py", line 35, in <module> 
    print(html_beautify(test1)) 
    File "d:\mcve\x.py", line 33, in html_beautify 
    return prettify(soup) 
    File "d:\mcve\x.py", line 25, in prettify 
    tag.attrs[attr].replace("\n", " ").split()) 
AttributeError: 'list' object has no attribute 'replace' 

Antwort

7

BeautifulSoup versucht, die Zeilenumbrüche und mehrere Leerzeichen zu erhalten, die Sie in t hatten Er Attribute Werte in der Eingabe HTML.

Eine Abhilfe wäre hier sein über die Elementattribute iterieren und reinigen zu Verhübsch-Schlechterung vor nach oben - die Zeilenumbrüche zu entfernen und mehrere aufeinander folgende Leerzeichen mit einem Leerzeichen ersetzen:

for tag in soup(): 
    for attr in tag.attrs: 
     tag.attrs[attr] = " ".join(tag.attrs[attr].replace("\n", " ").split()) 

print(soup.prettify()) 

Drucke:

<html> 
<head> 
</head> 
<body> 
    <h1 style="text-align: center;"> 
    Main site 
    </h1> 
    <div> 
    <p style="color: blue; text-align: center;"> 
    text1 
    </p> 
    <p style="color: blueviolet; text-align: center;"> 
    text2 
    </p> 
    </div> 
    <div> 
    <p style="text-align:center"> 
    <img alt="Testing static images" src="./foo/test.jpg" style=""/> 
    </p> 
    </div> 
</body> 
</html> 

aktualisieren (Adresse der mehrwertige Attribute wie class):

Sie brauchen nur eine leichte Modifikation Hinzufügen von Spezialbehandlung für den Fall hinzufügen, wenn ein Attribut eines list Typ ist:

for tag in soup(): 
    tag.attrs = { 
     attr: [" ".join(attr_value.replace("\n", " ").split()) for attr_value in value] 
       if isinstance(value, list) 
       else " ".join(value.replace("\n", " ").split()) 
     for attr, value in tag.attrs.items() 
    } 
+1

Akzeptieren und geben Kopfgeld hier für die nächsten Gründe: 1) Frage bezieht sich auf BS4 und diese bleibt an Anforderungen 2) Mehr Upvotes von Menschen und erste Antwort auf Land 3) @ Carlo chen Antwort funktioniert nicht aus der Box , dh: tidylib ist kein eigenständiges Paket und benötigt einige externe DLLs. – BPL

4

Während BeautifulSoup wird häufiger verwendet , HTML Tidy kann eine bessere Wahl sein, wenn Sie mit Macken arbeiten und spezifischere Anforderungen haben.

Nachdem die Bibliothek für Python Installation (pip install pytidylib) versuchen, den folgenden Code:

from tidylib import Tidy 
tidy = Tidy() 
# assign string to text 
config = { 
    "doctype": "omit", 
    # "show-body-only": True 
} 
print tidy.tidy_document(text, options=config)[0] 

tidy.tidy_document ein Tupel mit dem HTML zurückgibt und alle Fehler, die aufgetreten sind.Dieser Code gibt

<html> 
    <head> 
    <title></title> 
    </head> 
    <body> 
    <h1 style="text-align: center;"> 
     Main site 
    </h1> 
    <div> 
     <p style="color: blue; text-align: center;"> 
     text1 
     </p> 
     <p style="color: blueviolet; text-align: center;"> 
     text2 
     </p> 
    </div> 
    <div> 
     <p style="text-align:center"> 
     <img src="./foo/test.jpg" alt="Testing static images" style=""> 
     </p> 
    </div> 
    </body> 
</html> 

Durch die "show-body-only": True für die zweite Probe uncommenting.

<div id="dialer-capmaign-console" class="fill-vertically" style="flex: 1 1 auto;"> 
    <div id="sessionsGrid" data-columns="[ { field: 'dialerSession.startTime', format:'{0:G}', title:'Start time', width:122 }, { field: 'dialerSession.endTime', format:'{0:G}', title:'End time', width:122, attributes: {class:'tooltip-column'}}, { field: 'conversationStartTime', template: cty.ui.gct.duration_dialerSession_conversationStartTime_endTime, title:'Duration', width:80}, { field: 'dialerSession.caller.lastName',template: cty.ui.gct.person_dialerSession_caller_link, title:'Caller', width:160 }, { field: 'noteType',template:cty.ui.gct.nameDescription_noteType, title:'Note type', width:150, attributes: {class:'tooltip-column'}}, { field: 'note', title:'Note'} ]"></div> 
</div> 

Siehe more configuration für weitere Möglichkeiten und Anpassung. Es gibt spezielle Wrapping-Optionen für Attribute, die hilfreich sein können. Wie Sie sehen können, nehmen leere Elemente nur eine Zeile ein und html-tidy wird automatisch versuchen, Dinge wie DOCTYPE, head und title Tags hinzuzufügen.

Verwandte Themen