python爬取同花順_Java爬取同花順股票數據(附源碼)

最近有小伙伴問我能不能抓取同花順的數據,最近股票行情還不錯,想把數據抓下來自己分析分析。我大A股,大家都知道的,一個概念火了,相應的股票就都大漲。

如果能及時獲取股票漲跌信息,那就能在剛開始火起來的時候殺進去,小賺一筆。但是股票那么多,小伙伴也盯不過來,于是就微信問我,能不能抓取同花順的板塊下的股票信息存到數據庫里?他就能根據數據庫里的數據,制定一些策略。

俗話說:哪里有痛點,哪里就有編程!不就是個同花順嘛,辦他!

調研背景

于是我點開了同花順的板塊頁面:http://q.10jqka.com.cn/gn/ 發現有好268個概念:

分析概念板塊的網頁HTML發現,268個概念的URL就在HTML中:

打開其中的“阿里巴巴概念”,發現網頁又有分頁:

分頁的數據,是根據接口實時獲取的,接口中注入了一些Cooki信息和其他標識,同花順的反爬蟲策略一直比較強,使用模擬接口的方式可能難度會比較大,所以使用selenium模擬瀏覽器操作這種方式比較完美。

設計方案

技術方向有了,再簡單整理一下思路:根據http://q.10jqka.com.cn/gn/,獲取板塊網頁的源碼HTML,用Jsoup解析HTML獲取每個概念的url信息放到List中

遍歷List,根據概念的url獲取概念網頁源碼HTML,解析股票信息

再遞歸點擊執行“下一頁”操作,獲取每一頁的股票數據,直至尾頁

把股票信息存儲到數據庫

配置環境

先介紹下工程所需要的環境:編碼工具:idea 語言:java 依賴:jdk1.8、maven、chrome、ChromeDriver

我們使用的方案是模擬瀏覽器的操作,所以我們需要在電腦安裝chrome瀏覽器和chromedriver驅動。chrome的安裝這里就不說了,百度下載個瀏覽器就行。

關鍵是安裝 ChromeDriver ,需要安裝和當前chrome版本一致的驅動才寫。

查看chrome版本:chrome瀏覽器輸入:Chrome://version

在根據版本下載對于的驅動,版本最好要一致,比如我的是:79.0.3945.117 (正式版本) (64 位),我下載的就是 79.0.3945.36。

ChromeDriver各版本的下載地址:

下面這一步可做可不做,不做也能啟動工程,只是需要修改代碼中的一個配置即可。配置方式:

將下載好的ChromeDriver文件放到/usr/local/bin/目錄下:

shell cp chromedriver /usr/local/bin/

檢測是否安裝成功

shell chromedriver --version

如果不配置,只需要記得修改ChromeDriver在代碼中配置的路徑,你只需要將路徑改為你自己的ChromeDriver路徑即可,比如我的是:

System.setProperty(

"webdriver.chrome.driver",

"/Users/admin/Documents/selenium/chrome/79.0.3945.36/chromedriver"

);

記得修改代碼里ChromeDriver的路徑。 記得修改代碼里ChromeDriver的路徑。 記得修改代碼里ChromeDriver的路徑。

驗證方案

首先完成設計方案中的三步

package com.ths.controller;

import com.ths.service.ThsGnCrawlService;

import com.ths.service.ThsGnDetailCrawlService;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.ResponseBody;

import java.util.HashMap;

import java.util.List;

@Controller

public class CrawlController {

@Autowired

private ThsGnCrawlService thsGnCrawlService;

@Autowired

private ThsGnDetailCrawlService thsGnDetailCrawlService;

@RequestMapping("/test")

@ResponseBody

public void test() {

// 抓取所有概念板塊的url List> list = thsGnCrawlService.ThsGnCrawlListUrl();

// 放入阻塞隊列 thsGnDetailCrawlService.putAllArrayBlockingQueue(list);

// 根據url多線程抓取 thsGnDetailCrawlService.ConsumeCrawlerGnDetailData(1);

}

}

