ajax子页面main_sub.html

本文介绍了一个使用HTML和JavaScript实现的选择器界面设计案例,该案例实现了从左侧选择框向右侧选择框拖拽选项的功能,并提供了确认按钮将选择结果回传给父窗口。此设计适用于配置界面或参数选择场景。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>ModalDialog</title>
	
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="this is my page">
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    
	<script type="text/javascript">
		//父窗口
		var parentWindow;
		if (window.opener==undefined) {
			//支持ie浏览器进入
			parentWindow=window.dialogArguments;
		}else {
			//谷歌浏览器打开
			parentWindow=window.opener.parent;
		}
		window.onload = function() {
			//获取左右两侧的下拉列表
			var leftSelect = document.getElementById("leftSelect");
			var rightSelect = document.getElementById("rightSelect");
			//在窗体加载的时候:1、获取父页面的span文件内容
			//2、遍历左侧选项,选中了就判断(左侧删除,右侧添加)
			var spanHtml =parentWindow.document.getElementsByTagName("span")[0].innerHTML;
			//js里面是弱类型,按逗号切割自动封装成数组
			spanHtml =spanHtml.split(",");
			for (var i = 0; i < spanHtml.length; i++) {
				var str =spanHtml[i];
				//遍历左侧选项
				for (var j = 0; j < leftSelect.options.length; j++) {
					var text =leftSelect.options[j].text;
					//判断父页面显示的与子页面左侧栏的内容,如果相同,删除左侧栏里面相同的
					if (str == text) {
						//右侧添加、左侧删除
						var opt =document.createElement("option");
						//右侧创建一个下拉选项,内容跟和text一样,等于是移到右侧,把左侧删除
						opt.text =text;
						rightSelect.appendChild(opt);
						leftSelect.remove(j);
						break;
						
					}
				}
			}
			//向右选择一个或者多个
			document.getElementById("btn2Right").onclick=function(){
				var index = leftSelect.selectedIndex;//获取选中的下拉列表的下标
				//循环  只能用while ,用for循环角标会随时变
				//不等于-1说明有选中
				while(leftSelect.selectedIndex!=-1){
					//有选择就可以添加到右侧
					var opt =document.createElement("option");
					//opt的内容就是左侧栏选中的角标的值
					opt.text =leftSelect.options[leftSelect.selectedIndex].text;
					rightSelect.appendChild(opt);
					//移过去之后,删除左侧栏的选项
					leftSelect.remove(leftSelect.selectedIndex);
				}
			}
			//向右选择全部
			document.getElementById("btn2AllRight").onclick=function(){
				while (leftSelect.options.length>0) {
					//向右添加左边第一个option的值
					var opt =document.createElement("option");
					//把左侧栏0角标对应的值赋值给opt
					opt.text=leftSelect.options[0].text;
					//右侧栏添加opt
					rightSelect.appendChild(opt);
					//移除左边第一个option
					leftSelect.remove(0);
				}
			}
			//向左选择一个或者多个
			document.getElementById("btn2Left").onclick=function(){
				//右侧删除,左侧添加
				if (rightSelect.options.length!=-1) {
				var opt =document.createElement("option");
				opt.text=rightSelect.options[rightSelect.selectedIndex].text;
				leftSelect.appendChild(opt);
				
				rightSelect.remove(rightSelect.selectedIndex);
				}
			}
			//向左选择全部
			document.getElementById("btn2AllLeft").onclick = function() {
				while (rightSelect.options.length>0) {
					var opt = document.createElement("option");
					opt.text = rightSelect.options[0].text;
					leftSelect.appendChild(opt);
					
					rightSelect.remove(0);
				}
				
			}
			//点击确认,右侧列表项的文本内容回填到父窗口的span中
			document.getElementById("btnConfirm").onclick = function(){
				//存储右侧列表项的文本内容
				var str = "";
				for (var i=0; i<rightSelect.options.length; i++) {
					str += rightSelect.options[i].text + ",";
				}
				//去掉最后一个逗号
				str = str.substring(0,str.length-1);
				//通过父窗口,获取子窗口中span元素,设置值 即右侧列表的选项
				parentWindow.document.getElementsByTagName("span")[0].innerHTML = str;
				
				//关闭当前窗口
				window.close();
				
			}
		}
	</script>
  </head>
  
  <body>
    <table width="100%">
    	<tr>
    		<td width="40%" align="center">
    			<select style="width:70%;" id="leftSelect" multiple="multiple" size="12">
    				<option>Java - EE 学科</option>
    				<option>Java - Android 学科</option>
    				<option>C 学科</option>
    				<option>C++ 学科</option>
    				<option>IOS 学科</option>
    				<option>.Net 学科</option>
    				<option>PHP 学科</option>
    				<option>网页平面 学科</option>
    			</select>
    		</td>
    		<td align="center">
    			<input type="button" id="btn2Right" value=">"><br><br>
    			<input type="button" id="btn2AllRight" value=">>"><br><br><br>
    			<input type="button" id="btn2Left" value="<"><br><br>
    			<input type="button" id="btn2AllLeft" value="<<">
    		</td>
    		<td width="40%" align="center">
    			<select style="width:70%;" id="rightSelect" multiple="multiple" size="12">
    			</select>
    		</td>
    	</tr>
    	<tr>
    		<td colspan="3" height="10px"></td>
    	</tr>
    	<tr>
    		<td colspan="3" align="center">
    			<input type="button" id="btnConfirm" value="确定">   
    			<input type="button" value="关闭" onclick="window.close()">
    		</td>
    	</tr>
    </table>
  </body>
</html>

