- omni/vault.py: пул ключей парсеров (env-first, data/vault.json, ротация, cooldown 401/402/403=1ч, 429=10м), только маски в выводе - tools/build_vault.py: сборка пула из MASTER_KEYS.csv и локальных дампов (значения не в git) - tools/vault_check.py: живая проверка с маскированием - searchers: firecrawl/tavily/exa/serper/jina/apify берут ключ из vault, если нет env - omni radar: домены+каналы из radar_server.db -> реестр, --import-posts заливает посты каналов - radar-источники помечены opt_in: общий обход не тормозят (869 против 3337 всего) - omni/sources_cn.py + tools/wave_cn.py: 9 живых китайских источников (52pojie, nodeseek, kanxue, anquanke, 4hou, xz.aliyun, ithome, cnbeta, v2ex) - net.get(proxy=...): принудительный прокси для CF-площадок - /api/keys + строка ключей в панели - tests/test_vault_radar.py: 7 тестов, всего 29 passed
86 lines
4.4 KiB
Python
86 lines
4.4 KiB
Python
"""Волна по китайским/азиатским источникам: форумы, реверс, security-новости.
|
||
|
||
Проверяет кандидатов живыми запросами (лента / список тем), пишет живые в
|
||
omni/sources_cn.py. Запуск: python tools/wave_cn.py [--apply]
|
||
"""
|
||
import sys
|
||
import time
|
||
from pathlib import Path
|
||
|
||
import requests
|
||
|
||
ROOT = Path(__file__).resolve().parents[1]
|
||
OUT = ROOT / "omni" / "sources_cn.py"
|
||
|
||
UA = ("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
|
||
"(KHTML, like Gecko) Chrome/131.0 Safari/537.36")
|
||
|
||
# name, url, adapter, category, tags, limit
|
||
CAND = [
|
||
("cn_52pojie", "https://www.52pojie.cn/forum.php?mod=guide&view=new", "html_listing", "forum", ("cn", "reverse", "abuse"), 20),
|
||
("cn_nodeseek", "https://www.nodeseek.com/", "html_listing", "forum", ("cn", "hosting"), 20),
|
||
("cn_freebuf", "https://www.freebuf.com/", "html_listing", "security", ("cn", "security"), 20),
|
||
("cn_seebug", "https://paper.seebug.org/", "html_listing", "security", ("cn", "papers", "security"), 15),
|
||
("cn_anquanke", "https://www.anquanke.com/", "html_listing", "security", ("cn", "security"), 20),
|
||
("cn_4hou", "https://www.4hou.com/", "html_listing", "security", ("cn", "security"), 15),
|
||
("cn_kanxue", "https://bbs.kanxue.com/", "html_listing", "forum", ("cn", "reverse"), 20),
|
||
("cn_ithome", "https://www.ithome.com/rss/", "rss", "news", ("cn", "tech"), 20),
|
||
("cn_cnbeta", "https://www.cnbeta.com.tw/backend.php", "rss", "news", ("cn", "tech"), 20),
|
||
("cn_sspai", "https://sspai.com/feed", "rss", "news", ("cn", "tech"), 15),
|
||
("cn_chiphell", "https://www.chiphell.com/forum.php?mod=guide&view=new", "html_listing", "forum", ("cn", "hardware"), 15),
|
||
("cn_52pojie_rss", "https://www.52pojie.cn/forum.php?mod=rss", "rss", "forum", ("cn", "reverse"), 15),
|
||
("cn_v2ex_rss", "https://www.v2ex.com/index.xml", "rss", "forum", ("cn", "community"), 20),
|
||
("cn_linuxdo_rss", "https://linux.do/latest.rss", "rss", "forum", ("cn", "community", "ai"), 20),
|
||
("cn_hostloc_rss", "https://hostloc.com/forum.php?mod=rss", "rss", "forum", ("cn", "hosting"), 15),
|
||
("cn_baidu_sec", "https://xz.aliyun.com/", "html_listing", "security", ("cn", "security"), 15),
|
||
("cn_secfree", "https://www.secfree.com/", "html_listing", "security", ("cn", "security"), 10),
|
||
("cn_tttang", "https://tttang.com/", "html_listing", "security", ("cn", "security"), 10),
|
||
]
|
||
|
||
|
||
def probe(url, adapter="html_listing"):
|
||
try:
|
||
r = requests.get(url, headers={"User-Agent": UA}, timeout=18, allow_redirects=True)
|
||
except Exception as e:
|
||
return 0, type(e).__name__, 0
|
||
if adapter == "rss":
|
||
n_links = r.text.count("<item") + r.text.count("<entry")
|
||
else:
|
||
n_links = r.text.count("<a ")
|
||
return r.status_code, "", n_links
|
||
|
||
|
||
def main():
|
||
apply = "--apply" in sys.argv
|
||
live, dead = [], []
|
||
for name, url, adapter, cat, tags, lim in CAND:
|
||
code, err, links = probe(url, adapter)
|
||
ok = code == 200 and links >= 30
|
||
print("%-18s %-3s links %-5d %s" % (name, code or err, links, "LIVE" if ok else "—"))
|
||
if ok:
|
||
live.append((name, url, adapter, cat, tags, lim))
|
||
else:
|
||
dead.append(name)
|
||
time.sleep(0.8)
|
||
|
||
print("\nживых %d, мёртвых %d" % (len(live), len(dead)))
|
||
if not apply:
|
||
print("dry-run: --apply чтобы записать omni/sources_cn.py")
|
||
return 0
|
||
|
||
body = ['"""Китайские и азиатские источники (проверены живыми запросами tools/wave_cn.py).',
|
||
'', 'Форумы реверса/хостинга, security-новости, tech-ленты. Все — публичные списки,',
|
||
'без ключей и кук. Подключаются в реестр omni как категории forum/security/news.',
|
||
'"""', '', 'CN = [']
|
||
for name, url, adapter, cat, tags, lim in live:
|
||
t = ", ".join('"%s"' % x for x in tags)
|
||
body.append(' {"name": "%s", "category": "%s", "kind": "site", "url": "%s",'
|
||
' "adapter": "%s", "tags": (%s), "limit": %d},' % (name, cat, url, adapter, t, lim))
|
||
body.append(']')
|
||
OUT.write_text("\n".join(body) + "\n", encoding="utf-8", newline="\n")
|
||
print("записано", OUT, "| источников", len(live))
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main()) |