先看看thsGnCrawlService.ThsGnCrawlListUrl();方法,如何抓取所有概念板塊的url?

package com.ths.service.impl;

import com.ths.parse.service.ThsParseHtmlService;

import com.ths.service.ThsGnCrawlService;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.chrome.ChromeDriver;

import org.openqa.selenium.chrome.ChromeOptions;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import java.util.HashMap;

import java.util.List;

import java.util.concurrent.TimeUnit;

@Service

public class ThsGnCrawlServiceImpl implements ThsGnCrawlService {

private final static Logger LOGGER = LoggerFactory.getLogger(ThsGnCrawlServiceImpl.class);

/*** 同花順全部概念板塊url*/

private final static String GN_URL = "http://q.10jqka.com.cn/gn/";

@Autowired

private ThsParseHtmlService thsParseHtmlService;

@Override

public List> ThsGnCrawlListUrl() {

System.setProperty("webdriver.chrome.driver", "/Users/admin/Documents/selenium/chrome/79.0.3945.36/chromedriver");

ChromeOptions options = new ChromeOptions();

//是否啟用瀏覽器界面的參數 //無界面參數// options.addArguments("headless"); //禁用沙盒 就是被這個參數搞了一天// options.addArguments("no-sandbox"); WebDriver webDriver = new ChromeDriver(options);

try {

// 根據網速設置,網速慢可以調低點 webDriver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);

webDriver.get(GN_URL);

Thread.sleep(1000L);

String gnWindow = webDriver.getWindowHandle();

// 獲取同花順概念頁面的HTML String thsGnHtml = webDriver.getPageSource();

LOGGER.info("獲取同花順url:[{}]的html為:/n{}", GN_URL, thsGnHtml);

return thsParseHtmlService.parseGnHtmlReturnGnUrlList(thsGnHtml);

} catch (Exception e) {

LOGGER.error("獲取同花順概念頁面的HTML,出現異常:", e);

} finally {

webDriver.close();

webDriver.quit();

}

return null;

}

}

這里使用了上文說的ChromeDriver,我們需要根據自己的配置,修改對應的地址(重復第四遍!)。 根據代碼可以看到String thsGnHtml = webDriver.getPageSource();方法獲取頁面的HTML,再解析HTML就能獲取各大概念板塊的url。

解析HTML我使用的是Jsoup,簡單易上手,api也很簡單,解析HTML獲取各大板塊的url的代碼如下:

package com.ths.parse.service.impl;

import com.ths.parse.service.ThsParseHtmlService;

import org.jsoup.Jsoup;

import org.jsoup.helper.StringUtil;

import org.jsoup.nodes.Document;

import org.jsoup.nodes.Element;

import org.jsoup.select.Elements;

import org.springframework.stereotype.Service;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.List;

@Service

public class ThsParseHtmlServiceImpl implements ThsParseHtmlService {

/*** 解析同花順概念板塊的Html頁面:http://q.10jqka.com.cn/gn/* 返回所有概念板塊的url地址*/

public List> parseGnHtmlReturnGnUrlList(String html) {

if (StringUtil.isBlank(html)) {

return null;

}

List> list = new ArrayList<>();

Document document = Jsoup.parse(html);

Elements cateItemsFromClass = document.getElementsByClass("cate_items");

for (Element element : cateItemsFromClass) {

Elements as = element.getElementsByTag("a");

for (Element a : as) {

String gnUrl = a.attr("href");

String name = a.text();

HashMap map = new HashMap<>();

map.put("url", gnUrl);

map.put("gnName", name);

list.add(map);

}

}

return list;

}

}

可以看到,只要在html中有的數據,定位到標簽就能獲取對應的數據。

然后放到阻塞隊列:

/*** 阻塞隊列*/

private ArrayBlockingQueue> arrayBlockingQueue = new ArrayBlockingQueue<>(1000);

@Override

public void putAllArrayBlockingQueue(List> list) {

if (!CollectionUtils.isEmpty(list)) {

arrayBlockingQueue.addAll(list);

}

}