(() => { // ---------------- 1. 全局存储与状态 ---------------- window.__collected_actions = []; window.__element_identity_map = new Map(); // 元素唯一性标记 window.__event_map = new Map(); // 事件记录 window.__analysis_state = { completed: false, error: null, progress: 0 }; // ---------------- 2. 元素唯一 ID ---------------- function getElementUniqueId(el) { if (!el || el.nodeType !== 1) return null; if (el.dataset._uniqueId) return el.dataset._uniqueId; const parts = []; if (el.id) parts.push(`id:${el.id}`); const dataAttrs = ['data-id','data-key','data-uuid','data-component-id']; for (const attr of dataAttrs) { if (el.hasAttribute(attr)) { parts.push(`${attr}:${el.getAttribute(attr)}`); break; } } if (parts.length === 0) parts.push(`xpath:${getPreciseXPath(el)}`); const ts = Date.now(); el.dataset._uniqueId = parts.join('|') + `|ts:${ts}`; return el.dataset._uniqueId; } // ---------------- 3. 精确 XPath ---------------- function getPreciseXPath(el) { if (!el || el.nodeType !== 1) return ''; let path = '', current = el; while (current && current.nodeType === 1 && current !== document.documentElement) { let idx = 1, sibling = current.previousSibling; while (sibling) { if (sibling.nodeType===1 && sibling.tagName===current.tagName) idx++; sibling=sibling.previousSibling; } path = `/${current.tagName.toLowerCase()}[${idx}]` + path; current = current.parentNode; } return `/html${path}`; } // ---------------- 4. 精确 CSS Selector ---------------- function getPreciseCssSelector(el) { if (!el || el.nodeType !== 1) return ''; const parts = []; let current = el, level=0; while (current && current.nodeType===1 && level<10) { let part = current.tagName.toLowerCase(); if (current.id) { part += `#${CSS.escape(current.id)}`; parts.unshift(part); break; } if (current.className) part += '.' + current.className.trim().split(/\s+/).map(c=>CSS.escape(c)).join('.'); const keyAttrs = ['name','data-id','data-name','role','type']; for (const attr of keyAttrs) if (current.hasAttribute(attr)) part += `[${attr}="${CSS.escape(current.getAttribute(attr))}"]`; let idx=1, sib=current.previousSibling; while (sib){ if(sib.nodeType===1 && sib.tagName===current.tagName) idx++; sib=sib.previousSibling; } part += `:nth-child(${idx})`; parts.unshift(part); current=current.parentNode; level++; } return parts.join(' > '); } // ---------------- 5. 元素位置信息 ---------------- function getPrecisePosition(el) { const rect = el.getBoundingClientRect(); let scrollX=0, scrollY=0, current=el; while(current){ scrollX+=current.scrollLeft||0; scrollY+=current.scrollTop||0; current=current.parentElement; } let viewport = { x:rect.x, y:rect.y, width:rect.width, height:rect.height, top:rect.top,right:rect.right,bottom:rect.bottom,left:rect.left }; let docPos = { x:rect.x+window.scrollX, y:rect.y+window.scrollY, width:viewport.width, height:viewport.height, top:rect.top+window.scrollY,right:rect.right+window.scrollX,bottom:rect.bottom+window.scrollY,left:rect.left+window.scrollX }; let depth=0; current=el; while(current.parentElement){ depth++; current=current.parentElement; } return { viewport, document: docPos, depth, scrollOffset:{x:scrollX,y:scrollY} }; } // ---------------- 6. 判断滚动容器 ---------------- function isScrollableContainer(el) { if (!el || el.nodeType !== 1) return false; // HTML / BODY 特判 if (el === document.documentElement || el === document.body) { return Math.abs(document.documentElement.scrollHeight - window.innerHeight) > 5; } try { const style = window.getComputedStyle(el); const hasScrollProp = ['auto', 'scroll'].includes(style.overflowY) || ['auto', 'scroll'].includes(style.overflowX); const canScrollSize = el.scrollHeight - el.clientHeight > 5 || el.scrollWidth - el.clientWidth > 5; // 即使 overflow:hidden,但高度明显超出也判定为滚动容器 const likelyScroll = (!hasScrollProp && canScrollSize); return (hasScrollProp && canScrollSize) || likelyScroll; } catch { return false; } } // ---------------- 7. 创建动作对象 ---------------- function createActionObject(el){ const uid = getElementUniqueId(el); if(window.__element_identity_map.has(uid)) return null; window.__element_identity_map.set(uid,true); const tag=el.tagName.toLowerCase(), pos=getPrecisePosition(el); if(pos.viewport.width<1||pos.viewport.height<1) return null; let type='click'; if(isScrollableContainer(el)) type='scroll'; else if(tag==='input'||tag==='textarea') type='input'; else if(tag==='select') type='select'; else if(el.hasAttribute('onmouseover')||el.hasAttribute('onmouseenter')) type='hover'; else if(el.type==='submit'||el.form) type='submit'; let targetUrl=null; if(tag==='a' && el.href) targetUrl=el.href; else if(el.dataset.url) targetUrl=el.dataset.url; else if(el.hasAttribute('onclick')){ const m=el.getAttribute('onclick').match(/https?:\/\/[^\s'"]+/); if(m) targetUrl=m[0]; } return { unique_id: uid, type, full_xpath: getPreciseXPath(el), css_selector: getPreciseCssSelector(el), text: el.innerText?el.innerText.trim():'', attributes: Array.from(el.attributes).reduce((acc,a)=>{acc[a.name]=a.value;return acc;},{}), position: pos, visible: pos.viewport.width>0 && pos.viewport.height>0, event_listeners: [], target_url: targetUrl, features:{tag_name:tag,is_scrollable:type==='scroll',is_visible:pos.viewport.width>0 && pos.viewport.height>0,dom_depth:pos.depth}, timestamp:new Date().toISOString() }; } // ---------------- 8. Hook事件监听 ---------------- function hookEventListeners(){ const orig=Element.prototype.addEventListener; Element.prototype.addEventListener=function(type,listener,options){ const uid=getElementUniqueId(this); if(uid){ if(!window.__event_map.has(uid)) window.__event_map.set(uid,new Set()); window.__event_map.get(uid).add(type); } return orig.call(this,type,listener,options); }; } // ---------------- 9. 扫描可交互元素 ---------------- function scanInteractiveElements(){ const selectors=['a,button,[role="button"],[onclick]','input,textarea,select,[contenteditable]','div,section,main,article,nav,ul,ol','[data-action],[data-click],[data-scroll]','[onmouseover],[onmousedown],[onmouseup]']; const elementsSet=new Set(); selectors.forEach(sel=>document.querySelectorAll(sel).forEach(el=>elementsSet.add(el))); document.querySelectorAll('*').forEach(el=>{ if(isScrollableContainer(el)) elementsSet.add(el); }); Array.from(elementsSet).forEach((el,i)=>{ window.__analysis_state.progress=Math.round((i/elementsSet.size)*80); const a=createActionObject(el); if(a) window.__collected_actions.push(a); }); // 全局窗口滚动处理 if(document.documentElement.scrollHeight > window.innerHeight + 1){ const uid = 'window-scroll'; if(!window.__element_identity_map.has(uid)){ window.__element_identity_map.set(uid,true); window.__collected_actions.push({ unique_id: uid, type: 'scroll', full_xpath: '/html', css_selector: 'html, body', text: '', attributes: {}, position: getPrecisePosition(document.documentElement), visible: true, event_listeners: [], target_url: null, features: {tag_name:'html', is_scrollable:true, is_visible:true, dom_depth:0}, timestamp: new Date().toISOString() }); } } } // ---------------- 10. 合并事件 ---------------- function mergeEventListeners(){ window.__collected_actions.forEach((a,i)=>{ if(window.__event_map.has(a.unique_id)) a.event_listeners=Array.from(window.__event_map.get(a.unique_id)); if(i%10===0) window.__analysis_state.progress=80+Math.round((i/window.__collected_actions.length)*20); }); } // ---------------- 11. 主分析函数 ---------------- function main(){ const start=performance.now(), maxTime=60000; hookEventListeners(); const executeAnalysis=()=>{ if(performance.now()-start<maxTime-5000){ scanInteractiveElements(); mergeEventListeners(); }else console.warn('分析时间紧张,优先处理已收集元素'); window.__collected_actions.sort((a,b)=>a.position.depth-b.position.depth); window.__analysis_state.completed=true; console.log(`Action analysis completed. Found ${window.__collected_actions.length} unique actions.`); }; if(document.readyState==='complete') executeAnalysis(); else window.addEventListener('load',()=>setTimeout(executeAnalysis,500)); setTimeout(executeAnalysis,8000); } main(); })(); import json import time import re from urllib.parse import urlparse from pathlib import Path from playwright.sync_api import sync_playwright, TimeoutError as PlaywrightTimeoutError def load_analysis_script(file_path: Path) -> str: """加载注入浏览器的 JavaScript 分析脚本""" try: return file_path.read_text(encoding="utf-8") except Exception as e: print(f"[-] 加载分析脚本失败: {str(e)}") raise def safe_folder_name(url: str) -> str: """将 URL 转换为安全的文件夹名""" parsed = urlparse(url) domain = parsed.netloc.replace(":", "_") path = re.sub(r"[^a-zA-Z0-9_-]", "_", parsed.path.strip("/")) if not path: path = "root" return f"{domain}_{path}_{int(time.time())}" def ensure_dir(path: Path): """确保目录存在""" path.mkdir(parents=True, exist_ok=True) def main(): target_url = "https://www.ccidgroup.com/info/2110/44774.htm" max_analysis_timeout = 100 * 1000 load_wait_time = 4000 post_analysis_delay = 1500 output_root = Path("./results") ensure_dir(output_root) with sync_playwright() as p: browser = p.chromium.launch( headless=False, args=[ "--disable-blink-features=AutomationControlled", "--disable-features=IsolateOrigins,site-per-process", "--no-sandbox", "--disable-dev-shm-usage", "--disable-gpu", ], ) try: page = browser.new_page() page.set_default_timeout(60000) page.set_default_navigation_timeout(60000) print("[*] 加载修复版分析脚本...") analysis_code = load_analysis_script(Path("analysis_script.js")) page.add_init_script(script=analysis_code) print(f"[*] 导航至: {target_url}") page.goto(target_url) page.wait_for_load_state("networkidle", timeout=60000) print("[*] 初始页面加载完成") print("[*] 滚动到页面底部...") page.evaluate("window.scrollTo(0, document.body.scrollHeight)") print(f"[*] 等待 {load_wait_time / 1000} 秒,确保动态内容加载...") time.sleep(load_wait_time / 1000) print("[*] 滚动回页面顶部...") page.evaluate("window.scrollTo(0, 0)") time.sleep(1) print("[*] 等待 JS 分析可交互元素...") try: page.wait_for_event( "console", lambda msg: "Action analysis completed" in msg.text, timeout=max_analysis_timeout, ) page.wait_for_timeout(post_analysis_delay) except PlaywrightTimeoutError: print(f"[!] 分析超时(超过 {max_analysis_timeout / 1000} 秒),提取现有结果...") except Exception as e: print(f"[!] 等待分析出错: {str(e)},提取现有结果...") print("[*] 提取分析结果...") all_actions = page.evaluate("() => window.__collected_actions || []") if not isinstance(all_actions, list): all_actions = [] if len(all_actions) == 0: print("\n[!] 警告:未收集到任何可交互元素!") print(" 可能原因:1. JS脚本未正常执行 2. 页面加载失败 3. 浏览器环境限制") else: print(f"\n[+] 分析完成!共发现 {len(all_actions)} 个可交互元素。") action_types = {} for action in all_actions: action_type = action.get("type", "unknown") action_types[action_type] = action_types.get(action_type, 0) + 1 print("[+] 动作类型分布:") for type_name, count in action_types.items(): print(f" {type_name}: {count} 个") # ========== 📁 创建输出目录 ========== folder_name = safe_folder_name(target_url) save_dir = output_root / folder_name ensure_dir(save_dir) print(f"[*] 创建输出目录: {save_dir}") # ========== 📝 保存 URL ========== url_path = save_dir / "url.txt" url_path.write_text(target_url, encoding="utf-8") print(f"[+] URL 已保存: {url_path}") # ========== 📸 保存截图 ========== screenshot_path = save_dir / "page_screenshot.png" try: page.screenshot(path=str(screenshot_path), full_page=True) print(f"[+] 页面截图已保存: {screenshot_path}") except Exception as e: print(f"[!] 保存截图失败: {e}") # ========== 🧩 保存 MHTML ========== mhtml_path = save_dir / "page_snapshot.mhtml" try: cdp = page.context.new_cdp_session(page) mhtml_data = cdp.send("Page.captureSnapshot", {"format": "mhtml"}) mhtml_path.write_text(mhtml_data.get("data", ""), encoding="utf-8") print(f"[+] 页面 MHTML 存档已保存: {mhtml_path}") except Exception as e: print(f"[!] 保存 MHTML 失败: {e}") # ========== 💾 保存分析结果 ========== json_path = save_dir / "all_actions_result.json" json_path.write_text( json.dumps(all_actions, indent=2, ensure_ascii=False), encoding="utf-8", ) print(f"[+] 交互元素结果已保存: {json_path}") except Exception as e: print(f"[-] 执行错误: {str(e)}") finally: print("\n[*] 5 秒后关闭浏览器...") time.sleep(5) browser.close() if __name__ == "__main__": main()
10-11
import json import requests from lxml import etree import uuid import re import html import os import time import urllib3 import threading from queue import Queue from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry # 禁用SSL警告 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8', } # 创建分类文件夹 #1./////////////////////////////////// folders = { "200-500": "./高棉语/国家新闻/200-500/", "500以上": "./高棉语/国家新闻/500以上/" } # 确保文件夹存在 for folder_path in folders.values(): os.makedirs(folder_path, exist_ok=True) # 创建失败URL记录文件 #2.//////////////////////////////////// # failed_urls_file = "./泰语/新闻/failed_urls.txt" # os.makedirs(os.path.dirname(failed_urls_file), exist_ok=True) # 创建线程安全的队列 page_queue = Queue() url_queue = Queue() lock = threading.Lock() def clean_text(text): """ 清理文本中的特殊字符 """ if not text: return text # 1. 解码HTML实体(如   转换为普通空格) text = html.unescape(text) # 2. 替换不间断空格和零宽空格 text = text.replace('\u00A0', ' ') # 不间断空格 text = text.replace('\u200B', '') # 零宽空格 # 3. 移除控制字符(除了换行和制表符) text = re.sub(r'[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]', '', text) # 4. 移除多余的空白字符 text = re.sub(r'\s+', ' ', text).strip() return text def create_session(): """创建带有重试机制的会话""" session = requests.Session() # 定义重试策略 retry_strategy = Retry( total=3, backoff_factor=1, status_forcelist=[429, 500, 502, 503, 504], ) # 创建适配器并挂载到会话 adapter = HTTPAdapter(max_retries=retry_strategy) session.mount("https://", adapter) return session def fetch_page_list(): """获取所有页面列表""" #3.//////////////////////////////////////// pages = [] for i in range(1, 3): pages.append(i) return pages def page_worker(): """页面处理工作线程""" session = create_session() while not page_queue.empty(): page_num = page_queue.get() try: #4./////////////////////////////////////////////////// params = { 'page': page_num, 'isExtend': 'true', } response = session.get('https://api.information.gov.kh/v1/posts', params=params, headers=headers, verify=False, timeout=30) json_txt = response.json() for item in json_txt['posts']: url = item['post_url'] url_queue.put(url) print(f"页面 {page_num} 发现URL: {url}") print(f"页面 {page_num} 处理完成,发现 {len(json_txt['posts'])} 个URL") except Exception as e: print(f"获取第 {page_num} 页时出错: {e}") # with lock: # with open(failed_urls_file, "a", encoding="utf-8") as f: # f.write(f"Page {page_num}: {str(e)}\n") finally: page_queue.task_done() time.sleep(1) # 避免请求过于频繁 def url_worker(): """URL处理工作线程""" session = create_session() while True: url = url_queue.get() if url is None: # 结束信号 break try: #5.///////////////////////////////////// a = str(uuid.uuid4()) data = { "url": url, "id": a, "language": "km", "labels": "新闻", "title": "", "content": "", } res = session.get(url, headers=headers, verify=False, timeout=30) tree = etree.HTML(res.text) # print(res.text) title = tree.xpath('//*[@id="__next"]//h1//text()') content = tree.xpath('//*[@id="__next"]/div/div[4]/div/div[2]/div/div[1]/div/p//text()') print(title) print(content) # 清理标题和内容 if title: data["title"] = clean_text(title[0]) # 清理内容 cleaned_content = [clean_text(p) for p in content] data["content"] = '\n'.join(cleaned_content) # 计算字符数量 char_count = len(data["content"]) # 根据字符数量选择保存路径 if 200 <= char_count <= 500: save_path = folders["200-500"] category = "200-500" elif char_count > 500: save_path = folders["500以上"] category = "500以上" else: # 如果字符数少于200,可以选择跳过或保存到其他文件夹 print(f"内容字符数不足200: {char_count},跳过保存") continue # 保存到文件 with open(save_path + a + ".json", "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=4) print(f"已保存到 {category} url:{url} 题目·: {data['title']} (字符数: {char_count})") except Exception as e: print(f"处理URL {url} 时出错: {e}") # with lock: # with open(failed_urls_file, "a", encoding="utf-8") as f: # f.write(f"{url}\n") finally: url_queue.task_done() time.sleep(0.5) # 避免请求过于频繁 def main(): # 获取所有页面 pages = fetch_page_list() # 将页面加入队列 for page in pages: page_queue.put(page) # 创建并启动页面处理线程 page_threads = [] for _ in range(5): # 5个页面处理线程 t = threading.Thread(target=page_worker) t.daemon = True t.start() page_threads.append(t) # 创建并启动URL处理线程 url_threads = [] for _ in range(20): # 10个URL处理线程 t = threading.Thread(target=url_worker) t.daemon = True t.start() url_threads.append(t) # 等待页面队列完成 page_queue.join() print("所有页面处理完成!") # 等待URL队列完成 url_queue.join() print("所有URL处理完成!") # 发送结束信号给URL处理线程 for _ in range(len(url_threads)): url_queue.put(None) # 等待所有线程完成 for t in page_threads: t.join() for t in url_threads: t.join() print("爬虫任务全部完成!") if __name__ == "__main__": main()这是我的代码,但是匹配不到title和content,但是不是xpath的问题
10-10
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> <!DOCTYPE html> <html lang="en"> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no,minimum-scale=1.0"> <title><spring:message code="location_adjustment"/></title> <!-- compiled css output --> <%@ include file="../common/header.jsp" %> </head> <style> .float-bottom { position: fixed; bottom: 0; left: 0; width: 100%; background: #fff; } </style> <%--<spring:message code="login_name"></spring:message>--%> <body> <div id="container" class="scroller" style="height: 100%;"> <div class="loading"> Loading </div> <div class="bar-positive bar bar-header disable-user-behavior" style="position:relative;"> <div side="right" class="pull-right"> <button class="button button-icon button-clear ion-ios-undo-outline" onclick="end()"> </button> </div> <h1 class="h1 title"><spring:message code="location_adjustment"/></h1> <%@include file="../common/left.jsp"%> </div> <div class="content text-center padding" id="scanStep3"> <div id="scanStep0"> <h1> <div class="positive subheader-height-arrange-unit-scan-main-content"><spring:message code="out"/></div> </h1> <div class="positive has-subheader-content-arrange-unit-scan-sub-content"><spring:message code="please_scan_outof-location"/></div> </div> <div id="scanStep1" style="display: none;"> <h1> <div class="positive subheader-height-arrange-unit-scan-main-content"><spring:message code="in"/></div> </h1> <div class="positive has-subheader-content-arrange-unit-scan-sub-content"><spring:message code="please_scan_into-location"/></div> <div style="text-align:center;color:#AAAAAA;margin-top:10px;" id="outUnitName"></div> </div> <div class="content item-input-inset" style="margin-top:20px;"> <label class="item-input-wrapper"> <input type="text" placeholder="<spring:message code="scan_NFC/bar_code/input_mobile_location_manually" />" id="storageunit">   </label> <a href="javascript:WE.scanQRCode();"><i class="icon ion-ios-camera"></i></a> </div> <button class="button button-block button-positive" style="margin-top:20px;" type="submit" onclick="submitUnitName()"><spring:message code="submit_location"/></button> </div> <div id="scanStep2" style="display: none;"> <div class="bar bar-subheader-content subheader-height-arrange-unit-scan-finish padding" on-hold="arrangeAllOutUnitMac()"> <table class="xsa-table-content" width="100%"> <tr ng-show="true"> <td width="45%"><h3><span class="positive arrange-unit-scan-finish-unit-info-base" id="outUnit"></span></h3></td> <td width="10%" style="text-align:center"><h3><i class="icon ion-arrow-right-a positive"></i> </h3></td> <td width="45%"><h3><span class="positive arrange-unit-scan-finish-unit-info-base" id="inUnit"></span></h3></td> </tr> </table> <table class="arrange-unit-scan-finish-attached-info-table" width="100%"> <tr> <td width="45%"><span class="arrange-unit-scan-finish-unit-info-base arrange-unit-scan-finish-unit-attached-info-base" id="outstorageName">{{outUnitObj.storageName}}[{{outUnitObj.unitUseTypeDesc}}]</span> </td> <td width="10%"></td> <td width="45%"><span class="arrange-unit-scan-finish-unit-info-base arrange-unit-scan-finish-unit-attached-info-base" id="instorageName">{{inUnitObj.storageName}}[{{inUnitObj.unitUseTypeDesc}}]</span></td> </tr> <tr> <td width="45%"><span class="arrange-unit-scan-finish-unit-info-base arrange-unit-scan-finish-unit-attached-info-base" id="outquantity"><spring:message code="quantity"/> : {{outUnitObj.macnoQuantity}}</span> </td> <td width="10%"></td> <td width="45%"><span class="arrange-unit-scan-finish-unit-info-base arrange-unit-scan-finish-unit-attached-info-base" id="inquantity"><spring:message code="category"/> : {{inUnitObj.macnoQuantity}}</span> </td> </tr> <tr> <td width="45%"><span class="arrange-unit-scan-finish-unit-info-base arrange-unit-scan-finish-unit-attached-info-base" id="outtotalQuantity"><spring:message code="quantity"/> : {{outUnitObj.totalQuantity}}</span></td> <td width="10%"></td> <td width="45%"><span class="arrange-unit-scan-finish-unit-info-base arrange-unit-scan-finish-unit-attached-info-base" id="intotalQuantity"><spring:message code="category"/> : {{inUnitObj.totalQuantity}}</span></td> </tr> </table> </div> <div class="has-header has-subheader-content-arrange-unit-scan-finish has-bar-for-footer-button"> <div> <div class="macInfoList" class="xsa-pickup-finish item-button-right" style="padding-right:0px; overflow: hidden;"> <!--<ion-item quick-ng-repeat="item in macInfoList" quick-repeat-list="macInfoList" class="xsa-pickup-finish item-button-right" style="padding-right:0px">--> <table width="100%" class="table" style="margin-bottom: 80px"> </table> </div> </div> </div> </div> </div> <div class="content text-center padding float-bottom" style="display: none;"> <button class="button button-block button-positive" onclick="end()"><spring:message code="finish_adjustment"/></button> </div> <%@ include file="../common/footer.jsp" %> <script> function scanQRCodeResult(code) { $('#storageunit').val(code); submitUnitName(); } function readNFCDataResult(nfcData) { $('#storageunit').val(nfcData); submitUnitName(); } var scanStep = 0; var storage_unit_out = ''; var storage_unit_in = ''; $(function () { storage_unit_in = getCookie('storage_unit_in') ? $.parseJSON(getCookie('storage_unit_in')) : null; storage_unit_out = getCookie('storage_unit_out') ? $.parseJSON(getCookie('storage_unit_out')) : null; if (storage_unit_out != null) { scanStep = 1; } if (storage_unit_out != null && storage_unit_in != null) { scanStep = 2; } setStep(); $('body').on('click', '.macInfoList tr', function () { setCookie("current_mac_info", $(this).attr('data-value')); $('#spinner').show(); window.location.href = "<%=request.getContextPath()%>/mobile/arrangeunit/detail?v="+Date.parse(new Date()); }); }); function setStep() { if (scanStep == 0) { $('#scanStep0').show(); $('#scanStep1').hide(); $('#scanStep2').hide(); $('.float-bottom').hide(); $('.pull-right').hide(); } else if (scanStep == 1) { $('#outUnitName').html('<spring:message code="location" />:' + storage_unit_out.name + '(' + storage_unit_out.storageName + '[' + storage_unit_out.unitUseTypeDesc + '])'); $('#scanStep0').hide(); $('#scanStep1').show(); $('#scanStep2').hide(); $('.float-bottom').hide(); $('.pull-right').show(); } else if (scanStep == 2) { $('#scanStep0').hide(); $('#scanStep1').hide(); $('#scanStep2').show(); $('.float-bottom').show(); $('#scanStep3').hide(); $('.pull-right').hide(); $('#outUnit').html(storage_unit_out.name); $('#inUnit').html(storage_unit_in.name); $('#outstorageName').html(storage_unit_out.storageName + '[' + storage_unit_out.unitUseTypeDesc + '])'); $('#instorageName').html(storage_unit_in.storageName + '[' + storage_unit_in.unitUseTypeDesc + '])'); $('#outquantity').html('<spring:message code="category" /> :' + storage_unit_out.macnoQuantity); $('#inquantity').html('<spring:message code="category" /> :' + storage_unit_in.macnoQuantity); $('#outtotalQuantity').html('<spring:message code="quantity" /> :' + storage_unit_out.totalQuantity); $('#intotalQuantity').html('<spring:message code="quantity" /> :' + storage_unit_in.totalQuantity); macList(); } } function macList() { data = {outUnitName: (storage_unit_out.name)}; $.ajax({ type: "get", url: malApiUrl + "?data="+getPostdata(data,"xsa.arrangeUnitGetMacInfoList2") + "&callback=jsoncallback", async: false, dataType: "jsonp", jsonp:'callback',//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(默认为:callback) jsonpCallback:"success_jsonpCallback",//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名 success_jsonpCallback: function(data){ }, success: function (data) { data = setData(data); data.result = JSON.parse(data.result); if (data.code == 0) { if (data.result.length == 0) { BootstrapDialog.show({ title: '<spring:message code="message"/>', message: '<spring:message code="moving_location_finsh"/>'.format(storage_unit_out.name), buttons: [{ label: '<spring:message code="confirm"/>', action: function (dialog) { delCookie("storage_unit_in"); delCookie("storage_unit_out"); delCookie("current_mac_info"); $('#spinner').show(); window.location.href = "<%=request.getContextPath()%>/mobile/arrangeunit/unit?v="+Date.parse(new Date()); } }] }); } for (var i = 0; i < data.result.length; i++) { var item = data.result[i]; var item_str = escape(JSON.stringify(item)); $('.macInfoList table').append('<tr data-value="' + item_str + '"><td><div><h4>' + item.macname + '</h4></div><div>' + item.macno + '<span class="positive">* ' + item.quantity + '</span></div></td></tr>'); } } }, error: function (data) { message("提交数据失败!"); } }); } function submitUnitName() { setStep(); var url = ''; var data = ''; if (scanStep == 0) { data = {outUnitName: $('#storageunit').val()}; url = malApiUrl + "?data="+getPostdata(data,"xsa.arrangeUnitScanOutUnit2") + "&callback=jsoncallback"; } else if (scanStep == 1) { data = {outUnitName: storage_unit_out.name, inUnitName: $('#storageunit').val()}; url = malApiUrl + "?data="+getPostdata(data,"xsa.arrangeUnitScanInUnit2") + "&callback=jsoncallback"; } $.ajax({ type: "get", url: url, async: false, dataType: "jsonp", jsonp:'callback',//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(默认为:callback) jsonpCallback:"success_jsonpCallback",//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名 success_jsonpCallback: function(data){ }, success: function (data) { data = setData(data); if (data.code == 0) { data.result = JSON.parse(data.result); if (scanStep == 0) { storage_unit_out = data.result; setCookie('storage_unit_out', JSON.stringify(data.result)); playMp3('<%=request.getContextPath() %>/mobile/raw/sound_pickup_one_success.mp3'); message(data.msg); } else { storage_unit_in = data.result; if (storage_unit_out.name == storage_unit_in.name) { playMp3('<%=request.getContextPath() %>/mobile/raw/sound_pickup_fail.mp3'); message('<spring:message code="sample_location_out_in"/>'.format(storage_unit_out.name,storage_unit_in.name)); return; } setCookie('storage_unit_in', JSON.stringify(data.result)); playMp3('<%=request.getContextPath() %>/mobile/raw/sound_pickup_one_success.mp3'); message(data.msg); } scanStep = scanStep + 1; $('#storageunit').val(''); setStep(); } else { playMp3('<%=request.getContextPath() %>/mobile/raw/sound_pickup_fail.mp3'); message(data.msg); } }, error: function (data) { message("提交数据失败!"); } }); } function end() { BootstrapDialog.show({ title: '<spring:message code="message"/>', message: '<spring:message code="adjustment_finsh"/> ', buttons: [{ label: '<spring:message code="cancel"/>', class: 'cancle', action: function (dialog) { dialog.close(); } }, { label: '<spring:message code="confirm"/>', class: 'confirm', action: function (dialog) { delCookie("storage_unit_in"); delCookie("storage_unit_out"); delCookie("current_mac_info"); $('#spinner').show(); window.location.href = "<%=request.getContextPath()%>/mobile/arrangeunit/unit?v="+Date.parse(new Date()); } }] }); } </script> </body> </html> 这个里面的scanStep == 3时这个的页面的长按箭头按着应该弹出弹框的现在是为什么弹不出来呢
07-05
import requests from lxml import etree import csv import time import random import re import os # 固定请求头配置(需替换为实际值) FIXED_USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Edg/137.0.0.0' FIXED_COOKIE = 'll="118161"; bid=l5ki4SOlbBM; dbcl2="244638424:dLHXPIU8S0M"; push_noty_num=0; push_doumail_num=0; __utmv=30149280.24463; _pk_id.100001.4cf6=42b01014d5c31947.1748938983.; _vwo_uuid_v2=D9E78C6D9D4E71BBB6EC73B8583864961|9da3be87da4a6d3be6203809b085d4a9; __yadk_uid=2Zr6yzTnllQxMzDhrQB82h7doa8gM4Ku; ck=ILlj; ap_v=0,6.0; frodotk_db="dd9f2f5023b9a95198dd8df06b2cfbf3"; __utma=30149280.1697373246.1748938900.1749979892.1750127075.8; __utmc=30149280; __utmz=30149280.1750127075.8.6.utmcsr=cn.bing.com|utmccn=(referral)|utmcmd=referral|utmcct=/; __utmt=1; __utmb=30149280.2.10.1750127075; __utma=223695111.238348316.1748938983.1749979902.1750127082.8; __utmb=223695111.0.10.1750127082; __utmc=223695111; __utmz=223695111.1750127082.8.7.utmcsr=douban.com|utmccn=(referral)|utmcmd=referral|utmcct=/; _pk_ref.100001.4cf6=%5B%22%22%2C%22%22%2C1750127082%2C%22https%3A%2F%2Fwww.douban.com%2F%22%5D; _pk_ses.100001.4cf6=1' # 需从浏览器获取 # 基础URL和请求头 base_url = "https://movie.douban.com/subject/27181010/reviews" headers = { 'User-Agent': FIXED_USER_AGENT, 'Cookie': FIXED_COOKIE, 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6', 'Connection': 'keep-alive', 'Upgrade-Insecure-Requests': '1', 'Referer': 'https://movie.douban.com/subject/27181010/', 'Sec-Fetch-Dest': 'document', 'Sec-Fetch-Mode': 'navigate', 'Sec-Fetch-Site': 'same-origin', 'Sec-Fetch-User': '?1', } # 创建输出目录 os.makedirs('douban_data', exist_ok=True) # 创建CSV文件并写入表头 csv_file = open('douban_data/douban_reviews_fixed.csv', 'w', newline='', encoding='utf-8-sig') writer = csv.writer(csv_file) writer.writerow(['昵称', '星级评分', '评论时间', '文本评论', '点赞数', '回应数', '页面位置']) def extract_content(element): """提取评论内容,处理展开情况""" # 尝试提取短评内容 short_content = element.xpath('.//div[contains(@class, "short-content")]/text()') if short_content: return ''.join(short_content).strip() # 尝试提取完整评论内容 full_content = element.xpath('.//div[@class="review-content clearfix"]/text()') if full_content: return ''.join(full_content).strip() # 尝试提取折叠内容 folded_content = element.xpath('.//div[@class="folded"]/text()') if folded_content: return ''.join(folded_content).strip() return "无内容" # 爬取多页数据 for page in range(0, 125): # 构造URL参数 params = { 'start': page * 20, 'sort': 'new_score', 'status': 'P' } try: print(f"开始爬取第 {page+1} 页...") # 发送请求(禁止重定向以检测验证) response = requests.get( url=base_url, params=params, headers=headers, timeout=15, allow_redirects=False # 禁止重定向以检测验证 ) # 检查重定向状态码(302表示需要验证) if response.status_code == 302: location = response.headers.get('Location', '未知位置') print(f"⚠️ 第 {page+1} 页触发验证,重定向至: {location}") # 保存重定向页面供分析 with open(f'douban_data/redirect_page_{page}.html', 'w', encoding='utf-8') as f: f.write(response.text) continue response.encoding = 'utf-8' if response.status_code == 200: # 解析HTML html = etree.HTML(response.text) # 检查是否有验证码提示 captcha = html.xpath('//input[@name="captcha-id"]') if captcha: print(f"⚠️ 第 {page+1} 页需要验证码,跳过") # 保存验证码页面供分析 with open(f'douban_data/captcha_page_{page}.html', 'w', encoding='utf-8') as f: f.write(response.text) continue # 检查页面是否包含评论容器(使用更灵活的选择器) review_container = html.xpath('//div[@class="review-list"]') if not review_container: # 尝试备用选择器 review_container = html.xpath('//div[contains(@id, "content")]//div[contains(@class, "review")]') if not review_container: # 保存异常页面用于分析 with open(f'douban_data/error_page_{page}.html', 'w', encoding='utf-8') as f: f.write(response.text) print(f"❌ 第 {page+1} 页无评论容器,已保存页面供分析") continue # 提取评论项(更新后的选择器) comments = html.xpath('//div[contains(@class, "review-item")]') # 备用选择器:尝试抓取评论项 if not comments: comments = html.xpath('//div[contains(@class, "main") and contains(@class, "review-item")]') if not comments: comments = html.xpath('//div[@class="review-list"]/div[contains(@class, "review")]') if not comments: print(f"❌ 第 {page+1} 页找到0条评论,可能触发反爬") # 检查反爬提示 anti_spider = html.xpath('//div[contains(text(), "检测到异常请求")]') if anti_spider: print("⚠️ 检测到反爬提示,请更换Cookie或IP") # 保存页面供分析 with open(f'douban_data/antispider_page_{page}.html', 'w', encoding='utf-8') as f: f.write(response.text) continue print(f"✅ 第 {page+1} 页找到 {len(comments)} 条评论") for idx, comment in enumerate(comments): try: # 提取昵称 username = comment.xpath('.//a[contains(@class, "name")]/text()') if not username: username = comment.xpath('.//span[@class="author"]/a/text()') username = username[0].strip() if username else "无昵称" # 提取星级评分 rating = comment.xpath('.//span[contains(@class, "rating")]/@title') if not rating: rating = comment.xpath('.//span[contains(@class, "main-title-rating")]/@title') rating = rating[0] if rating else "无评分" # 提取评论时间 comment_time = comment.xpath('.//span[contains(@class, "main-meta")]/text()') if not comment_time: comment_time = comment.xpath('.//span[@class="review-date"]/text()') comment_time = comment_time[0].strip() if comment_time else "无时间" # 提取文本评论 content = extract_content(comment) # 提取点赞数 useful_count = comment.xpath('.//button[contains(@data-count, "useful")]/span/text()') if not useful_count: useful_count = comment.xpath('.//button[contains(@class, "useful")]/span/text()') useful_count = useful_count[0].strip() if useful_count else "0" # 提取回应数 reply_count = comment.xpath('.//a[contains(@class, "replythis")]/text()') if not reply_count: reply_count = comment.xpath('.//span[contains(@class, "reply-count")]/a/text()') reply_count = re.sub(r'\D', '', reply_count[0]) if reply_count else "0" # 写入CSV writer.writerow([ username, rating, comment_time, content, useful_count, reply_count, f"第{page+1}页第{idx+1}条" ]) except Exception as e: print(f"⚠️ 处理评论时出错: {e}") continue else: print(f"❌ 请求失败,状态码: {response.status_code}") except Exception as e: print(f"❌ 请求异常: {e}") # 随机延迟,避免频繁请求 delay = random.uniform(3, 8) print(f"⏳ 等待 {delay:.2f} 秒后继续...") time.sleep(delay) csv_file.close() print("✅ 爬取完成!数据已保存到 douban_data/douban_reviews_fixed.csv"),此代码的评论时间、点赞数和回应数显示有误,帮我修改,要求保留原有功能,再添加负赞数这一列数据,要完整代码
06-18
import requests import re import json import os from tqdm import tqdm import subprocess def clean_filename(title): """清理文件名中的非法字符""" illegal_chars = r'[\\/:*?"<>|]' return re.sub(illegal_chars, '', title).strip() def download_with_progress(url, save_path, headers, file_type='视频'): """带进度条的下载函数""" try: response = requests.get(url, headers=headers, stream=True) response.raise_for_status() # 获取文件大小 total_size = int(response.headers.get('content-length', 0)) block_size = 1024 # 1KB with open(save_path, 'wb') as f, tqdm( desc=f"下载{file_type}", total=total_size, unit='iB', unit_scale=True, unit_divisor=1024, ) as bar: for data in response.iter_content(block_size): bar.update(len(data)) f.write(data) return True except Exception as e: print(f"{file_type}下载失败: {str(e)}") return False def merge_video_audio(video_path, audio_path, output_path): """使用FFmpeg合并音视频""" try: cmd = [ 'ffmpeg', '-i', video_path, '-i', audio_path, '-c:v', 'copy', '-c:a', 'copy', '-y', # 覆盖已存在文件 output_path ] subprocess.run(cmd, check=True) print(f"合并完成: {output_path}") # 清理临时文件 os.remove(video_path) os.remove(audio_path) return True except Exception as e: print(f"合并失败: {str(e)}") print("请确保已安装FFmpeg并添加到系统PATH") return False def extract_playinfo(html): """更健壮的视频信息提取方法""" # 尝试多种匹配模式 patterns = [ r'window\.__playinfo__\s*=\s*({.*?})\s*;', # 标准格式 r'window\.__playinfo__\s*=\s*({.*?})\s*</script>', # 没有分号结尾 r'<script>window\.__playinfo__\s*=\s*({.*?})</script>', # 包含在script标签内 r'window\.__playinfo__\s*=\s*({[\s\S]*?})\s*(?=window)', # 后跟其他window对象 ] for pattern in patterns: match = re.search(pattern, html, re.DOTALL) if match: try: return json.loads(match.group(1)) except json.JSONDecodeError: print("JSON解析失败,尝试修复格式...") # 尝试修复常见的JSON格式问题 fixed_json = re.sub(r',\s*}', '}', match.group(1)) # 修复尾部逗号 fixed_json = re.sub(r',\s*]', ']', fixed_json) # 修复数组尾部逗号 try: return json.loads(fixed_json) except: continue # 如果所有模式都失败,保存HTML用于调试 with open('bilibili_error.html', 'w', encoding='utf-8') as f: f.write(html) raise ValueError("无法提取视频信息,已保存页面到bilibili_error.html") def download_bilibili_video(url, cookie): """主下载函数""" headers = { "Referer": url, "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36", "Cookie": cookie } try: # 创建保存目录 os.makedirs('downloads', exist_ok=True) # 获取页面内容 response = requests.get(url, headers=headers) response.raise_for_status() html = response.text # 提取视频标题 title_match = re.search(r'<title>(.*?)_哔哩哔哩_bilibili</title>', html) if not title_match: # 备用标题提取方法 title_match = re.search(r'<title>(.*?)</title>', html) if not title_match: raise ValueError("无法提取视频标题") raw_title = title_match.group(1) clean_title = clean_filename(raw_title) print(f"视频标题: {clean_title}") # 提取视频信息 - 使用更健壮的提取方法 json_data = extract_playinfo(html) # 检查视频信息结构 if 'data' not in json_data or 'dash' not in json_data['data']: raise ValueError("视频信息结构异常,可能需要更新Cookie") # 提取视频和音频URL video_url = json_data['data']['dash']['video'][0]['baseUrl'] audio_url = json_data['data']['dash']['audio'][0]['baseUrl'] print(f"视频流地址: {video_url[:60]}...") print(f"音频流地址: {audio_url[:60]}...") # 下载文件 video_path = f'downloads/{clean_title}_video_temp.mp4' audio_path = f'downloads/{clean_title}_audio_temp.mp3' final_path = f'downloads/{clean_title}.mp4' # 下载视频 if not download_with_progress(video_url, video_path, headers, '视频'): return False # 下载音频 if not download_with_progress(audio_url, audio_path, headers, '音频'): return False # 合并音视频 if merge_video_audio(video_path, audio_path, final_path): print(f"视频已保存至: {os.path.abspath(final_path)}") return True return False except Exception as e: print(f"处理失败: {str(e)}") return False # 使用示例 if __name__ == "__main__": # 用户配置 video_url = 'https://www.bilibili.com/video/BV1WJu1zzEji/' cookie = "buvid3=2DF7D3EE-69EF-B629-769E-627F0614328902132infoc; b_nut=1723202702; _uuid=B8D75DF5-14BC-5D9A-A481-E78910210B9E5412177infoc; buvid_fp=f79e3b28dd8991c0cfde7076bd449fe7; enable_web_push=DISABLE; header_theme_version=CLOSE; DedeUserID=687979926; DedeUserID__ckMd5=6c58c4afdcdfe451; rpdid=|(J~R~km)RJ)0J'u~kJk|~)|); CURRENT_QUALITY=80; enable_feed_channel=ENABLE; theme-tip-show=SHOWED; bili_ticket=eyJhbGciOiJIUzI1NiIsImtpZCI6InMwMyIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NTM5MjcwNjksImlhdCI6MTc1MzY2NzgwOSwicGx0IjotMX0.ciiOo9Isn7vM0U6_-nK1UnljMwuIndj4S4RDVsvHs3Y; bili_ticket_expires=1753927009; SESSDATA=f93100d6%2C1769219870%2C826cf%2A71CjAPZ6wNPzlaxKD_aD3XNqvcrlZBd0DxRfVbP8MhQRqeskul-5nGa8iV90qsRNCV9jMSVkpjWTN1ZlduMGdkM2hlTEVXLUY4blFoQmhMbklfWDA3REV5S2RScGpzUHo5TVR6Wlg2VTB2TTJCZE1QdXd1SkdQMmwwczFMNmppWTdrOXc2UzdXTUJBIIEC; bili_jct=44a16de90b7b177d083bf3feeef4aae9; theme-avatar-tip-show=SHOWED; bsource=search_bing; bmg_af_switch=1; bmg_src_def_domain=i0.hdslb.com; sid=6kbtp40x; buvid4=5A2C9CFF-DFAA-9EC0-2A58-D1E2DA026B6004338-024080911-ZlPRSE54r313qSSoZon1fKM%2Biei%2BvizeyhAuo4UTauXE456Tew1L4SMzKqrpdj6Q; b_lsid=DDE2BB93_1984FA71441; home_feed_column=5; browser_resolution=1707-944; bp_t_offset_687979926=1094549579044487168; CURRENT_FNVAL=4048" # 替换为你的有效Cookie if download_bilibili_video(video_url, cookie): print("下载成功!") else: print("下载失败,请检查错误信息") 处理失败: 无法提取视频标题 下载失败,请检查错误信息
07-29
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值