|
| 1 | +import re |
| 2 | +from enum import Enum |
| 3 | +from typing import Optional |
| 4 | +from urllib.parse import quote_plus |
| 5 | + |
| 6 | +import pythonmonkey as pm |
| 7 | +import requests |
| 8 | +from fastapi import APIRouter, Query |
| 9 | +from py_mini_racer import MiniRacer |
| 10 | + |
| 11 | +PAGE_KEY = "blogPageUrl" |
| 12 | + |
| 13 | +lofter_router = APIRouter() |
| 14 | + |
| 15 | + |
| 16 | +class JsEngine(str, Enum): |
| 17 | + py_mini_racer = "py_mini_racer" |
| 18 | + js2py = "js2py" |
| 19 | + pythonmonkey = "pythonmonkey" |
| 20 | + |
| 21 | + |
| 22 | +class Target(str, Enum): |
| 23 | + es5 = "es5" |
| 24 | + es6 = "es6" |
| 25 | + |
| 26 | + |
| 27 | +def transform(content: str, engine: JsEngine = JsEngine.pythonmonkey, target: Target = "es5"): |
| 28 | + print("-- replacing") |
| 29 | + if target == "es6": |
| 30 | + content = content.replace("var ", "export var ") |
| 31 | + |
| 32 | + if target == "es5": |
| 33 | + pattern = r'(var\s+(s\d+)\s*=\s*{};)' |
| 34 | + |
| 35 | + def replacement(match): |
| 36 | + full_match, var_name = match.groups() |
| 37 | + return f"{full_match}\nexports.{var_name} = {var_name};" |
| 38 | + |
| 39 | + content = """ |
| 40 | +exports={}; |
| 41 | +// 在导入任何其他模块之前,首先创建全局的 dwr 对象 |
| 42 | +dwr = { |
| 43 | + engine: { |
| 44 | + _remoteHandleCallback: function (id, seq, data) { |
| 45 | + // 可以根据需要处理或返回模拟数据 |
| 46 | + return; |
| 47 | + }, |
| 48 | + }, |
| 49 | + // 根据需要添加其他必要的属性和方法 |
| 50 | +}; |
| 51 | + """ + content |
| 52 | + content = re.sub(pattern, replacement, content) |
| 53 | + content += ";\nexports;" |
| 54 | + |
| 55 | + # print("-- eval content: ", content) |
| 56 | + if engine == JsEngine.py_mini_racer: |
| 57 | + ctx = MiniRacer() |
| 58 | + content = ctx.eval(content) |
| 59 | + |
| 60 | + elif engine == JsEngine.pythonmonkey: |
| 61 | + |
| 62 | + content = pm.eval(content, {}) |
| 63 | + |
| 64 | + def replace_null_object(obj): |
| 65 | + if obj is pm.null: |
| 66 | + return None |
| 67 | + elif isinstance(obj, dict): |
| 68 | + return {k: replace_null_object(v) for k, v in obj.items()} |
| 69 | + elif isinstance(obj, list): |
| 70 | + return [replace_null_object(item) for item in obj] |
| 71 | + else: |
| 72 | + return obj |
| 73 | + |
| 74 | + content = replace_null_object(content) |
| 75 | + |
| 76 | + # print("result: ", content) |
| 77 | + items = [] |
| 78 | + i = 0 |
| 79 | + for key, value in content.items(): |
| 80 | + if isinstance(value, dict): |
| 81 | + i += 1 |
| 82 | + # print(">> ", i, value) |
| 83 | + # if "blogName" in value: |
| 84 | + if PAGE_KEY in value: |
| 85 | + items.append(value) |
| 86 | + return items |
| 87 | + |
| 88 | + |
| 89 | +class Category(str, Enum): |
| 90 | + date = "date" |
| 91 | + total = "total" |
| 92 | + week = "week" |
| 93 | + month = "month" |
| 94 | + |
| 95 | + |
| 96 | +@lofter_router.get("/lofter/search") |
| 97 | +async def search_lofter( |
| 98 | + keyword: str = Query(..., description="Search keyword"), |
| 99 | + # 必选参数 |
| 100 | + script_session_id: str = Query("${scriptSessionId}187", alias="scriptSessionId", include_in_schema=False), |
| 101 | + http_session_id: Optional[str] = Query(None, alias="httpSessionId", include_in_schema=False), |
| 102 | + script_name: str = Query("TagBean", alias="scriptName", include_in_schema=False), |
| 103 | + method_name: str = Query("search", alias="methodName", include_in_schema=False), |
| 104 | + id: int = Query(0, include_in_schema=False), |
| 105 | + param1: int = Query(0, include_in_schema=False), |
| 106 | + param2: Optional[str] = Query(None, include_in_schema=False), |
| 107 | + category: Category = Query(Category.total), |
| 108 | + param4: bool = Query(False, include_in_schema=False), |
| 109 | + param5: int = Query(0, include_in_schema=False), |
| 110 | + page_size: int = Query(20), |
| 111 | + page_no: int = Query(0), |
| 112 | + param8: int = Query(0, include_in_schema=False), |
| 113 | + batch_id: int = Query(606849, alias="batchId", include_in_schema=False), |
| 114 | + call_count: int = Query(1, alias="callCount", include_in_schema=False) |
| 115 | +): |
| 116 | + # 需要转成 %开头 的URL编码 |
| 117 | + keyword = quote_plus(keyword) |
| 118 | + |
| 119 | + # 构造 payload 变量 |
| 120 | + payload = f"""callCount={call_count} |
| 121 | +scriptSessionId={script_session_id} |
| 122 | +httpSessionId={http_session_id or ''} |
| 123 | +c0-scriptName={script_name} |
| 124 | +c0-methodName={method_name} |
| 125 | +c0-id={id} |
| 126 | +c0-param0=string:{keyword} |
| 127 | +c0-param1=number:{param1} |
| 128 | +c0-param2=string:{param2 or ''} |
| 129 | +c0-param3=string:{category.value} |
| 130 | +c0-param4=boolean:{str(param4).lower()} |
| 131 | +c0-param5=number:{param5} |
| 132 | +c0-param6=number:{page_size} |
| 133 | +c0-param7=number:{page_no} |
| 134 | +c0-param8=number:{param8} |
| 135 | +batchId={batch_id}""" |
| 136 | + # print(payload) |
| 137 | + |
| 138 | + url = "https://www.lofter.com/dwr/call/plaincall/TagBean.search.dwr" |
| 139 | + |
| 140 | + headers = { |
| 141 | + 'Referer': 'https://www.lofter.com/tag/puppy/total?tab=archive', |
| 142 | + 'Content-Type': 'text/plain', |
| 143 | + 'Cookie': 'HMACCOUNT=F2993D5B41121606; Hm_lpvt_c5c55f9c94fbca8efd7d891afb3210e8=1728891466; Hm_lvt_c5c55f9c94fbca8efd7d891afb3210e8=1728891466; JSESSIONID-WLF-XXD=03fb249509f15bfe33d7f12b2df6d0ccf25a6f118b17719982527c11a213eb4de46d76495f2747d89625ed7d53fc85760c7fc277c41590f6b0c190a26f3ffaf66b2aae7b0ea1b88d479ffc6ae2bc97aa86981833227880ca4442ce62694332e1b76438d022512aed218eed17fa4edc0261832f33a10e0c7b9218a474fc3acb51542765be; LOFTER_SESS=Q_yiq1PyLbtcexv_P6dzelQ_pG4j8MSj2pXjVJsa4foL5dmfURVQ8sMe0xOg5aB1Z7j4RinnrzMYAN4PeP7IR-t0EeF672eG5gXnqynPLXpdTzRkVHzE_GzL99DpeIHkkpo_1K3DXeVh4h6FAc4x4hPijmSYSA1wFim0jeR_xMRhi3S4X8f12vDdYHu5YtWFjghLcshT-pj7braVdmmBO9ozWVuH_3on; LOFT_SESSION_ID=AjL0IyRo0rLeXSonKDeaHL5lumQCUm5A; NETEASE_WDA_UID=2216403593#|#1728891481954; firstentry=%2Flogin.do|https%3A%2F%2Fwww.google.com%2F; hb_MA-BFD7-963BF6846668_source=qita297790.lofter.com; reglogin_isLoginFlag=1; regtoken=2000; usertrack=dZPgEWcMyjQ9qd5yA0J5Ag==; NTESwebSI=1E12F50676A469369A03E6A3A08B023A.lofter-webapp-web-old-docker-lftpro-3-3nhsm-87n68-58f9455bdpzrp-8080; __LOFTER_TRACE_UID=E97060C8E14E40F8A11906730F70D22B#2216403593#12; __snaker__id=rBFF6uAyPJBnDDaN; gdxidpyhxdE=Yzs4NhyhMeHhkf5ik%5CIPRcy%5CVRX9%5CSoRRVMWyEf%5Clk9%2FoR4l7SOho40nOXkRdaLJzQ5dSsejJ1qtfCGp7jXDh38SkyewBDBAjSbbz0dsQgAyQp2qRnSMVZt8tgZRjpYUEeKKUx%2FDtjPoZ%2FKEPaXZhRMn1QTIN8VY%5CYhtJ2DvkCIGgdbr%3A1728892367321; noAdvancedBrowser=0' |
| 144 | + } |
| 145 | + |
| 146 | + response = requests.request("POST", url, headers=headers, data=payload) |
| 147 | + |
| 148 | + content = transform(response.text) |
| 149 | + # print([item[PAGE_KEY] for item in content]) |
| 150 | + return content |
0 commit comments