2017-12-19 123 views
8

Tôi có đôi chút mã này:Làm thế nào để prettify HTML để các thuộc tính thẻ sẽ vẫn còn trong một dòng duy nhất?

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)) 

Kết quả của đoạn mã trên ngay bây giờ là:

<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> 

Tôi muốn tìm ra cách để định dạng đầu ra để nó trở thành này thay vào đó:

<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> 

Nói cách khác, tôi muốn giữ các câu lệnh html như <tag attrib1=value1 attrib2=value2 ... attribn=valuen> trong một dòng nếu có thể. Khi tôi nói "nếu có thể", tôi có nghĩa là không tự sửa giá trị của các thuộc tính (value1, value2, ..., valuen).

Điều này có thể đạt được với beautifulsoup4 không? Theo tôi đã đọc trong các tài liệu có vẻ như bạn có thể sử dụng một tuỳ chỉnh formatter nhưng tôi không biết làm thế nào tôi có thể có một định dạng tùy chỉnh để nó có thể thực hiện các yêu cầu được mô tả.

EDIT:

@alecxe giải pháp khá đơn giản, không may thất bại trong một số trường hợp phức tạp hơn như hình dưới đây, ví dụ:

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' 

Trả lời

7

BeautifulSoup cố gắng giữ lại các dòng mới và nhiều khoảng trống bạn có trong t anh ta gán các giá trị trong HTML đầu vào.

Một workaround đây sẽ là để lặp qua các thuộc tính nguyên tố và làm sạch chúng trước khi prettifying - loại bỏ các dòng mới và thay thế nhiều không gian liên tiếp với một không gian duy nhất:

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

print(soup.prettify()) 

Prints:

<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> 

cập nhật (để giải quyết các đa giá trị thuộc tính như class):

Bạn chỉ cần thêm một sửa đổi nhỏ thêm xử lý đặc biệt đối với trường hợp khi một thuộc tính là một loại list:

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

Chấp nhận và tặng tiền thưởng ở đây vì những lý do tiếp theo: 1) Câu hỏi đề cập đến bs4 và câu hỏi này liên quan đến các yêu cầu 2) Số lượng upvotes từ mọi người và câu trả lời đầu tiên cho đất 3) @carlo chen answer không hoạt động , ví dụ: tidylib không phải là một gói tự chứa và nó đòi hỏi một số dlls bên ngoài. – BPL

4

Trong khi BeautifulSoup được thường được sử dụng , HTML Tidy có thể là lựa chọn tốt hơn nếu bạn đang làm việc với những điều kỳ quặc và có nhiều yêu cầu cụ thể hơn.

Sau khi cài đặt thư viện cho Python (pip install pytidylib) thử đoạn mã sau:

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 trả về một tuple với HTML và bất kỳ lỗi nào có thể xảy ra.Mã này sẽ xuất ra

<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> 

Bằng cách bỏ ghi chú "show-body-only": True cho mẫu thứ hai.

<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> 

Xem more configuration để có thêm tùy chọn và tùy chỉnh. Có các tùy chọn gói cụ thể cho các thuộc tính có thể hữu ích. Như bạn có thể thấy, các phần tử trống sẽ chỉ chiếm một dòng và html-gọn gàng sẽ tự động cố thêm các thứ như DOCTYPE, headtitle thẻ.

Các vấn đề liên quan