再開啟多個線程,從阻塞隊列里獲取url,分別抓取概念板塊的股票數據,如果頁面有分頁,就循環點擊下一頁,再獲取數據,直到尾頁,代碼如下:

package com.ths.service.impl;

import com.ths.dao.StockThsGnInfoDao;

import com.ths.domain.StockThsGnInfo;

import com.ths.service.ThsGnDetailCrawlService;

import org.jsoup.Jsoup;

import org.jsoup.nodes.Document;

import org.jsoup.nodes.Element;

import org.jsoup.select.Elements;

import org.openqa.selenium.By;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.chrome.ChromeDriver;

import org.openqa.selenium.chrome.ChromeOptions;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import org.springframework.util.CollectionUtils;

import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;

import java.math.BigDecimal;

import java.text.SimpleDateFormat;

import java.util.*;

import java.util.concurrent.ArrayBlockingQueue;

import java.util.concurrent.TimeUnit;

@Service

public class ThsGnDetailCrawlServiceImpl implements ThsGnDetailCrawlService {

private final static Logger LOGGER = LoggerFactory.getLogger(ThsGnDetailCrawlServiceImpl.class);

/*** 阻塞隊列*/

private ArrayBlockingQueue> arrayBlockingQueue = new ArrayBlockingQueue<>(1000);

@Autowired

private StockThsGnInfoDao stockThsGnInfoDao;

@Override

public void putAllArrayBlockingQueue(List> list) {

if (!CollectionUtils.isEmpty(list)) {

arrayBlockingQueue.addAll(list);

}

}

@Override

public void ConsumeCrawlerGnDetailData(int threadNumber) {

for (int i = 0; i < threadNumber; ++i) {

LOGGER.info("開啟線程第[{}]個消費", i);

new Thread(new crawlerGnDataThread()).start();

}

LOGGER.info("一共開啟線程[{}]個消費", threadNumber);

}

class crawlerGnDataThread implements Runnable {

@Override

public void run() {

try {

while (true) {

Map map = arrayBlockingQueue.take();

String url = map.get("url");

String gnName = map.get("gnName");

String crawlerDateStr = new SimpleDateFormat("yyyy-MM-dd HH:00:00").format(new Date());

//chromederiver存放位置 System.setProperty("webdriver.chrome.driver", "/Users/admin/Documents/selenium/chrome/79.0.3945.36/chromedriver");

ChromeOptions options = new ChromeOptions();

//無界面參數 // options.addArguments("headless"); //禁用沙盒 就是被這個參數搞了一天 // options.addArguments("no-sandbox"); WebDriver webDriver = new ChromeDriver(options);

try {

webDriver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);

webDriver.get(url);

Thread.sleep(1000L);

String oneGnHtml = webDriver.getPageSource();

LOGGER.info("當前概念:[{}],html數據為[{}]", gnName, oneGnHtml);

LOGGER.info(oneGnHtml);

// TODO 解析并存儲數據 parseHtmlAndInsertData(oneGnHtml, gnName, crawlerDateStr);

clicktoOneGnNextPage(webDriver, oneGnHtml, gnName, crawlerDateStr);

} catch (Exception e) {

LOGGER.error("用chromerDriver抓取數據,出現異常,url為[{}],異常為[{}]", url, e);

} finally {

webDriver.close();

webDriver.quit();

}

}

} catch (Exception e) {

LOGGER.error("阻塞隊列出現循環出現異常:", e);

}

}

}

