прогнал через скрипт список ссылок и источников, часть уже оформил правильно

This commit is contained in:
2026-05-31 22:53:56 +03:00
parent 8508150b07
commit 56d8bce9c3
2 changed files with 160 additions and 129 deletions
Regular → Executable
+122 -91
View File
@@ -1,15 +1,22 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import re import re
import shutil
import tempfile import tempfile
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
from pathlib import Path from pathlib import Path
from zipfile import ZipFile from zipfile import ZipFile
from urllib.parse import urlsplit, urlunsplit
import requests
from bs4 import BeautifulSoup
INPUT_DOCX = "Отчет.docx"
OUTPUT_DOCX = "Отчет_fixed.docx"
FIRST_NEW_SOURCE = 8 FIRST_NEW_SOURCE = 8
ACCESS_DATE = "30.05.2026"
NS = { NS = {
"w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main" "w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
@@ -22,30 +29,75 @@ def normalize_url(url):
if "#:~:text=" in url: if "#:~:text=" in url:
url = url.split("#:~:text=", 1)[0] url = url.split("#:~:text=", 1)[0]
url = url.rstrip("/") parts = urlsplit(url)
return urlunsplit((
parts.scheme.lower(),
parts.netloc.lower(),
parts.path.rstrip("/"),
"",
""
))
def get_title(url):
try:
response = requests.get(
url,
timeout=15,
headers={
"User-Agent": "Mozilla/5.0"
}
)
response.raise_for_status()
soup = BeautifulSoup(response.text, "html.parser")
og = soup.find("meta", property="og:title")
if og and og.get("content"):
return " ".join(og["content"].split())
meta = soup.find("meta", attrs={"name": "title"})
if meta and meta.get("content"):
return " ".join(meta["content"].split())
if soup.title and soup.title.string:
return " ".join(soup.title.string.split())
except Exception as e:
print(f"WARNING: {url}: {e}")
return url return url
def build_url_mapping(root): def main():
""" with tempfile.TemporaryDirectory() as tmpdir:
Первый проход.
Собираем все уникальные URL и tmpdir = Path(tmpdir)
присваиваем им номера.
""" with ZipFile(INPUT_DOCX) as archive:
archive.extractall(tmpdir)
document_xml = tmpdir / "word" / "document.xml"
tree = ET.parse(document_xml)
root = tree.getroot()
url_to_index = {} url_to_index = {}
index_to_url = {}
next_index = FIRST_NEW_SOURCE next_index = FIRST_NEW_SOURCE
#
# Первый проход:
# собираем все URL и выдаем номера
#
for node in root.findall(".//w:instrText", NS): for node in root.findall(".//w:instrText", NS):
text = "".join(node.itertext()) text = "".join(node.itertext())
match = re.search( match = re.search(r'HYPERLINK\s+"([^"]+)"', text)
r'HYPERLINK\s+"([^"]+)"',
text
)
if not match: if not match:
continue continue
@@ -56,26 +108,24 @@ def build_url_mapping(root):
continue continue
url_to_index[url] = next_index url_to_index[url] = next_index
index_to_url[next_index] = url
next_index += 1 next_index += 1
return url_to_index print("\n=== URL -> INDEX ===\n")
for index in sorted(index_to_url):
print(f"[{index}] {index_to_url[index]}")
def patch_document(root, url_to_index): print()
"""
Второй проход.
Находим:
HYPERLINK "..."
Затем ближайший следующий
<w:t>[старый номер]</w:t>
И меняем его на новый.
"""
#
# Второй проход:
# меняем номера ссылок
#
current_url = None current_url = None
waiting_for_visible_text = False waiting_for_text = False
replaced_count = 0
for elem in root.iter(): for elem in root.iter():
@@ -91,74 +141,41 @@ def patch_document(root, url_to_index):
) )
if match: if match:
current_url = normalize_url( current_url = normalize_url(match.group(1))
match.group(1) waiting_for_text = True
)
waiting_for_visible_text = True
continue continue
if ( if not waiting_for_text:
waiting_for_visible_text continue
and tag.endswith("t")
and elem.text
and re.match(r"\[\d+\]", elem.text.strip())
):
index = url_to_index[current_url]
old = elem.text if not tag.endswith("t"):
elem.text = f"[{index}]" continue
if elem.text is None:
continue
if not re.match(r"\[\d+\]", elem.text.strip()):
continue
new_index = url_to_index[current_url]
old_text = elem.text
elem.text = f"[{new_index}]"
print( print(
f"{old} -> {elem.text} : {current_url}" f"{old_text} -> {elem.text} : {current_url}"
) )
waiting_for_visible_text = False replaced_count += 1
waiting_for_text = False
current_url = None current_url = None
print()
def main(): print(f"Replaced links: {replaced_count}")
input_docx = Path("Отчет.docx") print()
output_docx = Path(
input_docx.stem + "_fixed.docx"
)
with tempfile.TemporaryDirectory() as tmp:
tmp = Path(tmp)
with ZipFile(input_docx) as archive:
archive.extractall(tmp)
document_xml = (
tmp /
"word" /
"document.xml"
)
tree = ET.parse(document_xml)
root = tree.getroot()
url_to_index = build_url_mapping(root)
print("\nURL -> INDEX\n")
for url, index in url_to_index.items():
print(
f"[{index}] {url}"
)
print(
f"\nFound {len(url_to_index)} unique sources\n"
)
patch_document(
root,
url_to_index
)
tree.write( tree.write(
document_xml, document_xml,
@@ -166,23 +183,36 @@ def main():
xml_declaration=True xml_declaration=True
) )
with ZipFile( with ZipFile(OUTPUT_DOCX, "w") as out_zip:
output_docx,
"w"
) as out_zip:
for file in tmp.rglob("*"): for file in tmpdir.rglob("*"):
if file.is_dir(): if file.is_dir():
continue continue
out_zip.write( out_zip.write(
file, file,
file.relative_to(tmp) file.relative_to(tmpdir)
) )
print(f"Saved: {OUTPUT_DOCX}")
#
# Формируем список литературы
#
print("\n=== SOURCES ===\n")
for index in sorted(index_to_url):
url = index_to_url[index]
title = get_title(url)
print( print(
f"\nSaved: {output_docx}" f"{index}. "
f"{title} "
f"(дата обращения: {ACCESS_DATE}). "
f"Режим доступа - {url}"
) )
@@ -190,3 +220,4 @@ if __name__ == "__main__":
main() main()
BIN
View File
Binary file not shown.