手把手教你如何用Crawlab構建技術文章聚合平臺(一)

      網友投稿 1077 2025-04-02

      背景


      說到爬蟲,大多數程序員想到的是scrapy這樣受人歡迎的框架。scrapy的確不錯,而且有很強大的生態圈,有gerapy等優秀的可視化界面。但是,它還是有一些不能做到的事情,例如在頁面上做翻頁點擊操作、移動端抓取等等。對于這些新的需求,可以用Selenium、Puppeteer、Appium這些自動化測試框架繞開繁瑣的動態內容,直接模擬用戶操作進行抓取。可惜的是,這些框架不是專門的爬蟲框架,不能對爬蟲進行集中管理,因此對于一個多達數十個爬蟲的大型項目來說有些棘手。

      Crawlab是一個基于Celery的分布式通用爬蟲管理平臺,擅長將不同編程語言編寫的爬蟲整合在一處,方便監控和管理。Crawlab有精美的可視化界面,能對多個爬蟲進行運行和管理。任務調度引擎是本身支持分布式架構的Celery,因此Crawlab可以天然集成分布式爬蟲。有一些朋友認為Crawlab只是一個任務調度引擎,其實這樣認為并不完全正確。Crawlab是類似Gerapy這樣的專注于爬蟲的管理平臺。

      本文將介紹如何使用Crawlab和Puppeteer抓取主流的技術博客文章,然后用Flask+Vue搭建一個小型的技術文章聚合平臺。

      Crawlab

      在前一篇文章《分布式通用爬蟲管理平臺Crawlab》已介紹了Crawlab的架構以及安裝使用,這里快速介紹一下如何安裝、運行、使用Crawlab。

      安裝

      到Crawlab的Github Repo用克隆一份到本地。

      git?clone?https://github.com/tikazyq/crawlab

      安裝相應的依賴包和庫。

      cd?crawlab

      #?安裝python依賴

      pip?install?-r?crawlab/requirements

      #?安裝前端依賴

      cd?frontend

      npm?install

      安裝mongodb和redis-server。Crawlab將用MongoDB作為結果集以及運行操作的儲存方式,Redis作為Celery的任務隊列,因此需要安裝這兩個數據庫。

      運行

      在運行之前需要對Crawlab進行一些配置,配置文件為config.py。

      #?project?variables

      PROJECT_SOURCE_FILE_FOLDER?=?'/Users/yeqing/projects/crawlab/spiders'?#?爬蟲源碼根目錄

      PROJECT_DEPLOY_FILE_FOLDER?=?'/var/crawlab'??#?爬蟲部署根目錄

      PROJECT_LOGS_FOLDER?=?'/var/logs/crawlab'??#?日志目錄

      PROJECT_TMP_FOLDER?=?'/tmp'??#?臨時文件目錄

      #?celery?variables

      BROKER_URL?=?'redis://192.168.99.100:6379/0'??#?中間者URL,連接redis

      CELERY_RESULT_BACKEND?=?'mongodb://192.168.99.100:27017/'??#?CELERY后臺URL

      CELERY_MONGODB_BACKEND_SETTINGS?=?{

      'database':?'crawlab_test',

      'taskmeta_collection':?'tasks_celery',

      }

      CELERY_TIMEZONE?=?'Asia/Shanghai'

      CELERY_ENABLE_UTC?=?True

      #?flower?variables

      FLOWER_API_ENDPOINT?=?'http://localhost:5555/api'??#?Flower服務地址

      #?database?variables

      MONGO_HOST?=?'192.168.99.100'

      MONGO_PORT?=?27017

      MONGO_DB?=?'crawlab_test'

      #?flask?variables

      DEBUG?=?True

      FLASK_HOST?=?'127.0.0.1'

      FLASK_PORT?=?8000

      啟動后端API,也就是一個Flask App,可以直接啟動,或者用gunicorn代替。

      cd?../crawlab

      python?app.py

      啟動Flower服務(抱歉目前集成Flower到App服務中,必須單獨啟動來獲取節點信息,后面的版本不需要這個操作)。

      python?./bin/run_flower.py

      啟動本地Worker。在其他節點中如果想只是想執行任務的話,只需要啟動這一個服務就可以了。

      python?./bin/run_worker.py

      啟動前端服務器。

      cd?../frontend

      npm?run?serve

      使用

      首頁Home中可以看到總任務數、總爬蟲數、在線節點數和總部署數,以及過去30天的任務運行數量。

      點擊側邊欄的Spiders或者上方到Spiders數,可以進入到爬蟲列表頁。

      這些是爬蟲源碼根目錄PROJECT_SOURCE_FILE_FOLDER下的爬蟲。Crawlab會自動掃描該目錄下的子目錄,將子目錄看作一個爬蟲。Action列下有一些操作選項,點擊部署Deploy按鈕將爬蟲部署到所有在線節點中。部署成功后,點擊運行Run按鈕,觸發抓取任務。這時,任務應該已經在執行了。點擊側邊欄的Tasks到任務列表,可以看到已經調度過的爬蟲任務。

      基本使用就是這些,但是Crawlab還能做到更多,大家可以進一步探索,詳情請見Github。

      Puppeteer

      Puppeteer是谷歌開源的基于Chromium和NodeJS的自動化測試工具,可以很方便的讓程序模擬用戶的操作,對瀏覽器進行程序化控制。Puppeteer有一些常用操作,例如點擊,鼠標移動,滑動,截屏,下載文件等等。另外,Puppeteer很類似Selenium,可以定位瀏覽器中網頁元素,將其數據抓取下來。因此,Puppeteer也成為了新的爬蟲利器。

      相對于Selenium,Puppeteer是新的開源項目,而且是谷歌開發,可以使用很多新的特性。對于爬蟲來說,如果前端知識足夠的話,寫數據抓取邏輯簡直不能再簡單。正如其名字一樣,我們是在操作木偶人來幫我們抓取數據,是不是很貼切?

      掘金上已經有很多關于Puppeteer的教程了(爬蟲利器 Puppeteer 實戰、Puppeteer 與 Chrome Headless —— 從入門到爬蟲),這里只簡單介紹一下Puppeteer的安裝和使用。

      安裝

      安裝很簡單,就一行npm install命令,npm會自動下載Chromium并安裝,這個時間會比較長。為了讓安裝好的puppeteer模塊能夠被所有nodejs爬蟲所共享,我們在PROJECT_DEPLOY_FILE_FOLDER目錄下安裝node的包。

      #?PROJECT_DEPLOY_FILE_FOLDER變量值

      cd?/var/crawlab

      #?安裝puppeteer

      npm?i?puppeteer

      #?安裝mongodb

      npm?i?mongodb

      安裝mongodb是為了后續的數據庫操作。

      使用

      以下是Copy/Paste的一段用Puppeteer訪問簡書然后截屏的代碼,非常簡潔。

      const?puppeteer?=?require('puppeteer');

      (async?()?=>?{

      const?browser?=?await?(puppeteer.launch());

      const?page?=?await?browser.newPage();

      await?page.goto('https://www.jianshu.com/u/40909ea33e50');

      await?page.screenshot({

      path:?'jianshu.png',

      type:?'png',

      //?quality:?100,?只對jpg有效

      fullPage:?true,

      //?指定區域截圖,clip和fullPage兩者只能設置一個

      //?clip:?{

      //???x:?0,

      //???y:?0,

      //???width:?1000,

      //???height:?40

      //?}

      });

      browser.close();

      })();

      關于Puppeteer的常用操作,請移步《我常用的puppeteer爬蟲api》。

      編寫爬蟲

      啰嗦了這么久,終于到了萬眾期待的爬蟲時間了。Talk is cheap, show me the code!咦?我們不是已經Show了不少代碼了么...

      由于我們的目標是建立一個技術文章聚合平臺,我們需要去各大技術網站抓取文章。資源當然是越多越好。作為展示用,我們將抓取下面幾個具有代表性的網站:

      掘金

      SegmentFault

      CSDN

      背景

      說到爬蟲,大多數程序員想到的是scrapy這樣受人歡迎的框架。scrapy的確不錯,而且有很強大的生態圈,有gerapy等優秀的可視化界面。但是,它還是有一些不能做到的事情,例如在頁面上做翻頁點擊操作、移動端抓取等等。對于這些新的需求,可以用Selenium、Puppeteer、Appium這些自動化測試框架繞開繁瑣的動態內容,直接模擬用戶操作進行抓取。可惜的是,這些框架不是專門的爬蟲框架,不能對爬蟲進行集中管理,因此對于一個多達數十個爬蟲的大型項目來說有些棘手。

      Crawlab是一個基于Celery的分布式通用爬蟲管理平臺,擅長將不同編程語言編寫的爬蟲整合在一處,方便監控和管理。Crawlab有精美的可視化界面,能對多個爬蟲進行運行和管理。任務調度引擎是本身支持分布式架構的Celery,因此Crawlab可以天然集成分布式爬蟲。有一些朋友認為Crawlab只是一個任務調度引擎,其實這樣認為并不完全正確。Crawlab是類似Gerapy這樣的專注于爬蟲的管理平臺。

      本文將介紹如何使用Crawlab和Puppeteer抓取主流的技術博客文章,然后用Flask+Vue搭建一個小型的技術文章聚合平臺。

      Crawlab

      在前一篇文章《分布式通用爬蟲管理平臺Crawlab》已介紹了Crawlab的架構以及安裝使用,這里快速介紹一下如何安裝、運行、使用Crawlab。

      到Crawlab的Github Repo用克隆一份到本地。

      git?clone?https://github.com/tikazyq/crawlab

      安裝相應的依賴包和庫。

      cd?crawlab

      #?安裝python依賴

      pip?install?-r?crawlab/requirements

      #?安裝前端依賴

      cd?frontend

      npm?install

      安裝mongodb和redis-server。Crawlab將用MongoDB作為結果集以及運行操作的儲存方式,Redis作為Celery的任務隊列,因此需要安裝這兩個數據庫。

      在運行之前需要對Crawlab進行一些配置,配置文件為config.py。

      #?project?variables

      PROJECT_SOURCE_FILE_FOLDER?=?'/Users/yeqing/projects/crawlab/spiders'?#?爬蟲源碼根目錄

      PROJECT_DEPLOY_FILE_FOLDER?=?'/var/crawlab'??#?爬蟲部署根目錄

      PROJECT_LOGS_FOLDER?=?'/var/logs/crawlab'??#?日志目錄

      PROJECT_TMP_FOLDER?=?'/tmp'??#?臨時文件目錄

      #?celery?variables

      BROKER_URL?=?'redis://192.168.99.100:6379/0'??#?中間者URL,連接redis

      CELERY_RESULT_BACKEND?=?'mongodb://192.168.99.100:27017/'??#?CELERY后臺URL

      CELERY_MONGODB_BACKEND_SETTINGS?=?{

      'database':?'crawlab_test',

      'taskmeta_collection':?'tasks_celery',

      }

      CELERY_TIMEZONE?=?'Asia/Shanghai'

      CELERY_ENABLE_UTC?=?True

      #?flower?variables

      FLOWER_API_ENDPOINT?=?'http://localhost:5555/api'??#?Flower服務地址

      #?database?variables

      MONGO_HOST?=?'192.168.99.100'

      MONGO_PORT?=?27017

      MONGO_DB?=?'crawlab_test'

      #?flask?variables

      DEBUG?=?True

      FLASK_HOST?=?'127.0.0.1'

      FLASK_PORT?=?8000

      啟動后端API,也就是一個Flask App,可以直接啟動,或者用gunicorn代替。

      cd?../crawlab

      python?app.py

      啟動Flower服務(抱歉目前集成Flower到App服務中,必須單獨啟動來獲取節點信息,后面的版本不需要這個操作)。

      python?./bin/run_flower.py

      啟動本地Worker。在其他節點中如果想只是想執行任務的話,只需要啟動這一個服務就可以了。

      python?./bin/run_worker.py

      啟動前端服務器。

      cd?../frontend

      npm?run?serve

      首頁Home中可以看到總任務數、總爬蟲數、在線節點數和總部署數,以及過去30天的任務運行數量。

      點擊側邊欄的Spiders或者上方到Spiders數,可以進入到爬蟲列表頁。

      這些是爬蟲源碼根目錄PROJECT_SOURCE_FILE_FOLDER下的爬蟲。Crawlab會自動掃描該目錄下的子目錄,將子目錄看作一個爬蟲。Action列下有一些操作選項,點擊部署Deploy按鈕將爬蟲部署到所有在線節點中。部署成功后,點擊運行Run按鈕,觸發抓取任務。這時,任務應該已經在執行了。點擊側邊欄的Tasks到任務列表,可以看到已經調度過的爬蟲任務。

      基本使用就是這些,但是Crawlab還能做到更多,大家可以進一步探索,詳情請見Github。

      Puppeteer

      Puppeteer是谷歌開源的基于Chromium和NodeJS的自動化測試工具,可以很方便的讓程序模擬用戶的操作,對瀏覽器進行程序化控制。Puppeteer有一些常用操作,例如點擊,鼠標移動,滑動,截屏,下載文件等等。另外,Puppeteer很類似Selenium,可以定位瀏覽器中網頁元素,將其數據抓取下來。因此,Puppeteer也成為了新的爬蟲利器。

      相對于Selenium,Puppeteer是新的開源項目,而且是谷歌開發,可以使用很多新的特性。對于爬蟲來說,如果前端知識足夠的話,寫數據抓取邏輯簡直不能再簡單。正如其名字一樣,我們是在操作木偶人來幫我們抓取數據,是不是很貼切?

      掘金上已經有很多關于Puppeteer的教程了(爬蟲利器 Puppeteer 實戰、Puppeteer 與 Chrome Headless —— 從入門到爬蟲),這里只簡單介紹一下Puppeteer的安裝和使用。

      安裝

      安裝很簡單,就一行npm install命令,npm會自動下載Chromium并安裝,這個時間會比較長。為了讓安裝好的puppeteer模塊能夠被所有nodejs爬蟲所共享,我們在PROJECT_DEPLOY_FILE_FOLDER目錄下安裝node的包。

      #?PROJECT_DEPLOY_FILE_FOLDER變量值

      cd?/var/crawlab

      #?安裝puppeteer

      npm?i?puppeteer

      #?安裝mongodb

      npm?i?mongodb

      安裝mongodb是為了后續的數據庫操作。

      使用

      以下是Copy/Paste的一段用Puppeteer訪問簡書然后截屏的代碼,非常簡潔。

      const?puppeteer?=?require('puppeteer');

      (async?()?=>?{

      const?browser?=?await?(puppeteer.launch());

      const?page?=?await?browser.newPage();

      await?page.goto('https://www.jianshu.com/u/40909ea33e50');

      await?page.screenshot({

      path:?'jianshu.png',

      type:?'png',

      //?quality:?100,?只對jpg有效

      fullPage:?true,

      //?指定區域截圖,clip和fullPage兩者只能設置一個

      //?clip:?{

      //???x:?0,

      //???y:?0,

      //???width:?1000,

      //???height:?40

      //?}

      });

      browser.close();

      })();

      關于Puppeteer的常用操作,請移步《我常用的puppeteer爬蟲api》。

      編寫爬蟲

      啰嗦了這么久,終于到了萬眾期待的爬蟲時間了。Talk is cheap, show me the code!咦?我們不是已經Show了不少代碼了么...

      由于我們的目標是建立一個技術文章聚合平臺,我們需要去各大技術網站抓取文章。資源當然是越多越好。作為展示用,我們將抓取下面幾個具有代表性的網站:

      掘金

      SegmentFault

      CSDN

      研究發現這三個網站都是由Ajax獲取文章列表,生成動態內容以作為傳統的分頁替代。這對于Puppeteer來說很容易處理,因為Puppeteer繞開了解析Ajax這一部分,瀏覽器會自動處理這樣的操作和請求,我們只著重關注數據獲取就行了。三個網站的抓取策略基本相同,我們以掘金為例著重講解。

      掘金

      首先是引入Puppeteer和打開網頁。

      const?puppeteer?=?require('puppeteer');

      const?MongoClient?=?require('mongodb').MongoClient;

      (async?()?=>?{

      //?browser

      const?browser?=?await?(puppeteer.launch({

      headless:?true

      }));

      //?define?start?url

      const?url?=?'https://juejin.im';

      //?start?a?new?page

      const?page?=?await?browser.newPage();

      ...

      })();

      headless設置為true可以讓瀏覽器以headless的方式運行,也就是指瀏覽器不用在界面中打開,它會在后臺運行,用戶是看不到瀏覽器的。browser.newPage()將新生成一個標簽頁。后面的操作基本就圍繞著生成的page來進行。

      接下來我們讓瀏覽器導航到start url。

      ...

      //?navigate?to?url

      try?{

      await?page.goto(url,?{waitUntil:?'domcontentloaded'});

      await?page.waitFor(2000);

      }?catch?(e)?{

      console.error(e);

      //?close?browser

      browser.close();

      //?exit?code?1?indicating?an?error?happened

      code?=?1;

      process.emit("exit?");

      process.reallyExit(code);

      return

      }

      ...

      這里try catch的操作是為了處理瀏覽器訪問超時的錯誤。當訪問超時時,設置exit code為1表示該任務失敗了,這樣Crawlab會將該任務狀態設置為FAILURE。

      然后我們需要下拉頁面讓瀏覽器可以讀取下一頁。

      ...

      //?scroll?down?to?fetch?more?data

      for?(let?i?=?0;?i?

      console.log('Pressing?PageDown...');

      await?page.keyboard.press('PageDown',?200);

      await?page.waitFor(100);

      }

      ...

      翻頁完畢后,就開始抓取數據了。

      ...

      //?scrape?data

      const?results?=?await?page.evaluate(()?=>?{

      let?results?=?[];

      document.querySelectorAll('.entry-list?>?.item').forEach(el?=>?{

      if?(!el.querySelector('.title'))?return;

      results.push({

      url:?'https://juejin.com'?+?el.querySelector('.title').getAttribute('href'),

      title:?el.querySelector('.title').innerText

      });

      });

      return?results;

      });

      ...

      page.evaluate可以在瀏覽器Console中進行JS操作。這段代碼其實可以直接在瀏覽器Console中直接運行。調試起來是不是方便到爽?前端工程師們,開始歡呼吧!

      獲取了數據,接下來我們需要將其儲存在數據庫中。

      ...

      //?open?database?connection

      const?client?=?await?MongoClient.connect('mongodb://192.168.99.100:27017');

      let?db?=?await?client.db('crawlab_test');

      const?colName?=?process.env.CRAWLAB_COLLECTION?||?'results_juejin';

      const?taskId?=?process.env.CRAWLAB_TASK_ID;

      const?col?=?db.collection(colName);

      //?save?to?database

      for?(let?i?=?0;?i?

      //?de-duplication

      const?r?=?await?col.findOne({url:?results[i]});

      if?(r)?continue;

      //?assign?taskID

      results[i].task_id?=?taskId;

      //?insert?row

      await?col.insertOne(results[i]);

      }

      ...

      這樣,我們就將掘金最新的文章數據保存在了數據庫中。其中,我們用url字段做了去重處理。CRAWLAB_COLLECTION和CRAWLAB_TASK_ID是Crawlab傳過來的環境變量,分別是儲存的collection和任務ID。任務ID需要以task_id為鍵保存起來,這樣在Crawlab中就可以將數據與任務關聯起來了。

      整個爬蟲代碼如下。

      const?puppeteer?=?require('puppeteer');

      const?MongoClient?=?require('mongodb').MongoClient;

      (async?()?=>?{

      //?browser

      const?browser?=?await?(puppeteer.launch({

      headless:?true

      }));

      //?define?start?url

      const?url?=?'https://juejin.im';

      手把手教你如何用Crawlab構建技術文章聚合平臺(一)

      //?start?a?new?page

      const?page?=?await?browser.newPage();

      //?navigate?to?url

      try?{

      await?page.goto(url,?{waitUntil:?'domcontentloaded'});

      await?page.waitFor(2000);

      }?catch?(e)?{

      console.error(e);

      //?close?browser

      browser.close();

      //?exit?code?1?indicating?an?error?happened

      code?=?1;

      process.emit("exit?");

      process.reallyExit(code);

      return

      }

      //?scroll?down?to?fetch?more?data

      for?(let?i?=?0;?i?

      console.log('Pressing?PageDown...');

      await?page.keyboard.press('PageDown',?200);

      await?page.waitFor(100);

      }

      //?scrape?data

      const?results?=?await?page.evaluate(()?=>?{

      let?results?=?[];

      document.querySelectorAll('.entry-list?>?.item').forEach(el?=>?{

      if?(!el.querySelector('.title'))?return;

      results.push({

      url:?'https://juejin.com'?+?el.querySelector('.title').getAttribute('href'),

      title:?el.querySelector('.title').innerText

      });

      });

      return?results;

      });

      //?open?database?connection

      const?client?=?await?MongoClient.connect('mongodb://192.168.99.100:27017');

      let?db?=?await?client.db('crawlab_test');

      const?colName?=?process.env.CRAWLAB_COLLECTION?||?'results_juejin';

      const?taskId?=?process.env.CRAWLAB_TASK_ID;

      const?col?=?db.collection(colName);

      //?save?to?database

      for?(let?i?=?0;?i?

      //?de-duplication

      const?r?=?await?col.findOne({url:?results[i]});

      if?(r)?continue;

      //?assign?taskID

      results[i].task_id?=?taskId;

      //?insert?row

      await?col.insertOne(results[i]);

      }

      console.log(`results.length:?${results.length}`);

      //?close?database?connection

      client.close();

      //?shutdown?browser

      browser.close();

      })();

      SegmentFault & CSDN

      這兩個網站的爬蟲代碼基本與上面的爬蟲一樣,只是一些參數不一樣而已。我們的爬蟲項目結構如下。

      運行爬蟲

      在Crawlab中打開Spiders,我們可以看到剛剛編寫好的爬蟲。

      點擊各個爬蟲的View查看按鈕,進入到爬蟲詳情。

      在Execute Command中輸入爬蟲執行命令。對掘金爬蟲來說,是node juejin_spider.js。輸入完畢后點擊Save保存。然后點擊Deploy部署爬蟲。最后點擊Run運行爬蟲。

      點擊左上角到刷新按鈕可以看到剛剛運行的爬蟲任務已經在運行了。點擊Create Time后可以進入到任務詳情。Overview標簽中可以看到任務信息,Log標簽可以看到日志信息,Results信息中可以看到抓取結果。目前在Crawlab結果列表中還不支持數據導出,但是不久的版本中肯定會將導出功能加入進來。

      總結

      在這一小節,我們已經能夠將Crawlab運行起來,并且能用Puppeteer編寫抓取三大網站技術文章的爬蟲,并且能夠用Crawlab運行爬蟲,并且讀取抓取后的數據。下一節,我們將用Flask+Vue做一個簡單的技術文章聚合網站。能看到這里的都是有耐心的好同學,贊一個。

      Github: tikazyq/crawlab

      Scrapy Python

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:幾個值得收集的優秀技能 這就像得到一張表格一樣簡單(豐富的技能)
      下一篇:Excel中簡單乘法計算公式的運用方法
      相關文章
      久久精品亚洲AV久久久无码| 亚洲影院在线观看| 亚洲黄色在线电影| 国产av无码专区亚洲av果冻传媒| 久久久国产亚洲精品| 亚洲欧洲日韩综合| 亚洲视频在线观看地址| 亚洲av无码av制服另类专区| 亚洲av无码乱码国产精品fc2| 久久被窝电影亚洲爽爽爽| 亚洲欧洲精品无码AV| 亚洲午夜日韩高清一区| 色噜噜AV亚洲色一区二区| 国产成人亚洲影院在线观看| 国产精品亚洲玖玖玖在线观看| 在线观看国产区亚洲一区成人| 亚洲一级特黄无码片| 色噜噜亚洲精品中文字幕 | 亚洲婷婷在线视频| 亚洲成a人片77777群色| 亚洲av产在线精品亚洲第一站| 亚洲1区1区3区4区产品乱码芒果| www.亚洲日本| 亚洲中文字幕一区精品自拍| 亚洲精品天堂无码中文字幕| 337p日本欧洲亚洲大胆人人| 亚洲高清无码在线观看| 久久亚洲2019中文字幕| 日本亚洲欧洲免费天堂午夜看片女人员 | 亚洲精品宾馆在线精品酒店| 九九精品国产亚洲AV日韩| 久久久亚洲精华液精华液精华液| 亚洲AV成人精品日韩一区18p| 亚洲中文字幕视频国产| 国产亚洲精AA在线观看SEE | 亚洲欧美黑人猛交群| 在线观看亚洲专区| 国产精品亚洲美女久久久| 亚洲av无码成h人动漫无遮挡| 亚洲理论精品午夜电影| 亚洲七久久之综合七久久|