public void parseHtmlAndInsertData(String html, String gnName, String crawlerDateStr) {

Document document = Jsoup.parse(html);

// Element boardElement = document.getElementsByClass("board-hq").get(0);// String gnCode = boardElement.getElementsByTag("h3").get(0).getElementsByTag("span").get(0).text();

Element table = document.getElementsByClass("m-pager-table").get(0);

Element tBody = table.getElementsByTag("tbody").get(0);

Elements trs = tBody.getElementsByTag("tr");

for (Element tr : trs) {

try {

Elements tds = tr.getElementsByTag("td");

String stockCode = tds.get(1).text();

String stockName = tds.get(2).text();

BigDecimal stockPrice = parseValueToBigDecimal(tds.get(3).text());

BigDecimal stockChange = parseValueToBigDecimal(tds.get(4).text());

BigDecimal stockChangePrice = parseValueToBigDecimal(tds.get(5).text());

BigDecimal stockChangeSpeed = parseValueToBigDecimal(tds.get(6).text());

BigDecimal stockHandoverScale = parseValueToBigDecimal(tds.get(7).text());

BigDecimal stockLiangBi = parseValueToBigDecimal(tds.get(8).text());

BigDecimal stockAmplitude = parseValueToBigDecimal(tds.get(9).text());

BigDecimal stockDealAmount = parseValueToBigDecimal(tds.get(10).text());

BigDecimal stockFlowStockNumber = parseValueToBigDecimal(tds.get(11).text());

BigDecimal stockFlowMakertValue = parseValueToBigDecimal(tds.get(12).text());

BigDecimal stockMarketTtm = parseValueToBigDecimal(tds.get(13).text());

// 存儲數據 StockThsGnInfo stockThsGnInfo = new StockThsGnInfo();

stockThsGnInfo.setGnName(gnName);

stockThsGnInfo.setGnCode(null);

stockThsGnInfo.setStockCode(stockCode);

stockThsGnInfo.setStockName(stockName);

stockThsGnInfo.setStockPrice(stockPrice);

stockThsGnInfo.setStockChange(stockChange);

stockThsGnInfo.setStockChangePrice(stockChangePrice);

stockThsGnInfo.setStockChangeSpeed(stockChangeSpeed);

stockThsGnInfo.setStockHandoverScale(stockHandoverScale);

stockThsGnInfo.setStockLiangBi(stockLiangBi);

stockThsGnInfo.setStockAmplitude(stockAmplitude);

stockThsGnInfo.setStockDealAmount(stockDealAmount);

stockThsGnInfo.setStockFlowStockNumber(stockFlowStockNumber);

stockThsGnInfo.setStockFlowMakertValue(stockFlowMakertValue);

stockThsGnInfo.setStockMarketTtm(stockMarketTtm);

stockThsGnInfo.setCrawlerTime(crawlerDateStr);

stockThsGnInfo.setCrawlerVersion("同花順概念板塊#" + crawlerDateStr);

stockThsGnInfo.setCreateTime(new Date());

stockThsGnInfo.setUpdateTime(new Date());

stockThsGnInfoDao.insert(stockThsGnInfo);

} catch (Exception e) {

LOGGER.error("插入同花順概念板塊數據出現異常:", e);

}

}

}

public BigDecimal parseValueToBigDecimal(String value) {

if (StringUtils.isEmpty(value)) {

return BigDecimal.ZERO;

} else if ("--".equals(value)) {

return BigDecimal.ZERO;

} else if (value.endsWith("億")) {

return new BigDecimal(value.substring(0, value.length() - 1)).multiply(BigDecimal.ONE);

}

return new BigDecimal(value);

}

public boolean clicktoOneGnNextPage(WebDriver webDriver, String oneGnHtml, String key, String crawlerDateStr) throws InterruptedException {

// 是否包含下一頁 String pageNumber = includeNextPage(oneGnHtml);

if (!StringUtils.isEmpty(pageNumber)) {

WebElement nextPageElement = webDriver.findElement(By.linkText("下一頁"));

webDriver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);

nextPageElement.click();

Thread.sleep(700);

String nextPageHtml = webDriver.getPageSource();

LOGGER.info("下一頁:");

LOGGER.info(nextPageHtml);

// TODO 解析并存儲數據 parseHtmlAndInsertData(nextPageHtml, key, crawlerDateStr);

clicktoOneGnNextPage(webDriver, nextPageHtml, key, crawlerDateStr);

}

return true;

}

public String includeNextPage(String html) {

Document document = Jsoup.parse(html);

List list = document.getElementsByTag("a");

for (Element element : list) {

String a = element.text();

if ("下一頁".equals(a)) {

String pageNumber = element.attr("page");

return pageNumber;

}

}

return null;

}

}

