gfps: 添加认证测试和FAQ 20260505
All checks were successful
Sync To /home/ubuntu/gfps / sync (push) Successful in 2s
All checks were successful
Sync To /home/ubuntu/gfps / sync (push) Successful in 2s
Change-Id: I71e676b997159a141ca07d16f82a73b45f4b2410
This commit is contained in:
145
convert_docs.py
Normal file
145
convert_docs.py
Normal file
@@ -0,0 +1,145 @@
|
||||
import os
|
||||
import glob
|
||||
import email
|
||||
from email import policy
|
||||
import subprocess
|
||||
import urllib.parse
|
||||
from bs4 import BeautifulSoup
|
||||
import uuid
|
||||
|
||||
def create_dirs():
|
||||
os.makedirs('convert_md/images', exist_ok=True)
|
||||
|
||||
def clean_filename(name):
|
||||
# remove .doc extension
|
||||
if name.endswith('.doc'):
|
||||
name = name[:-4]
|
||||
return name
|
||||
|
||||
def process_file(filepath):
|
||||
print(f"Processing: {filepath}")
|
||||
filename = os.path.basename(filepath)
|
||||
base_name = clean_filename(filename)
|
||||
|
||||
with open(filepath, 'rb') as f:
|
||||
msg = email.message_from_bytes(f.read(), policy=policy.default)
|
||||
|
||||
html_content = ""
|
||||
images = {}
|
||||
|
||||
# Extract parts
|
||||
if msg.is_multipart():
|
||||
for part in msg.walk():
|
||||
ctype = part.get_content_type()
|
||||
if ctype == 'text/html':
|
||||
html_content = part.get_payload(decode=True).decode(part.get_content_charset() or 'utf-8', errors='ignore')
|
||||
elif ctype.startswith('image/') or ctype == 'application/octet-stream':
|
||||
cloc = part.get("Content-Location")
|
||||
cid = part.get("Content-ID")
|
||||
|
||||
if not cloc and not cid:
|
||||
continue
|
||||
|
||||
img_data = part.get_payload(decode=True)
|
||||
if not img_data:
|
||||
continue
|
||||
|
||||
# guess extension
|
||||
ext = 'png'
|
||||
if img_data.startswith(b'\xff\xd8\xff'): ext = 'jpg'
|
||||
elif img_data.startswith(b'GIF8'): ext = 'gif'
|
||||
|
||||
safe_img_name = f"{base_name}_{uuid.uuid4().hex[:8]}.{ext}"
|
||||
|
||||
img_save_path = os.path.join('convert_md', 'images', safe_img_name)
|
||||
with open(img_save_path, 'wb') as img_f:
|
||||
img_f.write(img_data)
|
||||
|
||||
if cloc:
|
||||
images[cloc.strip()] = f"images/{safe_img_name}"
|
||||
if cid:
|
||||
clean_cid = cid.strip('<> \r\n')
|
||||
images[f"cid:{clean_cid}"] = f"images/{safe_img_name}"
|
||||
|
||||
if not html_content:
|
||||
print(f" No HTML content found in {filepath}. Skipping.")
|
||||
return
|
||||
|
||||
# Parse HTML and fix image links
|
||||
soup = BeautifulSoup(html_content, 'html.parser')
|
||||
|
||||
# Try to find the actual content wrapper to skip headers/footers
|
||||
wiki_content = soup.find(class_='wiki-content')
|
||||
if wiki_content:
|
||||
content_to_convert = str(wiki_content)
|
||||
else:
|
||||
# fallback to body
|
||||
body = soup.find('body')
|
||||
content_to_convert = str(body) if body else html_content
|
||||
|
||||
# Re-parse to fix images in the scoped content
|
||||
content_soup = BeautifulSoup(content_to_convert, 'html.parser')
|
||||
for img in content_soup.find_all('img'):
|
||||
src = img.get('src')
|
||||
if not src:
|
||||
continue
|
||||
|
||||
src = src.strip()
|
||||
|
||||
# Check if the src matches any of our extracted images
|
||||
matched = False
|
||||
for loc, new_path in images.items():
|
||||
if src == loc or src.endswith(loc) or loc.endswith(src):
|
||||
img['src'] = new_path
|
||||
matched = True
|
||||
break
|
||||
|
||||
if not matched:
|
||||
unquoted = urllib.parse.unquote(src)
|
||||
for loc, new_path in images.items():
|
||||
if unquoted == loc or unquoted.endswith(loc) or loc.endswith(unquoted):
|
||||
img['src'] = new_path
|
||||
matched = True
|
||||
break
|
||||
|
||||
# Remove some common Confluence boilerplate if any
|
||||
for el in content_soup.find_all(class_=['aui-message', 'table-wrap', 'confluence-embedded-file-wrapper', 'Section1']):
|
||||
if el.name in ('div', 'span'):
|
||||
el.unwrap()
|
||||
|
||||
# Clean up img tags to just src and alt so pandoc converts them to pure markdown images
|
||||
for img in content_soup.find_all('img'):
|
||||
src = img.get('src', '')
|
||||
alt = img.get('alt', '')
|
||||
img.attrs = {'src': src, 'alt': alt}
|
||||
|
||||
cleaned_html = str(content_soup)
|
||||
|
||||
# Convert HTML to Markdown using Pandoc
|
||||
# We use markdown_strict or gfm (GitHub Flavored Markdown) for good table support
|
||||
try:
|
||||
result = subprocess.run(
|
||||
['pandoc', '-f', 'html', '-t', 'gfm', '--wrap=none'],
|
||||
input=cleaned_html.encode('utf-8'),
|
||||
capture_output=True,
|
||||
check=True
|
||||
)
|
||||
md_content = result.stdout.decode('utf-8')
|
||||
|
||||
md_filepath = os.path.join('convert_md', f"{base_name}.md")
|
||||
with open(md_filepath, 'w', encoding='utf-8') as f:
|
||||
f.write(md_content)
|
||||
print(f" Successfully converted to {md_filepath}")
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f" Pandoc conversion failed for {filepath}: {e.stderr.decode('utf-8', errors='ignore')}")
|
||||
|
||||
|
||||
def main():
|
||||
create_dirs()
|
||||
files = glob.glob('*.doc')
|
||||
for f in files:
|
||||
process_file(f)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user