日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

分布式爬虫系统设计、实现与实战:爬取京东、苏宁易购全网手机商品数据+MySQL、HBase存储...

發布時間:2025/4/5 数据库 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 分布式爬虫系统设计、实现与实战:爬取京东、苏宁易购全网手机商品数据+MySQL、HBase存储... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

http://blog.51cto.com/xpleaf/2093952

1 概述

在不用爬蟲框架的情況,經過多方學習,嘗試實現了一個分布式爬蟲系統,并且可以將數據保存到不同地方,類似MySQL、HBase等。

基于面向接口的編碼思想來開發,因此這個系統具有一定的擴展性,有興趣的朋友直接看一下代碼,就能理解其設計思想,雖然代碼目前來說很多地方還是比較緊耦合,但只要花些時間和精力,很多都是可抽取出來并且可配置化的。

因為時間的關系,我只寫了京東和蘇寧易購兩個網站的爬蟲,但是完全可以實現不同網站爬蟲的隨機調度,基于其代碼結構,再寫國美、天貓等的商品爬取,難度不大,但是估計需要花很多時間和精力。因為在解析網頁的數據時,實際上需要花很多時間,比如我在爬取蘇寧易購商品的價格時,價格是異步獲取的,并且其api是一長串的數字組合,我花了幾個小時的時間才發現其規律,當然也承認,我的經驗不足。

這個系統的設計,除了基本的數據爬取以外,更關注以下幾個方面的問題:

  • 1.如何實現分布式,同一個程序打包后分發到不同的節點運行時,不影響整體的數據爬取
  • 2.如何實現url隨機循環調度,核心是針對不同的頂級域名做隨機
  • 3.如何定時向url倉庫中添加種子url,達到不讓爬蟲系統停下來的目的
  • 4.如何實現對爬蟲節點程序的監控,并能夠發郵件報警
  • 5.如何實現一個隨機IP代理庫,目的跟第2點有點類似,都是為了反反爬蟲

下面會針對這個系統來做一個整體的基本介紹,其實我在代碼中都有非常詳細的注釋,有興趣的朋友可以參考一下代碼,最后我會給出一些我爬蟲時的數據分析。

另外需要注意的是,這個爬蟲系統是基于Java實現的,但是語言本身仍然不是最重要的,有興趣的朋友可以嘗試用Python實現。

2 分布式爬蟲系統架構

整體系統架構如下:

所以從上面的架構可以看出,整個系統主要分為三個部分:

  • 爬蟲系統
  • URL調度系統
  • 監控報警系統

爬蟲系統就是用來爬取數據的,因為系統設計為分布式,因此,爬蟲程序本身可以運行在不同的服務器節點上。

url調度系統核心在于url倉庫,所謂的url倉庫其實就是用Redis保存了需要爬取的url列表,并且在我們的url調度器中根據一定的策略來消費其中的url,從這個角度考慮,url倉庫其實也是一個url隊列。

監控報警系統主要是對爬蟲節點進行監控,雖然并行執行的爬蟲節點中的某一個掛掉了對整體數據爬取本身沒有影響(只是降低了爬蟲的速度),但是我們還是希望知道能夠主動接收到節點掛掉的通知,而不是被動地發現。

下面將會針對以上三個方面并結合部分代碼片段來對整個系統的設計思路做一些基本的介紹,對系統完整實現有濃厚興趣的朋友可以直接參考源代碼。

3 爬蟲系統

(說明:zookeeper監控屬于監控報警系統,url調度器屬于URL調度系統)

爬蟲系統是一個獨立運行的進程,我們把我們的爬蟲系統打包成jar包,然后分發到不同的節點上執行,這樣并行爬取數據可以提高爬蟲的效率。

3.1 隨機IP代理器

加入隨機IP代理主要是為了反反爬蟲,因此如果有一個IP代理庫,并且可以在構建http客戶端時可以隨機地使用不同的代理,那么對我們進行反反爬蟲則會有很大的幫助。

在系統中使用IP代理庫,需要先在文本文件中添加可用的代理地址信息:

# IPProxyRepository.txt 58.60.255.104:8118 219.135.164.245:3128 27.44.171.27:9999 219.135.164.245:3128 58.60.255.104:8118 58.252.6.165:9000 ......

需要注意的是,上面的代理IP是我在西刺代理上拿到的一些代理IP,不一定可用,建議是自己花錢購買一批代理IP,這樣可以節省很多時間和精力去尋找代理IP。

然后在構建http客戶端的工具類中,當第一次使用工具類時,會把這些代理IP加載進內存中,加載到Java的一個HashMap:

// IP地址代理庫Map private static Map<String, Integer> IPProxyRepository = new HashMap<>(); private static String[] keysArray = null; // keysArray是為了方便生成隨機的代理對象 /** * 初次使用時使用靜態代碼塊將IP代理庫加載進set中 */ static { InputStream in = HttpUtil.class.getClassLoader().getResourceAsStream("IPProxyRepository.txt"); // 加載包含代理IP的文本 // 構建緩沖流對象 InputStreamReader isr = new InputStreamReader(in); BufferedReader bfr = new BufferedReader(isr); String line = null; try { // 循環讀每一行,添加進map中 while ((line = bfr.readLine()) != null) { String[] split = line.split(":"); // 以:作為分隔符,即文本中的數據格式應為192.168.1.1:4893 String host = split[0]; int port = Integer.valueOf(split[1]); IPProxyRepository.put(host, port); } Set<String> keys = IPProxyRepository.keySet(); keysArray = keys.toArray(new String[keys.size()]); // keysArray是為了方便生成隨機的代理對象 } catch (IOException e) { e.printStackTrace(); } }

之后,在每次構建http客戶端時,都會先到map中看是否有代理IP,有則使用,沒有則不使用代理:

CloseableHttpClient httpClient = null; HttpHost proxy = null; if (IPProxyRepository.size() > 0) { // 如果ip代理地址庫不為空,則設置代理 proxy = getRandomProxy(); httpClient = HttpClients.custom().setProxy(proxy).build(); // 創建httpclient對象 } else { httpClient = HttpClients.custom().build(); // 創建httpclient對象 } HttpGet request = new HttpGet(url); // 構建htttp get請求 ......

隨機代理對象則通過下面的方法生成:

/*** 隨機返回一個代理對象** @return*/ public static HttpHost getRandomProxy() { // 隨機獲取host:port,并構建代理對象 Random random = new Random(); String host = keysArray[random.nextInt(keysArray.length)]; int port = IPProxyRepository.get(host); HttpHost proxy = new HttpHost(host, port); // 設置http代理 return proxy; }

這樣,通過上面的設計,基本就實現了隨機IP代理器的功能,當然,其中還有很多可以完善的地方,比如,當使用這個IP代理而請求失敗時,是否可以把這一情況記錄下來,當超過一定次數時,再將其從代理庫中刪除,同時生成日志供開發人員或運維人員參考,這是完全可以實現的,不過我就不做這一步功能了。

3.2 網頁下載器

網頁下載器就是用來下載網頁中的數據,主要基于下面的接口開發:

/*** 網頁數據下載*/ public interface IDownload { /** * 下載給定url的網頁數據 * @param url * @return */ public Page download(String url); }

基于此,在系統中只實現了一個http get的下載器,但是也可以完成我們所需要的功能了:

/*** 數據下載實現類*/ public class HttpGetDownloadImpl implements IDownload { @Override public Page download(String url) { Page page = new Page(); String content = HttpUtil.getHttpContent(url); // 獲取網頁數據 page.setUrl(url); page.setContent(content); return page; } }

3.3 網頁解析器

網頁解析器就是把下載的網頁中我們感興趣的數據解析出來,并保存到某個對象中,供數據存儲器進一步處理以保存到不同的持久化倉庫中,其基于下面的接口進行開發:

/*** 網頁數據解析*/ public interface IParser { public void parser(Page page); }

網頁解析器在整個系統的開發中也算是比較重頭戲的一個組件,功能不復雜,主要是代碼比較多,針對不同的商城不同的商品,對應的解析器可能就不一樣了,因此需要針對特別的商城的商品進行開發,因為很顯然,京東用的網頁模板跟蘇寧易購的肯定不一樣,天貓用的跟京東用的也肯定不一樣,所以這個完全是看自己的需要來進行開發了,只是說,在解析器開發的過程當中會發現有部分重復代碼,這時就可以把這些代碼抽象出來開發一個工具類了。

目前在系統中爬取的是京東和蘇寧易購的手機商品數據,因此與就寫了這兩個實現類:

/*** 解析京東商品的實現類*/ public class JDHtmlParserImpl implements IParser { ...... } /** * 蘇寧易購網頁解析 */ public class SNHtmlParserImpl implements IParser { ...... }

3.4 數據存儲器

數據存儲器主要是將網頁解析器解析出來的數據對象保存到不同的,而對于本次爬取的手機商品,數據對象是下面一個Page對象:

/*** 網頁對象,主要包含網頁內容和商品數據*/ public class Page { private String content; // 網頁內容 private String id; // 商品Id private String source; // 商品來源 private String brand; // 商品品牌 private String title; // 商品標題 private float price; // 商品價格 private int commentCount; // 商品評論數 private String url; // 商品地址 private String imgUrl; // 商品圖片地址 private String params; // 商品規格參數 private List<String> urls = new ArrayList<>(); // 解析列表頁面時用來保存解析的商品url的容器 }

對應的,在MySQL中,表數據結構如下:

-- ---------------------------- -- Table structure for phone -- ---------------------------- DROP TABLE IF EXISTS `phone`; CREATE TABLE `phone` ( `id` varchar(30) CHARACTER SET armscii8 NOT NULL COMMENT '商品id', `source` varchar(30) NOT NULL COMMENT '商品來源,如jd suning gome等', `brand` varchar(30) DEFAULT NULL COMMENT '手機品牌', `title` varchar(255) DEFAULT NULL COMMENT '商品頁面的手機標題', `price` float(10,2) DEFAULT NULL COMMENT '手機價格', `comment_count` varchar(30) DEFAULT NULL COMMENT '手機評論', `url` varchar(500) DEFAULT NULL COMMENT '手機詳細信息地址', `img_url` varchar(500) DEFAULT NULL COMMENT '圖片地址', `params` text COMMENT '手機參數,json格式存儲', PRIMARY KEY (`id`,`source`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

而在HBase中的表結構則為如下:

## cf1 存儲 id source price comment brand url ## cf2 存儲 title params imgUrl create 'phone', 'cf1', 'cf2' ## 在HBase shell中查看創建的表 hbase(main):135:0> desc 'phone' Table phone is ENABLED phone COLUMN FAMILIES DESCRIPTION {NAME => 'cf1', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK _ENCODING => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'} {NAME => 'cf2', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK _ENCODING => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'} 2 row(s) in 0.0350 seconds

即在HBase中建立了兩個列族,分別為cf1、cf2,其中cf1用來保存id source price comment brand url字段信息,cf2用來保存title params imgUrl字段信息。

不同的數據存儲用的是不同的實現類,但是其都是基于下面同一個接口開發的:

/*** 商品數據的存儲*/ public interface IStore { public void store(Page page); }

然后基于此開發了MySQL的存儲實現類、HBase的存儲實現類還有控制臺的輸出實現類,如MySQL的存儲實現類,其實就是簡單的數據插入語句:

/*** 使用dbc數據庫連接池將數據寫入mysql表中*/ public class MySQLStoreImpl implements IStore { private QueryRunner queryRunner = new QueryRunner(DBCPUtil.getDataSource()); @Override public void store(Page page) { String sql = "insert into phone(id, source, brand, title, price, comment_count, url, img_url, params) values(?, ?, ?, ?, ?, ?, ?, ?, ?)"; try { queryRunner.update(sql, page.getId(), page.getSource(), page.getBrand(), page.getTitle(), page.getPrice(), page.getCommentCount(), page.getUrl(), page.getImgUrl(), page.getParams()); } catch (SQLException e) { e.printStackTrace(); } } }

而HBase的存儲實現類,則是HBase Java API的常用插入語句代碼:

...... // cf1:price Put pricePut = new Put(rowKey); // 必須要做是否為null判斷,否則會有空指針異常 pricePut.addColumn(cf1, "price".getBytes(), page.getPrice() != null ? String.valueOf(page.getPrice()).getBytes() : "".getBytes()); puts.add(pricePut); // cf1:comment Put commentPut = new Put(rowKey); commentPut.addColumn(cf1, "comment".getBytes(), page.getCommentCount() != null ? String.valueOf(page.getCommentCount()).getBytes() : "".getBytes()); puts.add(commentPut); // cf1:brand Put brandPut = new Put(rowKey); brandPut.addColumn(cf1, "brand".getBytes(), page.getBrand() != null ? page.getBrand().getBytes() : "".getBytes()); puts.add(brandPut); ......

當然,至于要將數據存儲在哪個地方,在初始化爬蟲程序時,是可以手動選擇的:

// 3.注入存儲器 iSpider.setStore(new HBaseStoreImpl());

目前還沒有把代碼寫成可以同時存儲在多個地方,按照目前代碼的架構,要實現這一點也比較簡單,修改一下相應代碼就好了。實際上,是可以先把數據保存到MySQL中,然后通過Sqoop導入到HBase中,詳細操作可以參考我寫的Sqoop文章。

仍然需要注意的是,如果確定需要將數據保存到HBase中,請保證你有可用的集群環境,并且需要將如下配置文檔添加到classpath下:

core-site.xml hbase-site.xml hdfs-site.xml

對大數據感興趣的同學可以折騰一下這一點,如果之前沒有接觸過的,直接使用MySQL存儲就好了,只需要在初始化爬蟲程序時注入MySQL存儲器即可:

// 3.注入存儲器 iSpider.setStore(new MySQLStoreImpl());

4 URL調度系統

URL調度系統是實現整個爬蟲系統分布式的橋梁與關鍵,正是通過URL調度系統的使用,才使得整個爬蟲系統可以較為高效(Redis作為存儲)隨機地獲取url,并實現整個系統的分布式。

4.1 URL倉庫

通過架構圖可以看出,所謂的URL倉庫不過是Redis倉庫,即在我們的系統中使用Redis來保存url地址列表,正是這樣,才能保證我們的程序實現分布式,只要保存了url是唯一的,這樣不管我們的爬蟲程序有多少個,最終保存下來的數據都是只有唯一一份的,而不會重復,是通過這樣來實現分布式的。

同時url倉庫中的url地址在獲取時的策略是通過隊列的方式來實現的,待會通過URL調度器的實現即可知道。

另外,在我們的url倉庫中,主要保存了下面的數據:

  • 種子URL列表

Redis的數據類型為list。

種子URL是持久化存儲的,一定時間后,由URL定時器通過種子URL獲取URL,并將其注入到我們的爬蟲程序需要使用的高優先級URL隊列中,這樣就可以保存我們的爬蟲程序可以源源不斷地爬取數據而不需要中止程序的執行。

  • 高優先級URL隊列

Redis的數據類型為set。

什么是高優先級URL隊列?其實它就是用來保存列表url的。

那么什么是列表url呢?

說白了就是一個列表中含有多個商品,以京東為列,我們打開一個手機列表為例:

該地址中包含的不是一個具體商品的url,而是包含了多個我們需要爬取的數據(手機商品)的列表,通過對每個高級url的解析,我們可以獲取到非常多的具體商品url,而具體的商品url,就是低優先url,其會保存到低優先級URL隊列中。

那么以這個系統為例,保存的數據類似如下:

jd.com.higher--https://list.jd.com/list.html?cat=9987,653,655&page=1... suning.com.higher--https://list.suning.com/0-20006-0.html...
  • 低優先級URL隊列

Redis的數據類型為set。

低優先級URL其實就是具體某個商品的URL,如下面一個手機商品:

通過下載該url的數據,并對其進行解析,就能夠獲取到我們想要的數據。

那么以這個系統為例,保存的數據類似如下:

jd.com.lower--https://item.jd.com/23545806622.html... suning.com.lower--https://product.suning.com/0000000000/690128156.html...

4.2 URL調度器

所謂url調度器,其實說白了就是url倉庫java代碼的調度策略,不過因為其核心在于調度,所以將其放到URL調度器中來進行說明,目前其調度基于以下接口開發:

/*** url 倉庫* 主要功能:* 向倉庫中添加url(高優先級的列表,低優先級的商品url)* 從倉庫中獲取url(優先獲取高優先級的url,如果沒有,再獲取低優先級的url)**/ public interface IRepository { /** * 獲取url的方法 * 從倉庫中獲取url(優先獲取高優先級的url,如果沒有,再獲取低優先級的url) * @return */ public String poll(); /** * 向高優先級列表中添加商品列表url * @param highUrl */ public void offerHigher(String highUrl); /** * 向低優先級列表中添加商品url * @param lowUrl */ public void offerLower(String lowUrl); }

其基于Redis作為URL倉庫的實現如下:

/*** 基于Redis的全網爬蟲,隨機獲取爬蟲url:** Redis中用來保存url的數據結構如下:* 1.需要爬取的域名集合(存儲數據類型為set,這個需要先在Redis中添加)* key* spider.website.domains* value(set)* jd.com suning.com gome.com* key由常量對象SpiderConstants.SPIDER_WEBSITE_DOMAINS_KEY 獲得* 2.各個域名所對應的高低優先url隊列(存儲數據類型為list,這個由爬蟲程序解析種子url后動態添加)* key* jd.com.higher* jd.com.lower* suning.com.higher* suning.com.lower* gome.com.higher* gome.come.lower* value(list)* 相對應需要解析的url列表* key由隨機的域名 + 常量 SpiderConstants.SPIDER_DOMAIN_HIGHER_SUFFIX或者SpiderConstants.SPIDER_DOMAIN_LOWER_SUFFIX獲得* 3.種子url列表* key* spider.seed.urls* value(list)* 需要爬取的數據的種子url* key由常量SpiderConstants.SPIDER_SEED_URLS_KEY獲得** 種子url列表中的url會由url調度器定時向高低優先url隊列中*/ public class RandomRedisRepositoryImpl implements IRepository { /** * 構造方法 */ public RandomRedisRepositoryImpl() { init(); } /** * 初始化方法,初始化時,先將redis中存在的高低優先級url隊列全部刪除 * 否則上一次url隊列中的url沒有消耗完時,再停止啟動跑下一次,就會導致url倉庫中有重復的url */ public void init() { Jedis jedis = JedisUtil.getJedis(); Set<String> domains = jedis.smembers(SpiderConstants.SPIDER_WEBSITE_DOMAINS_KEY); String higherUrlKey; String lowerUrlKey; for(String domain : domains) { higherUrlKey = domain + SpiderConstants.SPIDER_DOMAIN_HIGHER_SUFFIX; lowerUrlKey = domain + SpiderConstants.SPIDER_DOMAIN_LOWER_SUFFIX; jedis.del(higherUrlKey, lowerUrlKey); } JedisUtil.returnJedis(jedis); } /** * 從隊列中獲取url,目前的策略是: * 1.先從高優先級url隊列中獲取 * 2.再從低優先級url隊列中獲取 * 對應我們的實際場景,應該是先解析完列表url再解析商品url * 但是需要注意的是,在分布式多線程的環境下,肯定是不能完全保證的,因為在某個時刻高優先級url隊列中 * 的url消耗完了,但實際上程序還在解析下一個高優先級url,此時,其它線程去獲取高優先級隊列url肯定獲取不到 * 這時就會去獲取低優先級隊列中的url,在實際考慮分析時,這點尤其需要注意 * @return */ @Override public String poll() { // 從set中隨機獲取一個頂級域名 Jedis jedis = JedisUtil.getJedis(); String randomDomain = jedis.srandmember(SpiderConstants.SPIDER_WEBSITE_DOMAINS_KEY); // jd.com String key = randomDomain + SpiderConstants.SPIDER_DOMAIN_HIGHER_SUFFIX; // jd.com.higher String url = jedis.lpop(key); if(url == null) { // 如果為null,則從低優先級中獲取 key = randomDomain + SpiderConstants.SPIDER_DOMAIN_LOWER_SUFFIX; // jd.com.lower url = jedis.lpop(key); } JedisUtil.returnJedis(jedis); return url; } /** * 向高優先級url隊列中添加url * @param highUrl */ @Override public void offerHigher(String highUrl) { offerUrl(highUrl, SpiderConstants.SPIDER_DOMAIN_HIGHER_SUFFIX); } /** * 向低優先url隊列中添加url * @param lowUrl */ @Override public void offerLower(String lowUrl) { offerUrl(lowUrl, SpiderConstants.SPIDER_DOMAIN_LOWER_SUFFIX); } /** * 添加url的通用方法,通過offerHigher和offerLower抽象而來 * @param url 需要添加的url * @param urlTypeSuffix url類型后綴.higher或.lower */ public void offerUrl(String url, String urlTypeSuffix) { Jedis jedis = JedisUtil.getJedis(); String domain = SpiderUtil.getTopDomain(url); // 獲取url對應的頂級域名,如jd.com String key = domain + urlTypeSuffix; // 拼接url隊列的key,如jd.com.higher jedis.lpush(key, url); // 向url隊列中添加url JedisUtil.returnJedis(jedis); } }

通過代碼分析也是可以知道,其核心就在如何調度url倉庫(Redis)中的url。

4.3 URL定時器

一段時間后,高優先級URL隊列和低優先URL隊列中的url都會被消費完,為了讓程序可以繼續爬取數據,同時減少人為的干預,可以預先在Redis中插入種子url,之后定時讓URL定時器從種子url中取出url定存放到高優先級URL隊列中,以此達到程序定時不間斷爬取數據的目的。

url消費完畢后,是否需要循環不斷爬取數據根據個人業務需求而不同,因此這一步不是必需的,只是也提供了這樣的操作。因為事實上,我們需要爬取的數據也是每隔一段時間就會更新的,如果希望我們爬取的數據也跟著定時更新,那么這時定時器就有非常重要的作用了。不過需要注意的是,一旦決定需要循環重復爬取數據,則在設計存儲器實現時需要考慮重復數據的問題,即重復數據應該是更新操作,目前在我設計的存儲器不包括這個功能,有興趣的朋友可以自己實現,只需要在插入數據前判斷數據庫中是否存在該數據即可。

另外需要注意的一點是,URL定時器是一個獨立的進程,需要單獨啟動。

定時器基于Quartz實現,下面是其job的代碼:

/*** 每天定時從url倉庫中獲取種子url,添加進高優先級列表*/ public class UrlJob implements Job { // log4j日志記錄 private Logger logger = LoggerFactory.getLogger(UrlJob.class); @Override public void execute(JobExecutionContext context) throws JobExecutionException { /** * 1.從指定url種子倉庫獲取種子url * 2.將種子url添加進高優先級列表 */ Jedis jedis = JedisUtil.getJedis(); Set<String> seedUrls = jedis.smembers(SpiderConstants.SPIDER_SEED_URLS_KEY); // spider.seed.urls Redis數據類型為set,防止重復添加種子url for(String seedUrl : seedUrls) { String domain = SpiderUtil.getTopDomain(seedUrl); // 種子url的頂級域名 jedis.sadd(domain + SpiderConstants.SPIDER_DOMAIN_HIGHER_SUFFIX, seedUrl); logger.info("獲取種子:{}", seedUrl); } JedisUtil.returnJedis(jedis); // System.out.println("Scheduler Job Test..."); } }

調度器的實現如下:

/*** url定時調度器,定時向url對應倉庫中存放種子url** 業務規定:每天凌晨1點10分向倉庫中存放種子url*/ public class UrlJobScheduler { public UrlJobScheduler() { init(); } /** * 初始化調度器 */ public void init() { try { Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); // 如果沒有以下start方法的執行,則是不會開啟任務的調度 scheduler.start(); String name = "URL_SCHEDULER_JOB"; String group = "URL_SCHEDULER_JOB_GROUP"; JobDetail jobDetail = new JobDetail(name, group, UrlJob.class); String cronExpression = "0 10 1 * * ?"; Trigger trigger = new CronTrigger(name, group, cronExpression); // 調度任務 scheduler.scheduleJob(jobDetail, trigger); } catch (SchedulerException e) { e.printStackTrace(); } catch (ParseException e) { e.printStackTrace(); } } public static void main(String[] args) { UrlJobScheduler urlJobScheduler = new UrlJobScheduler(); urlJobScheduler.start(); } /** * 定時調度任務 * 因為我們每天要定時從指定的倉庫中獲取種子url,并存放到高優先級的url列表中 * 所以是一個不間斷的程序,所以不能停止 */ private void start() { while (true) { } } }

5 監控報警系統

監控報警系統的加入主要是為了讓使用者可以主動發現節點宕機,而不是被動地發現,因為實際中爬蟲程序可能是持續不斷運行的,并且我們會在多個節點上部署我們的爬蟲程序,因此很有必要對節點進行監控,并且在節點出現問題時可以及時發現并修正,需要注意的是,監控報警系統是一個獨立的進程,需要單獨啟動。

5.1 基本原理

首先需要先在zookeeper中創建一個/ispider節點:

[zk: localhost:2181(CONNECTED) 1] create /ispider ispider Created /ispider

監控報警系統的開發主要依賴于zookeeper實現,監控程序對zookeeper下面的這個節點目錄進行監聽:

[zk: localhost:2181(CONNECTED) 0] ls /ispider []

爬蟲程序啟動時會在該節點目錄下注冊一個臨時節點目錄:

[zk: localhost:2181(CONNECTED) 0] ls /ispider [192.168.43.166]

當節點出現宕機時,該臨時節點目錄就會被zookeeper刪除

[zk: localhost:2181(CONNECTED) 0] ls /ispider []

同時因為我們監聽了節點目錄/ispider,所以當zookeeper刪除其下的節點目錄時(或增加一個節點目錄),zookeeper會給我們的監控程序發送通知,即我們的監控程序會得到回調,這樣便可以在回調程序中執行報警的系統動作,從而完成監控報警的功能。

5.2 zookeeper Java API使用說明

可以使用zookeeper原生的Java API,我在另外寫的一個RPC框架(底層基于Netty實現遠程通信)中就是使用原生的API,不過顯然代碼會復雜很多,并且本身需要對zookeeper有更多的學習和了解,這樣用起來才會容易一些。

所以為了降低開發的難度,這里使用第三方封裝的API,即curator,來進行zookeeper客戶端程序的開發。

5.3 爬蟲系統zookeeper注冊

在啟動爬蟲系統時,我們的程序都會啟動一個zookeeper客戶端來向zookeeper來注冊自身的節點信息,主要是ip地址,并在/ispider節點目錄以創建一個以該爬蟲程序所在的節點IP地址命名的節點,如/ispider/192.168.43.116,實現的代碼如下:

/*** 注冊zk*/ private void registerZK() { String zkStr = "uplooking01:2181,uplooking02:2181,uplooking03:2181"; int baseSleepTimeMs = 1000; int maxRetries = 3; RetryPolicy retryPolicy = new ExponentialBackoffRetry(baseSleepTimeMs, maxRetries); CuratorFramework curator = CuratorFrameworkFactory.newClient(zkStr, retryPolicy); curator.start(); String ip = null; try { // 向zk的具體目錄注冊 寫節點 創建節點 ip = InetAddress.getLocalHost().getHostAddress(); curator.create().withMode(CreateMode.EPHEMERAL).forPath("/ispider/" + ip, ip.getBytes()); } catch (UnknownHostException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } }

應該注意到的是,我們創建的節點為臨時節點,要想實現監控報警功能,必須要為臨時節點。

5.4 監控程序

首先需要先監聽zookeeper中的一個節點目錄,在我們的系統中,設計是監聽/ispider這個節點目錄:

public SpiderMonitorTask() {String zkStr = "uplooking01:2181,uplooking02:2181,uplooking03:2181"; int baseSleepTimeMs = 1000; int maxRetries = 3; RetryPolicy retryPolicy = new ExponentialBackoffRetry(baseSleepTimeMs, maxRetries); curator = CuratorFrameworkFactory.newClient(zkStr, retryPolicy); curator.start(); try { previousNodes = curator.getChildren().usingWatcher(this).forPath("/ispider"); } catch (Exception e) { e.printStackTrace(); } }

在上面注冊了zookeeper中的watcher,也就是接收通知的回調程序,在該程序中,執行我們報警的邏輯:

/*** 這個方法,當監控的zk對應的目錄一旦有變動,就會被調用* 得到當前最新的節點狀態,將最新的節點狀態和初始或者上一次的節點狀態作比較,那我們就知道了是由誰引起的節點變化* @param event*/ @Override public void process(WatchedEvent event) { try { List<String> currentNodes = curator.getChildren().usingWatcher(this).forPath("/ispider"); // HashSet<String> previousNodesSet = new HashSet<>(previousNodes); if(currentNodes.size() > previousNodes.size()) { // 最新的節點服務,超過之前的節點服務個數,有新的節點增加進來 for(String node : currentNodes) { if(!previousNodes.contains(node)) { // 當前節點就是新增節點 logger.info("----有新的爬蟲節點{}新增進來", node); } } } else if(currentNodes.size() < previousNodes.size()) { // 有節點掛了 發送告警郵件或者短信 for(String node : previousNodes) { if(!currentNodes.contains(node)) { // 當前節點掛掉了 得需要發郵件 logger.info("----有爬蟲節點{}掛掉了", node); MailUtil.sendMail("有爬蟲節點掛掉了,請人工查看爬蟲節點的情況,節點信息為:", node); } } } // 掛掉和新增的數目一模一樣,上面是不包括這種情況的,有興趣的朋友可以直接實現包括這種特殊情況的監控 previousNodes = currentNodes; // 更新上一次的節點列表,成為最新的節點列表 } catch (Exception e) { e.printStackTrace(); } // 在原生的API需要再做一次監控,因為每一次監控只會生效一次,所以當上面發現變化后,需要再監聽一次,這樣下一次才能監聽到 // 但是在使用curator的API時則不需要這樣做 }

當然,判斷節點是否掛掉,上面的邏輯還是存在一定的問題的,按照上面的邏輯,假如某一時刻新增節點和刪除節點事件同時發生,那么其就不能判斷出來,所以如果需要更精準的話,可以將上面的程序代碼修改一下。

5.5 郵件發送模塊

使用模板代碼就可以了,不過需要注意的是,在使用時,發件人的信息請使用自己的郵箱。

下面是爬蟲節點掛掉時接收到的郵件:

實際上,如果購買了短信服務,那么通過短信API也可以向我們的手機發送短信。

6 實戰:爬取京東、蘇寧易購全網手機商品數據

因為前面在介紹這個系統的時候也提到了,我只寫了京東和蘇寧易購的網頁解析器,所以接下來也就是爬取其全網的手機商品數據。

6.1 環境說明

需要確保Redis、Zookeeper服務可用,另外如果需要使用HBase來存儲數據,需要確保Hadoop集群中的HBase可用,并且相關配置文件已經加入到爬蟲程序的classpath中。

還有一點需要注意的是,URL定時器和監控報警系統是作為單獨的進程來運行的,并且也是可選的。

6.2 爬蟲結果

進行了兩次爬取,分別嘗試將數據保存到MySQL和HBase中,給出如下數據情況。

6.2.1 保存到MySQL

mysql> select count(*) from phone; +----------+ | count(*) | +----------+ | 12052 | +----------+ 1 row in setmysql> select count(*) from phone where source='jd.com'; +----------+ | count(*) | +----------+ | 9578 | +----------+ 1 row in setmysql> select count(*) from phone where source='suning .com'; +----------+ | count(*) | +----------+ | 2474 | +----------+ 1 row in set

在可視化工具中查看數據情況:

6.2.2 保存到HBase

hbase(main):225:0* count 'phone' Current count: 1000, row: 11155386088_jd.com Current count: 2000, row: 136191393_suning.com Current count: 3000, row: 16893837301_jd.com Current count: 4000, row: 19036619855_jd.com Current count: 5000, row: 1983786945_jd.com Current count: 6000, row: 1997392141_jd.com Current count: 7000, row: 21798495372_jd.com Current count: 8000, row: 24154264902_jd.com Current count: 9000, row: 25687565618_jd.com Current count: 10000, row: 26458674797_jd.com Current count: 11000, row: 617169906_suning.com Current count: 12000, row: 769705049_suning.com 12348 row(s) in 1.5720 seconds => 12348

在HDFS中查看數據情況:

6.2.3 數據量與實際情況分析

  • 京東

京東手機的列表大概有160多頁,每個列表有60個商品數據,所以總量在9600左右,我們的數據基本是符合的,后面通過日志分析其實可以知道,一般丟失的數據為連接超時導致的,所以在選取爬蟲的環境時,更建議在網絡環境好的主機上進行,同時如果可以有IP代理地址庫就更好了,另外對于連接超時的情況,其實是可以進一步在我們的程序中加以控制,一旦出現爬取數據失敗的url,可以將其加入到重試url隊列中,目前這一點功能我是沒有做,有興趣的同學可以試一下。

  • 蘇寧易購

再來看看蘇寧的,其有100頁左右的手機列表,每頁也是60個商品數據,所以總量在6000左右。但可以看到,我們的數據卻只有3000這樣的數量級(缺少的依然是頻繁爬取造成的連接失敗問題),這是為什么呢?

這是因為,打開蘇寧的某個列表頁面后,其是先加載30個商品,當鼠標向下滑動時,才會通過另外的API去加載其它的30個商品數據,每一個列表頁面都是如此,所以,實際上,我們是缺少了一半的商品數據沒有爬取。知道這個原因之后,實現也不難,但是因為時間關系,我就沒有做了,有興趣的朋友折騰一下吧。

6.3 通過日志分析爬蟲系統的性能

在我們的爬蟲系統中,每個關鍵的地方,如網頁下載、數據解析等都是有打logger的,所以通過日志,可以大概分析出相關的時間參數。

2018-04-01 21:26:03 [pool-1-thread-1] [cn.xpleaf.spider.utils.HttpUtil] [INFO] - 下載網頁:https://list.jd.com/list.html?cat=9987,653,655&page=1,消耗時長:590 ms,代理信息:null:null 2018-04-01 21:26:03 [pool-1-thread-1] [cn.xpleaf.spider.core.parser.Impl.JDHtmlParserImpl] [INFO] - 解析列表頁面:https://list.jd.com/list.html?cat=9987,653,655&page=1, 消耗時長:46ms 2018-04-01 21:26:03 [pool-1-thread-3] [cn.xpleaf.spider.core.parser.Impl.SNHtmlParserImpl] [INFO] - 解析列表頁面:https://list.suning.com/0-20006-0.html, 消耗時長:49ms 2018-04-01 21:26:04 [pool-1-thread-5] [cn.xpleaf.spider.utils.HttpUtil] [INFO] - 下載網頁:https://item.jd.com/6737464.html,消耗時長:219 ms,代理信息:null:null 2018-04-01 21:26:04 [pool-1-thread-2] [cn.xpleaf.spider.utils.HttpUtil] [INFO] - 下載網頁:https://list.jd.com/list.html?cat=9987,653,655&page=2&sort=sort_rank_asc&trans=1&JL=6_0_0,消耗時長:276 ms,代理信息:null:null 2018-04-01 21:26:04 [pool-1-thread-4] [cn.xpleaf.spider.utils.HttpUtil] [INFO] - 下載網頁:https://list.suning.com/0-20006-99.html,消耗時長:300 ms,代理信息:null:null 2018-04-01 21:26:04 [pool-1-thread-4] [cn.xpleaf.spider.core.parser.Impl.SNHtmlParserImpl] [INFO] - 解析列表頁面:https://list.suning.com/0-20006-99.html, 消耗時長:4ms ...... 2018-04-01 21:27:49 [pool-1-thread-3] [cn.xpleaf.spider.utils.HttpUtil] [INFO] - 下載網頁:https://club.jd.com/comment/productCommentSummaries.action?referenceIds=23934388891,消耗時長:176 ms,代理信息:null:null 2018-04-01 21:27:49 [pool-1-thread-3] [cn.xpleaf.spider.core.parser.Impl.JDHtmlParserImpl] [INFO] - 解析商品頁面:https://item.jd.com/23934388891.html, 消耗時長:413ms 2018-04-01 21:27:49 [pool-1-thread-2] [cn.xpleaf.spider.utils.HttpUtil] [INFO] - 下載網頁:https://review.suning.com/ajax/review_satisfy/general-00000000010017793337-0070079092-----satisfy.htm,消耗時長:308 ms,代理信息:null:null 2018-04-01 21:27:49 [pool-1-thread-2] [cn.xpleaf.spider.core.parser.Impl.SNHtmlParserImpl] [INFO] - 解析商品頁面:https://product.suning.com/0070079092/10017793337.html, 消耗時長:588ms ......

平均下來,下載一個商品網頁數據的時間在200~500毫秒不等,當然這個還需要取決于當時的網絡情況。

另外,如果想要真正計算爬取一個商品的數據,可以通過日志下面的數據來計算:

  • 下載一個商品頁面數據的時間
  • 獲取價格數據的時間
  • 獲取評論數據的時間

在我的主機上(CPU:E5 10核心,內存:32GB,分別開啟1個虛擬機和3個虛擬機),情況如下:

節點數每節點線程數商品數量時間
15京東+蘇寧易購近13000個商品數據141分鐘
35京東+蘇寧易購近13000個商品數據65分鐘

可以看到,當使用3個節點時,時間并不會相應地縮小為原來的1/3,這是因為此時影響爬蟲性能的問題主要是網絡問題,節點數量多,線程數量大,網絡請求也多,但是帶寬一定,并且在沒有使用代理的情況,請求頻繁,連接失敗的情況也會增多,對時間也有一定的影響,如果使用隨機代理庫,情況將會好很多。

但可以肯定的是,在橫向擴展增加爬蟲節點之后,確實可以大大縮小我們的爬蟲時間,這也是分布式爬蟲系統的好處。

7 爬蟲系統中使用的反反爬蟲策略

在整個爬蟲系統的設計中,主要使用下面的策略來達到反反爬蟲的目的:

  • 使用代理來訪問-->IP代理庫,隨機IP代理
  • 隨機頂級域名url訪問-->url調度系統
  • 每個線程每爬取完一條商品數據sleep一小段時間再進行爬取

8 總結

需要說明的是,本系統是基于Java實現的,但個人覺得,語言本身依然不是問題,核心在于對整個系統的設計上以及理解上,寫此文章是希望分享這樣一種分布式爬蟲系統的架構給大家,如果對源代碼感興趣,可以到我的GitHub上查看。

GitHub:https://github.com/xpleaf/ispider

轉載于:https://www.cnblogs.com/davidwang456/p/8820037.html

總結

以上是生活随笔為你收集整理的分布式爬虫系统设计、实现与实战:爬取京东、苏宁易购全网手机商品数据+MySQL、HBase存储...的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

欧美色黄 | 中文字幕在线播放视频 | 国产高清黄 | 国产精品一区二区三区电影 | 九九有精品 | 久久av电影 | 国产亚洲精品bv在线观看 | 成人av一区二区在线观看 | 日韩有码网站 | 国产露脸91国语对白 | 高潮毛片无遮挡高清免费 | 去干成人网| 99热在线这里只有精品 | 在线观看视频h | 亚洲年轻女教师毛茸茸 | 精品视频免费久久久看 | 日韩精品短视频 | 国产精品一区二区三区免费视频 | 亚洲国产精品va在线看黑人 | 欧美精品一二三 | 中文字幕在线视频一区二区 | 欧美 日韩 国产 中文字幕 | 久久久久久久久久久免费视频 | 91丨精品丨蝌蚪丨白丝jk | a在线视频v视频 | 国产色综合| 99在线视频免费观看 | 成片免费观看视频大全 | 最近的中文字幕大全免费版 | 久草在线一免费新视频 | 欧美日韩国产区 | 日韩精品影视 | 欧美日韩在线观看一区二区 | 中文久久精品 | 夜夜夜影院 | 久久se视频| 九九热精品视频在线播放 | 99综合视频 | 日韩视频在线不卡 | 亚洲一区二区91 | 日韩精品一区二区三区视频播放 | 九九久久电影 | 国产精品欧美久久 | 91黄站| www.香蕉视频在线观看 | 永久免费毛片在线观看 | 欧美日韩午夜爽爽 | 激情久久综合 | 亚洲精品玖玖玖av在线看 | 国产97色 | 最近免费中文字幕 | 欧美一级片 | 国产免费久久 | 国产精品美女www爽爽爽视频 | 伊人网综合在线观看 | 色婷婷骚婷婷 | 在线观看你懂的网站 | 国产玖玖在线 | 国产黄色免费观看 | 日韩成人欧美 | 午夜精品成人一区二区三区 | 久久久久五月天 | 丁香色婷 | 欧美一级电影免费观看 | 久久精品国产免费看久久精品 | 香蕉视频日本 | 亚洲成人av一区二区 | 91大神电影 | 91色国产在线 | 欧美孕交vivoestv另类 | 91精品国产乱码在线观看 | 不卡视频在线 | 欧美小视频在线观看 | av网站有哪些 | 国产中文字幕91 | 久久艹欧美 | 午夜精品一区二区三区在线视频 | 日韩精品免费专区 | 欧美精品在线一区 | av丝袜制服| 成人免费共享视频 | 色伊人网 | av电影免费观看 | 国产免费一区二区三区网站免费 | 国产精品午夜免费福利视频 | 国产69久久久欧美一级 | 五月婷婷六月综合 | 日本三级不卡 | 国产96视频 | av资源免费观看 | 成人精品国产 | 欧美亚洲国产日韩 | 一本一本久久a久久精品综合妖精 | 欧美午夜视频在线 | 久草在线费播放视频 | 国产一区欧美日韩 | 欧美久草视频 | 麻豆94tv免费版 | 四虎免费在线观看 | 波多野结衣电影一区 | 亚洲精品美女视频 | 亚洲国产中文在线观看 | www.日韩免费| 精品国产资源 | 麻豆视频入口 | 国产亚洲精品久久久网站好莱 | 免费看黄视频 | 久久免费看视频 | 人人要人人澡人人爽人人dvd | 色九九影院 | 日韩色一区二区三区 | 中文字幕在线观看的网站 | 免费日韩电影 | 91xav| 456成人精品影院 | 玖玖国产精品视频 | 999成人| 黄色av免费电影 | 中文字幕在线观看2018 | 一区二区三区在线免费观看 | 国产一级片免费视频 | 日韩午夜小视频 | 日日夜夜国产 | 国产精品久久久久久久久久久久午夜 | 国产成人久久精品77777 | 久久久电影 | 日韩欧美大片免费观看 | 麻豆成人精品视频 | 婷婷伊人五月 | 国产精品久久久久一区二区 | 日本h在线播放 | 美女视频黄在线观看 | 在线观看亚洲国产精品 | 91精品国产成人 | 99热这里是精品 | 偷拍视频一区 | 欧美性色黄大片在线观看 | 国内精品免费 | 黄网站色视频免费观看 | 国产成人久久精品 | 色综合天天色综合 | 欧美一区在线看 | 夜夜夜夜猛噜噜噜噜噜初音未来 | 伊人伊成久久人综合网站 | 久久国产精品系列 | aaa日本高清在线播放免费观看 | 免费成人在线观看 | 黄色在线观看污 | 久久国内精品视频 | 免费观看的黄色片 | 黄网站色 | 亚洲日日射 | 国产大片黄色 | 婷婷综合电影 | 色婷婷av一区二 | 成年人免费在线看 | 午夜精品久久久99热福利 | 精产嫩模国品一二三区 | 色网av | 一本一道久久a久久综合蜜桃 | 国产一区二区精品91 | 久久久久伊人 | 久久久久久久亚洲精品 | 天天操天天草 | 最近中文字幕免费av | 午夜在线观看一区 | www狠狠操| 91亚洲精品在线 | 中文字幕在线影视资源 | 狠狠色狠狠综合久久 | 欧美久久久久久久久久 | 在线视频a | www日韩视频 | 天天操人人干 | 精产嫩模国品一二三区 | 中文字幕免费不卡视频 | 国产一性一爱一乱一交 | 国产一级淫片免费看 | av电影免费在线 | 青春草免费在线视频 | 日日夜夜骑 | 免费国产一区二区 | 91福利社在线观看 | 色爱成人网 | 日韩精品一区二区在线视频 | 波多野结衣一区二区三区中文字幕 | 免费观看www小视频的软件 | 亚洲精品ww| 99人久久精品视频最新地址 | 日韩最新av| 婷婷色亚洲 | 九热在线 | 国产精品高清一区二区三区 | 91麻豆精品国产自产在线 | 成人九九视频 | 午夜视频一区二区 | 99精品国产一区二区 | 夜夜躁狠狠躁 | a天堂最新版中文在线地址 久久99久久精品国产 | 91插插插网站 | 成人在线视频你懂的 | 日韩在线免费高清视频 | 91麻豆精品国产91久久久使用方法 | 青青河边草观看完整版高清 | 国产精品av免费 | 亚洲欧洲日韩在线观看 | 国产一级91| 国产精品一区二区美女视频免费看 | 成 人 黄 色 免费播放 | 久久国产精品久久精品 | 久久免费中文视频 | 日本久久不卡视频 | 一区二区三区影院 | 天天曰视频| 私人av| 91在线观看视频 | 波多野结衣动态图 | 99国产精品一区二区 | 精品免费在线视频 | 中文字幕亚洲国产 | 亚洲欧美日韩不卡 | 国产清纯在线 | 久久免费国产视频 | 亚洲精品1234区 | 996久久国产精品线观看 | 日韩欧美在线播放 | 欧洲精品视频一区二区 | 在线免费黄 | 美女黄濒 | 久草观看| 精品国产乱码 | 91中文字幕永久在线 | 久久久视屏 | 天天草天天干天天 | 免费看久久久 | a级片久久久 | 中文字幕精品三级久久久 | 久久午夜精品 | 91九色蝌蚪视频在线 | 激情av在线资源 | 久久怡红院 | 国内精品久久久久影院一蜜桃 | 国产69久久 | www.av在线.com | 中文字幕亚洲欧美日韩2019 | 99久久99视频只有精品 | 欧洲精品视频一区 | 日韩欧美视频一区二区三区 | www.黄色小说.com | 亚洲成免费 | 精品一二三四五区 | 欧美成年网站 | 国产黄色片久久 | 丁香五香天综合情 | 精品国产一区二区三区久久久 | 免费日韩av片 | 91正在播放 | 91看片淫黄大片91 | 欧美激情精品 | 精品一区91| 国产美女精品 | 亚洲精品乱码久久久久久蜜桃欧美 | 91人人在线 | 免费在线观看91 | 精品国产一区二区三区久久久蜜月 | 亚洲精品日韩av | 在线成人免费电影 | 国产日韩欧美精品在线观看 | 欧洲一区精品 | 麻豆影视在线免费观看 | 国产精品精品国产婷婷这里av | 日韩欧美高清 | 国产高清一级 | 97超碰人人爱| 四虎影视精品 | 日韩av影视在线观看 | 在线观看中文 | 香蕉影视app | 欧美日韩免费一区 | 日日夜夜天天久久 | 久久亚洲精品电影 | 九九在线播放 | 日韩欧美高清一区二区 | 玖玖玖国产精品 | 综合久色| 久久免费黄色网址 | 久久精品女人毛片国产 | 亚洲免费高清视频 | 69视频网站 | 在线播放国产一区二区三区 | 色香蕉视频 | 97精品超碰一区二区三区 | 在线观看麻豆av | 毛片3| 国产成人精品久久亚洲高清不卡 | 在线免费观看视频你懂的 | 日韩国产在线观看 | 五月天天av | 国产成人a亚洲精品 | 亚洲国产av精品毛片鲁大师 | 麻豆免费观看视频 | 免费久久网 | 成年人免费在线观看网站 | 国产精品成人自产拍在线观看 | 亚洲精品成人免费 | 人交video另类hd| 天天操天天干天天干 | 成人av在线一区二区 | www.福利视频 | 欧美地下肉体性派对 | 久久久香蕉视频 | 4438全国亚洲精品在线观看视频 | 手机看片| 97理论片 | 日本黄区免费视频观看 | 日韩精品免费 | 日韩四虎| 最近高清中文字幕在线国语5 | 美女视频又黄又免费 | 亚洲3级| 亚洲aaa级 | 色综合天天爱 | 日韩美女高潮 | 亚州精品国产 | 夜夜操网站 | 操操操com | 免费看毛片在线 | 亚洲精品综合在线 | 亚洲欧美日韩一区二区三区在线观看 | 狠狠操综合网 | 日日躁你夜夜躁你av蜜 | 国产精品久久久久久久久软件 | 久久爱资源网 | 成人9ⅰ免费影视网站 | 最新日韩电影 | 国产高清成人av | 日韩精品中文字幕在线不卡尤物 | 人人添人人澡人人澡人人人爽 | 国产亚洲视频在线 | 夜夜躁天天躁很躁波 | 精品国产一区二区三区蜜臀 | 六月久久婷婷 | 成人黄色毛片视频 | 国产黄色成人 | 中文字幕一区二区三区久久蜜桃 | 国产精品美乳一区二区免费 | 九九视频在线观看视频6 | 女人18片| 日日干狠狠操 | 亚洲免费av一区二区 | 亚洲日本精品视频 | 91久久国产综合精品女同国语 | 韩国一区在线 | 国内精品久久久久久久97牛牛 | 91麻豆操| 四虎在线永久免费观看 | 91精品视频在线 | 国产自产高清不卡 | 婷婷av资源 | 国产精品久久久久久久久婷婷 | 久久免费精品一区二区三区 | 精品日韩在线 | 亚洲人av免费网站 | 成人免费观看网站 | 视频二区在线视频 | 日韩一二区在线观看 | 亚洲精品国偷拍自产在线观看蜜桃 | 一区二区三区在线播放 | 日本久久精| 二区三区在线 | 国产我不卡 | 日韩精品一区二区在线观看视频 | 三级av网站 | 免费视频91 | 亚洲成人av电影在线 | 中文字幕人成不卡一区 | 99精品99| 欧美一级电影在线观看 | 91av视频在线观看免费 | 国产精品乱码高清在线看 | 激情欧美丁香 | 成人在线视频免费观看 | www色,com | 国产一区二区三精品久久久无广告 | 欧美激精品 | 国产自在线观看 | 99看视频在线观看 | 97爱| 国产精品18久久久久久久网站 | 日韩精品一区二区在线观看 | www.午夜色.com| 亚洲国产成人高清精品 | 六月激情 | 综合中文字幕 | 亚洲综合在线观看视频 | 男女视频91| 欧美精品乱码久久久久久 | 人人澡人人舔 | 天天综合网国产 | 天天躁日日| 久久人人插 | 亚洲三级国产 | 日韩a级黄色 | 四虎永久精品在线 | 成人免费观看网址 | 久久免费公开视频 | 色狠狠干 | 国产在线p | 亚洲精品国精品久久99热一 | 手机在线看片日韩 | 久久婷亚洲五月一区天天躁 | 午夜av在线免费 | 中文字幕乱码日本亚洲一区二区 | 91精品黄色 | 337p欧美 | 国产精品永久 | 在线日韩| 久久精品久久久久久久 | 国产综合片 | 久久99精品国产麻豆宅宅 | 久久精品资源 | 国产成人久久久77777 | 精品久久久久久久久亚洲 | 久久久九九 | 91精品一区国产高清在线gif | 亚洲激情在线观看 | 97超碰超碰久久福利超碰 | 亚av在线 | 亚洲一级黄色av | 欧美精品免费一区二区 | 99色在线观看视频 | 久久99精品国产麻豆宅宅 | 久久精品波多野结衣 | 国产精选在线观看 | av大片免费看 | 久久精品影视 | 午夜视频在线网站 | 免费高清av在线看 | 综合网伊人 | 国产精品手机看片 | 日韩黄色免费在线观看 | 国产一级h | 久久久久国产成人免费精品免费 | 色开心| 欧美一级大片在线观看 | 国产一卡在线 | 成人免费影院 | 成人在线免费看 | 亚洲欧美日韩精品久久久 | 日韩欧美区 | 999视频在线播放 | 综合久久综合久久 | 中文字幕在线播放一区 | 国产无套精品久久久久久 | 久草资源在线观看 | 国产色一区| 中文字幕在线观看不卡 | 天天激情在线 | 免费a视频在线观看 | 日韩狠狠操 | 高清国产在线一区 | 在线亚洲天堂网 | 国产精品免费久久久 | 另类五月激情 | 在线观看中文字幕一区 | 九九精品视频在线观看 | 九九免费精品视频在线观看 | 亚洲精品国产片 | 国产成人香蕉 | 亚洲免费在线看 | 国产成人精品一区二区在线 | 91麻豆免费版 | 97av色| 免费99精品国产自在在线 | 久久国产电影 | 久久艹国产视频 | 97成人精品视频在线观看 | 五月婷婷影视 | 射射射av| 色婷婷www| 精品国产成人av | 日韩欧美一区二区三区在线 | 一区二区三区在线观看 | 久久99精品国产麻豆宅宅 | 91亚洲精品久久久 | 久久免费在线 | 婷婷久久五月天 | 国产精品va| 丰满少妇对白在线偷拍 | 午夜av色 | 成人精品一区二区三区中文字幕 | 国产资源在线播放 | 91黄色免费网站 | 最近日本中文字幕a | 91在线看视频 | 日韩免费视频观看 | 福利网址在线观看 | 精品亚洲欧美无人区乱码 | 久久一区二区三区国产精品 | 国产精品孕妇 | 日韩av不卡在线播放 | 一级片观看 | 国产一级片播放 | 久久高清国产视频 | 2018精品视频 | 国产人成在线视频 | 99久久精品免费 | 成人在线你懂得 | 国内成人av| 欧美色图狠狠干 | 亚洲更新最快 | 久草成人在线 | 日韩专区 在线 | 中文字幕av日韩 | 一区二区三区动漫 | 国产一级免费观看 | 国内精品视频在线播放 | 婷婷精品国产欧美精品亚洲人人爽 | 亚洲婷婷在线视频 | 国产在线观看你懂的 | 久久一区国产 | 欧美一区二区三区不卡 | 麻豆国产网站 | 国产精品女人久久久 | 91| 婷婷色影院 | 免费91麻豆精品国产自产在线观看 | 免费av看片 | 极品美女被弄高潮视频网站 | 久草亚洲视频 | www久久国产 | 日韩精品1区2区 | 99久久精品一区二区成人 | 激情网色 | 中文字幕在线观 | 国产精品久久久久久久久久久久 | 午夜影院日本 | 91精品国产综合久久久久久久 | 亚洲 精品在线视频 | 一本一本久久a久久精品牛牛影视 | a久久免费视频 | 精品96久久久久久中文字幕无 | 午夜电影 电影 | 国产香蕉97碰碰久久人人 | 91九色在线视频观看 | 国产精品成人免费一区久久羞羞 | 久久草草影视免费网 | 九九热99视频 | 久久久久久97三级 | 三级黄色欧美 | 中文字幕av在线免费 | 久久人网 | 亚洲国产mv | 天天天天爽| 国产午夜三级 | 黄色avwww| 亚洲精品一区中文字幕乱码 | 国精产品999国精产品视频 | 亚洲欧美日韩国产 | 国产不卡精品视频 | 国内精品99 | 最近中文字幕高清字幕在线视频 | 中文字幕欧美日韩va免费视频 | 亚洲精品看片 | 97在线观看免费高清完整版在线观看 | 免费在线电影网址大全 | 色大片免费看 | 一区二区三区日韩视频在线观看 | 国产中出在线观看 | 91九色精品 | 日韩免费av在线 | 精品久久久亚洲 | 日韩在线网址 | 国产精品免费看久久久8精臀av | 看片一区二区三区 | 久久久免费精品国产一区二区 | h视频在线看 | 丁香网婷婷| 精品国产乱码一区二 | 国产精品自产拍在线观看 | 久久久久久久国产精品 | 久久99久久精品 | 看v片| 久久久99精品免费观看乱色 | 狠狠地日 | 亚洲免费高清视频 | 亚洲性xxxx | 精品国产一区二区三区在线观看 | 亚洲午夜精品一区二区三区电影院 | 亚洲综合国产精品 | 九七视频在线观看 | 欧美日韩精品在线一区二区 | 免费看高清毛片 | 国产精品岛国久久久久久久久红粉 | 国产免费作爱视频 | 在线三级播放 | 日本精品在线 | 亚洲污视频 | 免费一级片在线 | 婷婷久久久久 | 五月天天在线 | 久久九九久久精品 | 天天干人人干 | 亚洲另类xxxx | 久久精品国产亚洲精品2020 | 五月亚洲 | 欧美成人精品欧美一级乱黄 | 99在线免费观看视频 | 91香蕉视频 mp4 | 久久网站最新地址 | 超碰在线99 | h动漫中文字幕 | 操操碰 | 亚洲最大av网 | 91干干干 | 日韩中文字幕免费在线观看 | 国内精品视频久久 | 日韩三级视频在线看 | 狠狠干成人综合网 | 免费在线观看视频a | 99热播精品 | 免费特级黄毛片 | 国产电影黄色av | 国产在线精品福利 | 色五丁香 | 欧美aaa一级 | 天堂av网站 | 亚洲欧美日本一区二区三区 | 成人在线免费看视频 | 国产黄色av影视 | 国产亚洲在线 | 91精品国产高清自在线观看 | 色在线视频 | 久久手机看片 | h网站免费在线观看 | 精品资源在线 | 亚洲欧美日本国产 | 超碰在线94| 欧美精品久久久 | 亚洲另类交 | 日韩精品 在线视频 | 超碰97中文 | 国产一区二区久久久久 | 国产精品福利在线 | 欧洲精品在线视频 | 91视频啪| 一区二区三区手机在线观看 | 欧美日韩aa | 亚洲综合激情 | 精品在线播放 | 免费日韩一区二区三区 | 日韩高清在线一区二区 | 欧美激情第一页xxx 午夜性福利 | 国产黄a三级三级 | 亚洲精品国产精品久久99 | 色www精品视频在线观看 | 国产精品亚洲综合久久 | 青青网视频 | 亚洲,国产成人av | 91人人爽久久涩噜噜噜 | 欧美日韩aa | 久久天堂影院 | 久久伊99综合婷婷久久伊 | 一二三区在线 | 韩国三级在线一区 | 99久久综合狠狠综合久久 | 黄色软件在线看 | 在线看一区二区 | 91色偷偷 | 五月婷婷久久丁香 | 国产福利不卡视频 | 婷婷在线网站 | 亚洲在线色 | 国产精品美女免费看 | 成人av免费网站 | 91九色蝌蚪国产 | 精品成人国产 | 日韩在观看线 | 91爱爱网址 | 日韩精品观看 | 黄色毛片电影 | 一区三区视频在线观看 | 91视频-88av | 欧美一二区在线 | 不卡视频国产 | 久久精品站 | 九九热中文字幕 | 中文字幕a∨在线乱码免费看 | 人人狠 | 国产黄色一级片在线 | 久久成视频 | 99热99热 | 成人在线免费小视频 | 亚洲成人av在线电影 | av线上免费观看 | 91精品国产92久久久久 | 国产一级视频在线观看 | 色在线网 | 成人av资源网站 | 亚洲国产97在线精品一区 | 91精品小视频 | 国内视频1区 | 国产黄色精品在线观看 | 高清av网站 | 亚洲成人二区 | 看污网站 | 免费开视频| 91资源在线免费观看 | 国产成人久久久77777 | 欧美日韩国产色综合一二三四 | 韩日成人av | 在线黄色av| 精品无人国产偷自产在线 | 日韩av成人在线观看 | 91精品国产自产在线观看永久 | 久草视频在线看 | 成人理论电影 | 久久婷婷精品 | 亚洲国产成人高清精品 | 成人免费观看在线视频 | 波多野结衣一区二区三区中文字幕 | 国产精品女教师 | 久久精品亚洲国产 | 免费av片在线 | 久久久久在线 | 又爽又黄又刺激的视频 | 国产精品成人久久久久 | 色网站黄| 日韩成人免费在线 | www.夜夜| 四虎影视精品永久在线观看 | 免费日p视频 | 国产精品99久久久久久久久久久久 | 麻豆视屏 | 欧美人交a欧美精品 | 麻豆影视在线观看 | a电影免费看 | 五月天激情在线 | 啪一啪在线 | 激情综合网五月 | 在线视频中文字幕一区 | 最近更新的中文字幕 | 久久精品久久久久久久 | 99精品免费久久久久久久久 | 天天操天天摸天天爽 | 国产欧美精品一区二区三区四区 | 久久综合色一综合色88 | 国产69精品久久久久9999apgf | 欧美一级大片在线观看 | av中文字幕网 | 免费看片在线观看 | 亚洲午夜小视频 | 久久久久国产成人精品亚洲午夜 | 高潮毛片无遮挡高清免费 | 00av视频 | 国产美女免费视频 | 久久精品免费播放 | 亚洲激情影院 | 国产四虎在线 | 日韩av成人 | 一级特黄aaa大片在线观看 | 亚洲精品在线视频观看 | 日韩精品中文字幕在线 | 国产在线黄 | 天天综合色网 | 国产免费黄视频在线观看 | 久久99精品热在线观看 | 精品99999| 国产精品乱码久久久久 | 91精品国产91久久久久福利 | 综合网av | 亚洲黑丝少妇 | 日韩超碰 | 天天操天天添天天吹 | 中文字幕在线观看91 | 六月婷操 | av韩国在线 | 亚洲欧美乱综合图片区小说区 | 色中色亚洲 | 日韩中文幕 | 亚洲国产中文在线观看 | 精品九九九九 | 亚洲精品一区中文字幕乱码 | 国产精品久久三 | 91九色porny在线 | 久久久国产精品亚洲一区 | 91亚洲国产成人久久精品网站 | 欧美激情另类文学 | 成人h视频 | 精品久久久久久久久中文字幕 | 国产乱码精品一区二区蜜臀 | 婷婷五综合| 亚洲日本韩国一区二区 | a级国产毛片 | 中文字幕在线观看网站 | 精品国产aⅴ麻豆 | 美女黄网站视频免费 | 在线一级片 | 91精品国产电影 | 亚洲免费av网站 | 涩涩成人在线 | 国产麻豆果冻传媒在线观看 | 免费看成人片 | 色久综合 | 免费看片成年人 | 欧美日韩xxxxx | 91色影院| 一区二区三区视频在线 | 成人av资源网站 | 日韩精品一区二区三区免费观看 | 天天看天天干 | 国产精品九九九 | 久久免费一 | 夜夜嗨av色一区二区不卡 | 不卡的av在线 | 亚洲国产精品一区二区尤物区 | 网站在线观看日韩 | 一区二区三区免费在线 | 激情综合色综合久久综合 | 中文字幕专区高清在线观看 | 天天色 天天 | 天天插天天爱 | 日韩无在线 | 日本女人的性生活视频 | 久久伊人色综合 | 久久高清 | 伊人小视频| 久久五月精品 | 国产特级毛片aaaaaa | 黄色特一级| 99re久久资源最新地址 | 丁香花在线观看免费完整版视频 | 成年人在线免费看视频 | 国产手机av在线 | 三三级黄色片之日韩 | 亚洲高清在线精品 | 91精品久久久久久久久久入口 | 亚洲精品久久久久999中文字幕 | 91九色网址 | 亚洲视频在线观看免费 | 国产精品自拍在线 | 久久男人中文字幕资源站 | 久久久影院一区二区三区 | 国产精品18久久久久久久久 | 亚洲天堂在线观看完整版 | 国产精品免费一区二区三区在线观看 | 808电影| 午夜三级理论 | 国产精品不卡在线观看 | 国产精品二区在线观看 | 久99久精品视频免费观看 | 色综合天天色综合 | 91福利视频免费 | 亚洲91精品在线观看 | 激情av五月婷婷 | 99热这里精品 | 亚洲视频精品 | 日韩在线无 | 日本福利视频在线 | 成人午夜片av在线看 | 国产一卡久久电影永久 | 天天弄天天干 | 久久国产经典视频 | 国产在线精品国自产拍影院 | 国产中文在线观看 | 激情婷婷综合网 | 成人蜜桃| 欧美日韩天堂 | 91黄视频在线观看 | 成人中文字幕+乱码+中文字幕 | 国产99久久九九精品免费 | 免费看wwwwwwwwwww的视频 久久久久久99精品 91中文字幕视频 | 免费观看久久 | 国产亚洲精品久久久久久久久久 | 久久精品视频网址 | 亚洲精品在线二区 | 国产成人免费网站 | 啪嗒啪嗒免费观看完整版 | 韩国av一区二区 | 日韩美女免费线视频 | 丝袜护士aⅴ在线白丝护士 天天综合精品 | 成人国产电影在线观看 | 国产麻豆精品传媒av国产下载 | 日韩欧美在线一区 | 国产首页 | 久久久午夜精品理论片中文字幕 | 国产欧美最新羞羞视频在线观看 | 亚洲视频 在线观看 | av福利电影| 亚洲成人资源 | 在线影院av | 四虎最新入口 | 99精品免费在线观看 | 欧美国产日韩一区二区三区 | 国产无吗一区二区三区在线欢 | 欧美极品少妇xxxxⅹ欧美极品少妇xxxx亚洲精品 | 欧美极度另类性三渗透 | 久久高清 | 天天摸日日摸人人看 | 又黄又刺激的视频 | 夜夜操夜夜干 | 福利网在线 | 操操综合网 | 免费国产在线视频 | 久久免费av | 欧美日韩在线观看一区 | 97超碰在线资源 | 国产精品精品久久久 | 国产成人一区二区精品非洲 | 亚洲国产精品电影在线观看 | 久久久综合九色合综国产精品 | 国产精品男女视频 | 欧美精品久久99 | 免费看短 | 国产精品久久久久久久电影 | 国产精品毛片一区二区 | 中文字幕2021 | 毛片a级片 | 99精品国产aⅴ| 麻豆久久久 | 免费在线观看成人小视频 | 成人av动漫在线观看 | 天天色婷婷 | 激情综合六月 | 国产中文字幕在线免费观看 | 成人免费视频网站 | 日韩特级片 | 亚洲六月丁香色婷婷综合久久 | 午夜一级免费电影 | 国产老太婆免费交性大片 | 在线播放第一页 | 久久久久久久影视 | 久久精品麻豆 | 四虎精品成人免费网站 | 五月天综合色 | 六月久久婷婷 | 久久精品这里热有精品 | 亚洲精品高清视频在线观看 | 中文字幕亚洲欧美日韩2019 | 日韩午夜电影网 | 成 人 a v天堂 | 久久综合色天天久久综合图片 | 久久激情影院 | 观看免费av| 国产精选在线 | 91传媒在线 | 97成人在线 | 亚洲国产日韩精品 | 91最新地址永久入口 | 国产成人免费观看久久久 | 成人污视频在线观看 | 2021久久| 五月婷婷操 | 91中文在线视频 | 欧美一区二区三区四区夜夜大片 | 日日精品 | 人人干天天射 | 美女av在线免费 | 成人免费在线视频 | 爱爱一区| 欧美日韩视频在线 | 中文字幕高清在线播放 | 欧美一区二区三区不卡 | 国产成人久久77777精品 | 香蕉色综合| 久久在线视频精品 | 91看片一区二区三区 | 天天操偷偷干 | www.狠狠插.com| 久操伊人 | 涩涩爱夜夜爱 | 国产中文字幕一区 | 波多野结衣资源 | 国内成人综合 | 国产亚洲久久 | 免费福利在线 | 亚洲最大av网站 | 国产亚洲精品久久久久久网站 | 久久免费视频在线观看 | 久久激情视频网 | 亚洲午夜精品一区二区三区电影院 | 久久亚洲精品电影 | 99久久久国产精品免费99 | 午夜精品视频一区二区三区在线看 | 久久视频免费在线 | 日本久久电影网 | 四虎国产精品成人免费影视 | 国产精品福利午夜在线观看 | 99精品欧美一区二区蜜桃免费 | 蜜臀av性久久久久蜜臀av | 中文字幕免费久久 | 在线免费观看黄色 | 久久99精品热在线观看 | 天天射天天射 | 国产不卡在线播放 | 国产自产在线视频 | 久久久久久久久久网站 | 色偷偷av男人天堂 |