最后對,概念板塊的頁面數據進行解析入庫。

數據展示

如果遇到問題,可以關注我的公眾號:java之旅或掃描下方二維碼,回復【加群】,加我個人微信詢問我

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/276743.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/276743.shtml
英文地址,請注明出處:http://en.pswp.cn/news/276743.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

開會=浪費時間?阿里技術團隊這樣開項目復盤會

2019獨角獸企業重金招聘Python工程師標準>>> 阿里妹導讀&#xff1a;復盤是項目結束后必不可少的階段&#xff0c;好的復盤會議能夠有效地促進團隊成長。今天&#xff0c;阿里項目管理專家鹿迦以自身的經驗&#xff0c;為大家分享如何做好一個項目的復盤。這篇文章分…

Spring @Value注解無法正確賦值問題

正確的調用方式為&#xff1a; Component public class IconProperties {Value("${icon.url}")private String url; } public class test{AutowiredIconProperties icon;public void test(){ String url icon.url; } } 這里有三個需要注意的點&#xff1a; 1.Value…

Extjs中使用FusionChart舉例

一 前言&#xff1a; 在項目實施中&#xff0c;設計統計部分經常會使用圖表進行顯示&#xff0c;在Extjs3中內置了圖表控件&#xff0c;但實際表現無法達到3D的美觀效果&#xff0c;通過查找FusionChart可以實現比較美觀的3D或2D圖表顯示。注&#xff1a;FusionChart是個商業…

drawitem設置指定行的背景顏色_Java 為 Excel 中的行設置交替背景色

點擊上方 好好學java &#xff0c;選擇 星標 公眾號重磅資訊、干貨&#xff0c;第一時間送達今日推薦&#xff1a;牛人 20000 字的 Spring Cloud 總結&#xff0c;太硬核了~作者&#xff1a;Jazzz鏈接&#xff1a;https://www.cnblogs.com/jazz-z/p/12665819.html在制作Excel表…

常見的關系型數據庫和非關系型數據及其區別

一、關系型數據庫 關系型數據庫最典型的數據結構是表&#xff0c;由二維表及其之間的聯系所組成的一個數據組織 優點&#xff1a;1、易于維護&#xff1a;都是使用表結構&#xff0c;格式一致&#xff1b;2、使用方便&#xff1a;SQL語言通用&#xff0c;可用于復雜查詢&#x…

逆序數技巧 - 牛客

鏈接&#xff1a;https://ac.nowcoder.com/acm/contest/308/D來源&#xff1a;牛客網 題目描述 tokitsukaze給你一個長度為n的序列&#xff0c;這個序列是1到n的一種排列。然后她會進行q次操作。每次操作會給你L R k這三個數&#xff0c;表示區間[L,R]往右移動k次。移動一次的…

Ajax跨域提交JSON和JSONP

可以直接使用$.getJSON()方法實現跨域請求&#xff0c;參數中必須加上callback&#xff0c;如&#xff1a; var jsonpUrl http://www.test.com/index.php?cApi_Order&aAddOrder&callback?;var param {uid:uid,type:type,cityId:cityId};$.getJSON(jsonpUrl, param,…

mysql數據庫商業版與社區版的區別

1、商業版本組織管理與測試環節控制更嚴格&#xff0c;穩定性方面&#xff0c;會比社區版本更穩定。 2、mysql是成熟產品&#xff0c;商業版與社區版之間性能方面相差不大。 3、商業版不遵守GPL協議&#xff0c;社區版遵守GPL協議可以免費使用。 4、使用商業版后可以購買相關的…

UML的奧妙 - 學習UML筆記(1)

前兩天買了一本《大象 Thinking in UML》&#xff0c;其實本就有學習UML的念頭&#xff0c;但都因這樣那樣的事兒耽擱了&#xff0c;當然&#xff0c;也有些惰性在作祟...... 閑話少說&#xff0c;這本書看完了一章&#xff0c;發現還是不錯的&#xff0c;先把這兩天的學習情況…

