추가 수정했는데 안되네.
This commit is contained in:
Binary file not shown.
@@ -3,14 +3,29 @@ from urllib.request import Request, urlopen
|
||||
from urllib.error import URLError, HTTPError
|
||||
import re
|
||||
from html import unescape
|
||||
from urllib.parse import urlparse, parse_qs, unquote
|
||||
from html.parser import HTMLParser
|
||||
|
||||
TARGET_URL = "https://m.slrclub.com/l/free"
|
||||
|
||||
class ProxyHandler(BaseHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
requested_path = self.path
|
||||
if requested_path == "/" or requested_path == "":
|
||||
requested_path = "/l/free"
|
||||
# parse query params for skip/limit and optional path override
|
||||
parsed = urlparse(self.path)
|
||||
qs = parse_qs(parsed.query)
|
||||
try:
|
||||
skip = int(qs.get('skip', ['0'])[0])
|
||||
except Exception:
|
||||
skip = 0
|
||||
try:
|
||||
limit = int(qs.get('limit', ['10'])[0])
|
||||
except Exception:
|
||||
limit = 10
|
||||
|
||||
# Path to fetch on the remote site. If `path` query provided, use it; otherwise use request path or default.
|
||||
requested_path = qs.get('path', [unquote(parsed.path or '')])[0]
|
||||
if requested_path in ('', '/', None):
|
||||
requested_path = '/l/free'
|
||||
|
||||
target_url = f"https://m.slrclub.com{requested_path}"
|
||||
|
||||
@@ -27,46 +42,87 @@ class ProxyHandler(BaseHTTPRequestHandler):
|
||||
charset = response.headers.get_content_charset(failobj="utf-8")
|
||||
raw_html = response.read().decode(charset, errors="replace")
|
||||
|
||||
# Extract up to 10 post entries (제목 + 링크) and render a simple list.
|
||||
items = []
|
||||
# Extract links using HTMLParser to better preserve anchor text, titles, and img alts
|
||||
class LinkExtractor(HTMLParser):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.stack = []
|
||||
self.anchors = []
|
||||
self.current = None
|
||||
|
||||
# First try: find anchors inside list items (common for post lists)
|
||||
def handle_starttag(self, tag, attrs):
|
||||
attrs = dict(attrs)
|
||||
self.stack.append((tag, attrs))
|
||||
if tag == 'a':
|
||||
self.current = {
|
||||
'href': attrs.get('href', '').strip(),
|
||||
'title': attrs.get('title', '').strip(),
|
||||
'text': '',
|
||||
'ancestors': [t for t, a in self.stack[:-1]],
|
||||
}
|
||||
if tag == 'img' and self.current is not None:
|
||||
alt = attrs.get('alt') or attrs.get('title') or ''
|
||||
if alt:
|
||||
self.current.setdefault('img_alt', alt.strip())
|
||||
|
||||
def handle_data(self, data):
|
||||
if self.current is not None:
|
||||
self.current['text'] += data
|
||||
|
||||
def handle_endtag(self, tag):
|
||||
if not self.stack:
|
||||
return
|
||||
top_tag, top_attrs = self.stack.pop()
|
||||
if tag == 'a' and self.current is not None:
|
||||
href = (self.current.get('href') or '').strip()
|
||||
text = (self.current.get('text') or '').strip()
|
||||
title = (self.current.get('title') or '').strip()
|
||||
img_alt = (self.current.get('img_alt') or '').strip()
|
||||
final = text or title or img_alt
|
||||
ancestors = self.current.get('ancestors', [])
|
||||
self.anchors.append({'href': href, 'text': unescape(final), 'ancestors': ancestors})
|
||||
self.current = None
|
||||
|
||||
parser = LinkExtractor()
|
||||
parser.feed(raw_html)
|
||||
|
||||
# Prepare li blocks to help extract nearby text when anchor text is empty
|
||||
li_blocks = re.findall(r"<li[^>]*>(.*?)</li>", raw_html, flags=re.DOTALL | re.IGNORECASE)
|
||||
for block in li_blocks:
|
||||
m = re.search(r"<a[^>]+href=[\"']([^\"']+)[\"'][^>]*>(.*?)</a>", block, flags=re.DOTALL | re.IGNORECASE)
|
||||
if m:
|
||||
href = m.group(1).strip()
|
||||
anchor_html = m.group(0)
|
||||
inner_html = m.group(2)
|
||||
txt = re.sub(r"<[^>]+>", "", inner_html).strip()
|
||||
txt = unescape(txt)
|
||||
|
||||
# If inner text is empty, try anchor title or image alt attributes
|
||||
if not txt:
|
||||
t = re.search(r'title=["\']([^"\']+)["\']', anchor_html, flags=re.IGNORECASE)
|
||||
if t:
|
||||
txt = unescape(t.group(1).strip())
|
||||
items = []
|
||||
for a in parser.anchors:
|
||||
href = a['href']
|
||||
txt = (a['text'] or '').strip()
|
||||
if not href:
|
||||
continue
|
||||
if href.lower().startswith('javascript:'):
|
||||
continue
|
||||
# skip anchors inside nav/header/footer elements
|
||||
anc = ' '.join(a.get('ancestors', [])).lower()
|
||||
if any(k in anc for k in ('nav', 'header', 'footer', 'menu', 'notice', 'gnb', 'util')):
|
||||
continue
|
||||
|
||||
if not txt:
|
||||
img_alt = re.search(r'<img[^>]+alt=["\']([^"\']+)["\']', anchor_html, flags=re.IGNORECASE)
|
||||
if img_alt:
|
||||
txt = unescape(img_alt.group(1).strip())
|
||||
# If anchor text is empty, try to find text inside the same <li> block
|
||||
if not txt:
|
||||
for block in li_blocks:
|
||||
if href in block:
|
||||
cand = re.sub(r"<[^>]+>", "", block).strip()
|
||||
# pick the first non-empty line as title
|
||||
for line in cand.splitlines():
|
||||
line = line.strip()
|
||||
if line:
|
||||
txt = unescape(line)
|
||||
break
|
||||
if txt:
|
||||
break
|
||||
|
||||
# Only append if we have a non-empty title
|
||||
if txt:
|
||||
items.append((href, txt))
|
||||
# still empty? skip
|
||||
if not txt:
|
||||
continue
|
||||
|
||||
# Fallback: collect anchors from whole page if no list-items found
|
||||
if not items:
|
||||
anchors = re.findall(r"<a[^>]+href=[\"']([^\"']+)[\"'][^>]*>(.*?)</a>", raw_html, flags=re.DOTALL | re.IGNORECASE)
|
||||
for href, txt_html in anchors:
|
||||
href = href.strip()
|
||||
txt = re.sub(r"<[^>]+>", "", txt_html).strip()
|
||||
txt = unescape(txt)
|
||||
if txt and not href.lower().startswith("javascript:"):
|
||||
items.append((href, txt))
|
||||
items.append((href, txt))
|
||||
|
||||
# Deduplicate and filter out obvious navigation/header links
|
||||
# Deduplicate while preserving order
|
||||
seen = set()
|
||||
filtered = []
|
||||
for href, txt in items:
|
||||
@@ -74,22 +130,23 @@ class ProxyHandler(BaseHTTPRequestHandler):
|
||||
if key in seen:
|
||||
continue
|
||||
seen.add(key)
|
||||
lowtxt = (txt or "").strip().lower()
|
||||
if href in ("#", "/", "", "/l/free"):
|
||||
# ignore trivial/empty titles
|
||||
if not txt:
|
||||
continue
|
||||
if lowtxt in ("홈", "로그인", "검색", "공지", "more", "더보기"):
|
||||
if href in ('#', '/', '', '/l/free'):
|
||||
continue
|
||||
filtered.append((href, txt))
|
||||
|
||||
# Skip the top 5 posts and show the next 10
|
||||
posts = filtered[5:5+10]
|
||||
# Exclude notices (공지) anywhere in the title, then apply skip/limit
|
||||
filtered_no_notice = [(h, t) for (h, t) in filtered if '공지' not in (t or '').lower()]
|
||||
posts = filtered_no_notice[skip:skip+limit]
|
||||
|
||||
# Build simple HTML list for posts only
|
||||
host = self.headers.get("Host", f"localhost:{self.server.server_port}")
|
||||
out_lines = [
|
||||
'<!doctype html>',
|
||||
'<html><head><meta charset="utf-8"><title>SLRClub - 게시글 목록</title></head><body>',
|
||||
'<h1>게시글 (최대 10개)</h1>',
|
||||
f'<h1>게시글 (skip={skip}, limit={limit})</h1>',
|
||||
'<ul>'
|
||||
]
|
||||
for href, txt in posts:
|
||||
|
||||
Reference in New Issue
Block a user