[PARSING] urlparss.py
Сущности
#!/usr/bin/env python3<br>
# -*- coding: utf-8 -*-<br>
<br>
import sys<br>
import time<br>
import re<br>
import requests<br>
from pathlib import Path<br>
from playwright.sync_api import sync_playwright<br>
from newspaper import Article<br>
from youtube_transcript_api import (<br>
YouTubeTranscriptApi, <br>
TranscriptsDisabled, <br>
NoTranscriptFound, <br>
VideoUnavailable<br>
)<br>
from pytube import YouTube<br>
from bs4 import BeautifulSoup<br>
<br>
# Попытка импортировать Readability (readability-lxml)<br>
try:<br>
from readability import Document<br>
except ImportError:<br>
print("Установите readability-lxml (pip install readability-lxml)")<br>
sys.exit(1)<br>
<br>
# -----------------------------------------------------------------------------<br>
# Альтернативное извлечение с помощью Readability<br>
# -----------------------------------------------------------------------------<br>
def extract_with_readability(html):<br>
try:<br>
doc = Document(html)<br>
title = doc.title() # извлекает заголовок<br>
summary_html = doc.summary() # извлекает HTML основного контента<br>
soup = BeautifulSoup(summary_html, "html.parser")<br>
text = soup.get_text(separator="\n").strip()<br>
return title, text<br>
except Exception as e:<br>
print("Ошибка извлечения через Readability:", e)<br>
return "", ""<br>
<br>
# -----------------------------------------------------------------------------<br>
# Базовое извлечение статьи с использованием Newspaper3k и альтернативных методов<br>
# -----------------------------------------------------------------------------<br>
def extract_article_info(html, url):<br>
# Определяем язык страницы по атрибуту <html lang="..."><br>
soup = BeautifulSoup(html, "html.parser")<br>
html_tag = soup.find("html")<br>
language = "ru"<br>
if html_tag and html_tag.has_attr("lang"):<br>
lang = html_tag["lang"].lower()<br>
if lang.startswith("en"):<br>
language = "en"<br>
elif lang.startswith("kk"):<br>
language = "ru"<br>
elif lang.startswith("ru"):<br>
language = "ru"<br>
else:<br>
language = lang<br>
if "reuters.com" in url:<br>
language = "en"<br>
<br>
# Попытка извлечь данные с помощью Newspaper3k<br>
article = Article(url=url, language=language)<br>
article.download(input_html=html)<br>
article.parse()<br>
data = {<br>
"title": article.title.strip() if article.title else "",<br>
"authors": article.authors or [],<br>
"publish_date": str(article.publish_date).strip() if article.publish_date else "",<br>
"text": article.text.strip() if article.text else ""<br>
}<br>
# Попытка извлечь метаданные из meta-тегов<br>
meta_title = soup.find("meta", property="og:title")<br>
if meta_title and meta_title.get("content"):<br>
meta_title_val = meta_title["content"].strip()<br>
if meta_title_val.lower() not in ["reuters.com", ""]:<br>
data["title"] = meta_title_val<br>
meta_date = soup.find("meta", property="article:published_time")<br>
if meta_date and meta_date.get("content"):<br>
data["publish_date"] = meta_date["content"].strip()<br>
<br>
# Если для Reuters заголовок некорректный или текст слишком короткий, используем Readability<br>
if ("reuters.com" in url and (not data["title"] or data["title"].strip().lower() == "reuters.com")) or len(data["text"]) < 200:<br>
rb_title, rb_text = extract_with_readability(html)<br>
if rb_title and rb_title.lower() != "reuters.com":<br>
data["title"] = rb_title<br>
if rb_text and len(rb_text) > len(data["text"]):<br>
data["text"] = rb_text<br>
<br>
# Дополнительное: если авторы не найдены, пытаемся извлечь строку, начинающуюся с "By"<br>
if not data["authors"]:<br>
alt_text = soup.get_text(separator="\n")<br>
m_authors = re.search(r"By\s+(.+)", alt_text, re.IGNORECASE)<br>
if m_authors:<br>
authors_line = m_authors.group(1).strip()<br>
# Разбиваем по запятым или "and"<br>
authors = re.split(r",|\band\b", authors_line)<br>
data["authors"] = [a.strip() for a in authors if a.strip()]<br>
return data<br>
<br>
def post_process_article(data):<br>
# Возможное дополнительное очищение текста и авторов<br>
title = data.get("title", "").strip()<br>
authors = data.get("authors", [])<br>
pub_date = data.get("publish_date", "").strip()<br>
text = data.get("text", "")<br>
match = re.search(r"(\d{2}\.\d{2}\.\d{4}),\s*автор\s+([^.]+)", text)<br>
if match:<br>
pub_date = match.group(1).strip()<br>
authors = [match.group(2).strip()]<br>
text = text.replace(match.group(0), "")<br>
filtered_authors = [a for a in authors if re.search(r"[A-Za-zА-ЯЁа-яё]", a)]<br>
text = re.sub(r"\b(RU|KZ|EN)\b", "", text)<br>
text = re.sub(r"media@sputniknews\.com\S*", "", text)<br>
text = re.sub(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}\+\d{4}", "", text)<br>
text = re.sub(r"\n\s*\n+", "\n\n", text).strip()<br>
return {<br>
"title": title,<br>
"authors": filtered_authors,<br>
"publish_date": pub_date,<br>
"text": text,<br>
}<br>
<br>
# -----------------------------------------------------------------------------<br>
# Функции для обработки YouTube-ссылок (без изменений)<br>
# -----------------------------------------------------------------------------<br>
def format_duration(seconds):<br>
m, s = divmod(seconds, 60)<br>
h, m = divmod(m, 60)<br>
return f"{h:02d}:{m:02d}:{s:02d}"<br>
<br>
def get_youtube_metadata(url):<br>
try:<br>
yt = YouTube(url)<br>
video_title = yt.title<br>
channel_name = yt.author<br>
video_duration = format_duration(yt.length)<br>
video_description = yt.description<br>
if not video_title or not channel_name:<br>
raise Exception("Неполучены метаданные через pytube")<br>
return video_title, channel_name, video_duration, video_description<br>
except Exception as e:<br>
print(f"Ошибка получения метаданных через pytube: {e}")<br>
try:<br>
import yt_dlp<br>
except ImportError:<br>
print("Модуль yt_dlp не установлен. Установите его через: pip install yt-dlp")<br>
return "", "", "", ""<br>
ydl_opts = {<br>
"skip_download": True,<br>
"quiet": True,<br>
"no_warnings": True,<br>
"format": "best",<br>
}<br>
with yt_dlp.YoutubeDL(ydl_opts) as ydl:<br>
try:<br>
info = ydl.extract_info(url, download=False)<br>
video_title = info.get("title", "")<br>
channel_name = info.get("uploader", "")<br>
duration = info.get("duration", 0)<br>
video_duration = format_duration(duration)<br>
video_description = info.get("description", "")<br>
return video_title, channel_name, video_duration, video_description<br>
except Exception as e:<br>
print(f"Ошибка получения метаданных через yt_dlp: {e}")<br>
return "", "", "", ""<br>
<br>
def get_video_id(yt_url):<br>
patterns = [<br>
r"v=([A-Za-z0-9_\-]{5,})",<br>
r"youtu\.be/([A-Za-z0-9_\-]{5,})",<br>
]<br>
for pat in patterns:<br>
match = re.search(pat, yt_url)<br>
if match:<br>
return match.group(1)<br>
return None<br>
<br>
def fetch_subtitles(video_id, languages=("ru", "en")):<br>
try:<br>
transcript_list = YouTubeTranscriptApi.get_transcript(video_id, languages=list(languages))<br>
except TranscriptsDisabled:<br>
return "Субтитры отключены для этого видео."<br>
except NoTranscriptFound:<br>
return "Субтитры для данного видео не найдены."<br>
except VideoUnavailable:<br>
return "Видео недоступно или удалено."<br>
except Exception as e:<br>
return f"Ошибка при получении субтитров: {e}"<br>
lines = [item["text"] for item in transcript_list]<br>
full_text = "\n".join(lines)<br>
return full_text<br>
<br>
def process_youtube_link(url):<br>
video_id = get_video_id(url)<br>
if not video_id:<br>
print("Не удалось определить video_id из ссылки. Пропускаем.")<br>
return None, None, None, None, None, None<br>
video_title, channel_name, video_duration, video_description = get_youtube_metadata(url)<br>
subtitles_text = fetch_subtitles(video_id)<br>
subtitles_text = " ".join(subtitles_text.split())<br>
return video_id, subtitles_text, video_title, channel_name, video_duration, video_description<br>
<br>
# -----------------------------------------------------------------------------<br>
# Основная функция для обработки ссылок<br>
# -----------------------------------------------------------------------------<br>
# Увеличенное время ожидания после загрузки страницы до 5000 мс<br>
PLAYWRIGHT_TIMEOUT = 60000 <br>
WAIT_AFTER_LOAD = 5000 <br>
<br>
def render_with_playwright(url):<br>
with sync_playwright() as p:<br>
browser = p.chromium.launch(headless=True)<br>
context = browser.new_context(<br>
user_agent=(<br>
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "<br>
"AppleWebKit/537.36 (KHTML, как Gecko) "<br>
"Chrome/98.0.4758.102 Safari/537.36"<br>
),<br>
ignore_https_errors=True<br>
)<br>
page = context.new_page()<br>
page.goto(url, timeout=PLAYWRIGHT_TIMEOUT)<br>
page.wait_for_load_state("domcontentloaded", timeout=PLAYWRIGHT_TIMEOUT)<br>
page.wait_for_timeout(WAIT_AFTER_LOAD)<br>
html = page.content()<br>
browser.close()<br>
return html<br>
<br>
def fallback_get(url):<br>
r = requests.get(url, timeout=60)<br>
r.raise_for_status()<br>
return r.text<br>
<br>
def process_link(url):<br>
attempts = 0<br>
html = None<br>
while attempts < 3 and html is None:<br>
try:<br>
html = render_with_playwright(url)<br>
except Exception as e:<br>
print(f"[Playwright] Попытка {attempts+1} не удалась: {e}")<br>
attempts += 1<br>
time.sleep(2)<br>
if html is None:<br>
print(f"[Fallback] Обращаемся к requests для {url}")<br>
try:<br>
html = fallback_get(url)<br>
except Exception as e:<br>
print(f"Ошибка и в fallback для {url}: {e}")<br>
return {"title": "", "authors": [], "publish_date": "", "text": "Не удалось обработать страницу."}<br>
data = extract_article_info(html, url)<br>
clean_data = post_process_article(data)<br>
return clean_data<br>
<br>
# -----------------------------------------------------------------------------<br>
# Главная функция обработки ссылок<br>
# -----------------------------------------------------------------------------<br>
def main():<br>
try:<br>
with open("link.txt", "r", encoding="utf-8") as f:<br>
urls = [line.strip() for line in f if line.strip()]<br>
except Exception as e:<br>
print(f"Ошибка чтения link.txt: {e}")<br>
sys.exit(1)<br>
if not urls:<br>
print("Файл link.txt пуст или не содержит ссылок.")<br>
sys.exit(0)<br>
for i, url in enumerate(urls, start=1):<br>
# Игнорирование ссылок на telegram, instagram и gov.kz<br>
if any(forbidden in url for forbidden in ["telegram", "instagram", "gov.kz"]):<br>
print(f"Пропускаем ссылку (запрещенный домен): {url}")<br>
continue<br>
print(f"=== Обработка {i}/{len(urls)}: {url}")<br>
video_id = get_video_id(url)<br>
if video_id:<br>
result = process_youtube_link(url)<br>
if result[1] is None:<br>
print("Ошибка при обработке YouTube ссылки, пропускаем.")<br>
continue<br>
video_id, subtitles_text, video_title, channel_name, video_duration, video_description = result<br>
output_filename = Path(f"result_{i}.txt")<br>
with open(output_filename, "w", encoding="utf-8") as fout:<br>
fout.write(f"URL: {url}\n")<br>
fout.write(f"VIDEO_ID: {video_id}\n")<br>
fout.write(f"Канал: {channel_name}\n")<br>
fout.write(f"Название видео: {video_title}\n")<br>
fout.write(f"Длительность: {video_duration}\n")<br>
fout.write(f"Описание:\n{video_description}\n\n")<br>
fout.write("Субтитры:\n")<br>
fout.write(subtitles_text + "\n")<br>
print(f" => Сохранено в {output_filename}\n")<br>
else:<br>
data = process_link(url)<br>
output_filename = Path(f"result_{i}.txt")<br>
with open(output_filename, "w", encoding="utf-8") as fout:<br>
fout.write(f"URL: {url}\n")<br>
fout.write(f"Заголовок: {data['title']}\n")<br>
fout.write(f"Автор(ы): {data['authors']}\n")<br>
fout.write(f"Дата: {data['publish_date']}\n\n")<br>
fout.write(data['text'] + "\n")<br>
print(f" => Сохранено в {output_filename}\n")<br>
<br>
if __name__ == "__main__":<br>
main()