無法檢查指定的位置是否位于cfs上_(干貨分享)一文搞明白 節氣門位置傳感器的作用、故障類型與癥狀、診斷方法...

1 位置節氣門位置傳感器(ThrottlePositionSensor&#xff0c;TPS)&#xff0c;位于節氣門體上&#xff0c;其安裝形式因節氣門結構的不同而有所差異&#xff1a;對于傳統的機械拉索式節氣門&#xff0c;節氣門位置傳感器通常以一個獨立元件的形式安裝在節氣門體的側面&#xf…

盒子模型

1 <!doctype html>2 <html>3 <head>4 <title>盒子模型</title>5 <meta charset"utf-8">6 <meta name"keywords", content"">7 <meta name"description&…

表單跨域提交

利用form表單跨域post 現在ajax應用這么廣泛&#xff0c;一般的應用都是直接通過異步調用就可以了&#xff0c;但是有些東西必須要使用post&#xff0c;而且是跨域的時候&#xff0c;ajax異步調用的方式就無能為力了。當然現在也有很多種辦法&#xff0c;比如通過flash中轉去po…

Asp.net(C#)-顯示所有緩存 清除所有緩存

//清除所有緩存protectedvoidRemoveAllCache() { System.Web.Caching.Cache _cache HttpRuntime.Cache; IDictionaryEnumerator CacheEnum _cache.GetEnumerator(); ArrayList al new ArrayList(); while (CacheEnum.MoveNext()) { …

mysql數據庫三大引擎優缺點

1.MyISAM 特性&#xff1a; ①不支持事務。 ②表級鎖定&#xff0c;并發性能大大降低。 ③讀寫互相阻塞。 適用場景&#xff1a; ①不支持事務。 ②并發相對較低&#xff0c;表鎖定。 ③執行大量select語句操作的表。 ④count(*)操作較快。 ⑤不支持外鍵。 注&#xff1a;查詢速…

Python--day60--一個簡單(不完整)的web框架

轉載于:https://www.cnblogs.com/xudj/p/10091775.html

activemq 發兩條只收到一條_淺談ActiveMQ與使用

更多大數據架構、實戰經驗&#xff0c;歡迎關注【大數據每日嗶嗶】&#xff0c;期待與你一起成長&#xff01;本文將介紹一下 ActiveMQ 的安裝、原理和簡單實戰。一、什么是消息中間件消息中間件顧名思義實現的就是在兩個系統或兩個客戶端之間進行消息傳送二、什么是ActiveMQAc…

php發送get、post請求的幾種方法

方法1: 用file_get_contents 以get方式獲取內容 <?php $urlhttp://www.domain.com/; $html file_get_contents($url); echo $html; ?>方法2: 用fopen打開url, 以get方式獲取內容<?php $fp fopen($url, r); stream_get_meta_data($fp); while(!feof($fp)) { $res…

ZZ:深入理解new

new的過程當我們使用關鍵字new在堆上動態創建一個對象時&#xff0c;它實際上做了三件事&#xff1a;獲得一塊內存空間、調用構造函數、返回正確的指針。當然&#xff0c;如果我們創建的是簡單類型的變量&#xff0c;那么第二步會被省略。假如我們定義了如下一個類A&#xff1a…

mysql數據庫的優缺點

優點1. 通常存儲過程 標題有助于提高應用程序的性能。因為當你創建他的時候就已經編譯了&#xff0c;只不過是按需編譯的。2.存儲過程有助于減少應用程序和數據庫服務器之間的流量&#xff0c;因為應用程序不必發送多個冗長的SQL語句&#xff0c;而只能發送存儲過程的名稱和參數…

大數據小白系列——HDFS(1)

【注1&#xff1a;結尾有大福利&#xff01;】 【注2&#xff1a;想寫一個大數據小白系列&#xff0c;介紹大數據生態系統中的主要成員&#xff0c;理解其原理&#xff0c;明白其用途&#xff0c;萬一有用呢&#xff0c;對不對。】 大數據是什么&#xff1f;拋開那些高大上但籠…