v1.0.8: 自动爬取URL规范化去重(锚点/跟踪参数/尾部斜杠/默认端口) + 修复反爬启发式误伤小页面
This commit is contained in:
@@ -37,13 +37,12 @@ _CHALLENGE_MARKS = [
|
|||||||
|
|
||||||
|
|
||||||
def is_challenge_page(title, html):
|
def is_challenge_page(title, html):
|
||||||
|
"""判断是否仍在反爬验证页 (仅关键词启发, 避免误伤正常小页面)"""
|
||||||
low = html.lower()
|
low = html.lower()
|
||||||
t = (title or "").lower()
|
t = (title or "").lower()
|
||||||
for mark in _CHALLENGE_MARKS:
|
for mark in _CHALLENGE_MARKS:
|
||||||
if mark in t or mark in low:
|
if mark in t or mark in low:
|
||||||
return True
|
return True
|
||||||
if len(html) < 5000 and ("<article" not in low and "<main" not in low):
|
|
||||||
return True
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
@@ -77,6 +76,36 @@ def _settle_wait(page, timeout_s):
|
|||||||
return True, page.title(), page.content()
|
return True, page.title(), page.content()
|
||||||
|
|
||||||
|
|
||||||
|
_TRACKING_PARAMS = {
|
||||||
|
"utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content",
|
||||||
|
"fbclid", "gclid", "yclid", "mc_cid", "mc_eid", "ref", "ref_src",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_url(url):
|
||||||
|
"""URL 规范化 (用于去重): 去锚点/跟踪参数/尾部斜杠/默认端口, host 小写"""
|
||||||
|
try:
|
||||||
|
p = urllib.parse.urlparse(str(url))
|
||||||
|
host = (p.hostname or "").lower()
|
||||||
|
if not host:
|
||||||
|
return str(url)
|
||||||
|
port = ""
|
||||||
|
if p.port and p.port not in (80, 443):
|
||||||
|
port = f":{p.port}"
|
||||||
|
path = p.path or "/"
|
||||||
|
if len(path) > 1 and path.endswith("/"):
|
||||||
|
path = path.rstrip("/")
|
||||||
|
query = ""
|
||||||
|
if p.query:
|
||||||
|
kept = [kv for kv in p.query.split("&")
|
||||||
|
if kv.split("=", 1)[0].lower() not in _TRACKING_PARAMS]
|
||||||
|
if kept:
|
||||||
|
query = "?" + "&".join(kept)
|
||||||
|
return f"{p.scheme.lower()}://{host}{port}{path}{query}"
|
||||||
|
except Exception:
|
||||||
|
return str(url)
|
||||||
|
|
||||||
|
|
||||||
def filter_links(hrefs, seed_url, include=None, exclude=None,
|
def filter_links(hrefs, seed_url, include=None, exclude=None,
|
||||||
same_domain=True, use_regex=False):
|
same_domain=True, use_regex=False):
|
||||||
"""按规则过滤链接, 返回 (included, excluded); excluded 含排除原因"""
|
"""按规则过滤链接, 返回 (included, excluded); excluded 含排除原因"""
|
||||||
@@ -551,18 +580,19 @@ class CrawlJob:
|
|||||||
|
|
||||||
p, browser, ctx, page, cookie_file = self._open_browser()
|
p, browser, ctx, page, cookie_file = self._open_browser()
|
||||||
queue = [(seed, 0, "")] # (url, depth, 来源链接)
|
queue = [(seed, 0, "")] # (url, depth, 来源链接)
|
||||||
visited = set()
|
visited = set() # 规范化 URL 去重
|
||||||
queued = set([seed])
|
queued = set([normalize_url(seed)])
|
||||||
idx = 0
|
idx = 0
|
||||||
try:
|
try:
|
||||||
while queue and not self._stop.is_set():
|
while queue and not self._stop.is_set():
|
||||||
self._wait_if_paused()
|
self._wait_if_paused()
|
||||||
url, depth, src = queue.pop(0)
|
url, depth, src = queue.pop(0)
|
||||||
if url in visited:
|
key = normalize_url(url)
|
||||||
|
if key in visited:
|
||||||
continue
|
continue
|
||||||
if len(visited) >= max_pages:
|
if len(visited) >= max_pages:
|
||||||
break
|
break
|
||||||
visited.add(url)
|
visited.add(key)
|
||||||
idx += 1
|
idx += 1
|
||||||
run["progress"]["current_url"] = url
|
run["progress"]["current_url"] = url
|
||||||
run["progress"]["done"] = len(visited)
|
run["progress"]["done"] = len(visited)
|
||||||
@@ -574,8 +604,9 @@ class CrawlJob:
|
|||||||
self._persist()
|
self._persist()
|
||||||
if entry["status"] == "OK" and depth < max_depth:
|
if entry["status"] == "OK" and depth < max_depth:
|
||||||
for link in self._discover_links(page):
|
for link in self._discover_links(page):
|
||||||
if link not in visited and link not in queued:
|
lk = normalize_url(link)
|
||||||
queued.add(link)
|
if lk not in visited and lk not in queued:
|
||||||
|
queued.add(lk)
|
||||||
queue.append((link, depth + 1, url))
|
queue.append((link, depth + 1, url))
|
||||||
if entry["status"] == "OK":
|
if entry["status"] == "OK":
|
||||||
self._delay()
|
self._delay()
|
||||||
|
|||||||
Reference in New Issue
Block a user