Nutch爬虫环境搭建

2018-03-01 11:10:17来源:oschina作者:郭正阳人点击

分享

1 前言 1


2 环境介绍 2


3 准备工作 3


4 Solr安装 8


5 Hbase安装 14


6 Hadoop安装 17


7 Nutch安装 19


8 Solr使用 19


9 Nutch使用 19


1前言

1.1目的和范围


通过该环境框架的建置,使公司抓取互联网的数据为所用,并把获取数据进行统计化,信息化,预测化,本文档是Nutch爬虫框架安装手册,供公司项目经理同工程师搭建环境使用,本框架环境可适用于有需要全国互联网数据需求的企业。


1.2范围


目前该许多公司需要了解全国性质的互联网数据,如果需要人单个网页去爬取数据的话太慢,这种速度极慢而且互联网数据是动态的不是静态的很难去以人力去爬取,现在有很多公司想要了解全国互联网数据并进行公司数据的数据获取并做出统计,基于以上问题搭建此环境。此环境是“Nutch+solr+hbase+hadoop”集成环境,由郑州智游爱峰科技有限公司提出申请并搭建。


1.3术语定义


Jdk:java程序运行的基础环境


Ant:将软件编译、测试、部署等步骤联系在一起的自动化工具


Nutch:开源java搜索引擎,包括全文搜索和Web爬虫


Solr:独立的搜索应用服务器,它对外提供类似WebserviceAPI接口


Hbase:分布式NoSQL开源数据库,分布式存储系统


Hadoop:分布式系统基础环境


Linux:操作系统


CentOS:Linux发行版系统之一


Zookeeper:开源的分布式应用程序协调服务


1.4参考数据


《Nutch-API》


《Hbase-使用手册》


《Hadoop-使用手册》


《Solr-部署手册》


2环境介绍

2.1环境介绍


成熟性


环境在充分调研,需求,领导检讨的基础上进行选择与配置,并同领导确认,沟通了解,保证框架后续的实用性及易操作,并参考目前所有的爬虫框架进行分析,应更具成熟性。


实用性


在后续的了解中,需要部门及单位需要该爬虫或许数据进行分析统计,由此可见搭建后的实用性。


可靠性


能够正爬取用户所需要的数据,保证爬取准确,完整,使爬取数据能正常存储并分析。


稳定性


保证用户能正常爬取,设置由全文检索及全网爬取,能及时准确找到所需要爬取的数据,爬取的数据为爬取网站所使用所有数据如json、js、等后台交互的数据等文件,保证了爬取数据资料完整性和一致性。


可扩充性


本环境之初目标使用者为智游就业部方便采集全国就业数据,将来可采集其他数据并推广,该环境使用分布式技术,实现分布式爬取,并可试用Hadoop等分布式工具进行爬取数据分析等扩展功能。


Nutch该环境灵活性很高,可以被很多的客户订制并集成到自己的应用程序中,使用


Nutch的插件机制,Nutch可以作为一个搜索不同信息载体的搜索平台。当然最简单的 就是集成Nutch到你的站点,为你的用户提供搜索服务。


透明度


该环境所试用的技术全为开放源代码的,因此任何人都可以查看环境是如何工作的。


2.2功能及性能


该环境使用”Nutch+solr+Hbase+Hadoop”技术搭建,该环境致力于让每个人都能很 容易,同时花费很少就可以配置一台一流Web搜索引擎,环境能够做到如下功能与性 能:


Ø每个月取几十亿网页


Ø为这些网页维护一个索引


Ø对索引文件进行每秒上千次的搜索


Ø提供高质量的搜索结果


Ø以最小的成本运行


Ø分布式存储大量数据文件


Ø分布式分析统计数据

3准备工作

3.1软件


Nutch2.3.1的话,接下来说到Nutch 2.3.1的话,它是基于Gora 0.6.1的,所以版本必须和Gora的版本一致,Apache Gora官网上是这么描述的。

官方描述版本信息

经过研究,其实不需要这么多就可以完成本地模式的搭建,但是如下软件版本号必须一一对应:


ØJdk1.7(只要Hadoop Hbase支持即可)


ØApache Hadoop 1.2.1 and 2.5.2


ØApache HBase 0.98.8-hadoop2 (although also tested with 1.X)


ØApache Solr 4.10.3


ØApache Tomcat(可选,版本随意,用于集成solr)


ØAnt(版本无要求)

这几个就够了,这些都可以在Apache的官网Download,但有一点一定要注意版本一定要对! 版本一定要对! 版本一定要对!,这个重要各位读者必须懂得。

下载网址:http://mirror.bit.edu.cn/apache/

如果以上Hbase版本不对或者配置不对将导致Nutch抓取过程中InjectorJob的时间变得很长极长超级长。在这个位置上:

Hbabse版本不对及配置错误

下载完成后:

下载完成后

软件下载完成后请打开Linux命令窗口执行解压命令,本安装手册解压在了/use/Dzy下,这个目录代表着我存放这些软件的地方,文中代码中看见了这个路径请自动转换成你解压的位置。


解压命令:


cd /usr/Dzy


tar -zxvf解压缩文件名

解压完成:

左上角是路径


3.2基础环境配置说明


3.2.1JDK配置


1.在/usr/目录下创建java目录


[root@localhost ~]# mkdir/usr/java [root@localhost ~]# cd /usr/java


2.下载jdk,然后解压


[root@localhost java]# tar -zxvf jdk-7u79-linux-x64.tar.gz


3.设置环境变量


[root@localhost java]# vi /etc/profile


在profile中添加如下内容:


#set java environment JAVA_HOME=/usr/java/jdk1.7.0_79 JRE_HOME=/usr/java/jdk1.7.0_79/jre CLASS_PATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin export JAVA_HOME JRE_HOME CLASS_PATH PATH


让修改生效:


[root@localhost java]# source /etc/profile


4.验证JDK有效性


[root@localhost java]# java -version java version "1.7.0_79" Java(TM) SE Runtime Environment (build 1.7.0_79-b15) Java HotSpot(TM) 64-Bit Server VM (build 24.79-b02, mixed mode)

3.2.2Linux配置


SSH免密码登录


采用ssh策略的原因为了hadoop中zookeeper的管理便利,我们所知远程访问主机采取的多为用户名+密码的模式,往往因为权限问题导致错误,发现ssh策略就可以很好的回避这点


1.开启终端,输入以下指令,安装ssh


sudo apt-get install openssh-server


2.在终端,输入以下指令,开启ssh服务


service sshd restart


3.输入以下命令,制作ssh密钥上传到本机


ssh-keygen -t dsa -P '' -f ~/.ssh/id_dsa


cat ~/.ssh/id_dsa.pub >> ~/.ssh/authorized_keys

4.可以用以下命令检测是否成功,成功的话会显示系统版本及登录时间


ssh localhost

登录成功

关闭防火墙


chkconfig iptables off


此操作是为了Hbase zookeeper等能正常安装启动。

3.2.3Ant配置


1.下载tar.gz版ant


2.复制到/usr下


3.解压


tar -vxzf apahce-ant-1.9.2-bin.tar.gz


4.改变权限


chown -R yjdabcapahce-ant-1.9.2


chown -R :usersapahce-ant-1.9.2


chmod -R +xapahce-ant-1.9.2


5.修改系统配置文件


vi /etc/profile


#set Ant enviroment


export ANT_HOME=/usr/apache-ant-1.9.2


export PATH=$PATH:$ANT_HOME/bin


6.立刻将配置生效


source /etc/proifle


7.测试ant是否生效,运行ant -version,输出如下内容


[root@localhost ~]# ant -version


Apache Ant version 1.8.1 compiled on April 30 2010


ant配置完成。

3.3路径说明

以下是本人的解压位置: TomcatPath的值就是/usr/Dzy/tomcat SolrPath的值就是/usr/Dzt/solr-4.10.3 NutchPath的值就是/usr/Dzy/apache-nutch-2.3.1


HadoopPath的值就是/usr/Dzy/hadoop-2.5.2 HbasePath的值就是/usr/Dzy/hbase-0.98.8-hadoop2

4Solr安装

4.1Solr与Tomcat集成


单独启动Solr也可以,但原因在于将来实现的不是这样的伪集群模式,Solr在搜索引擎中的作用是建立索引,而管理集群的工作则交给了zookeeper,而载体就是Tomcat。


参照准备工作的步骤完成准备工作,接下来就可以正式开始部署。


4.2解压Tomcat与Solr


这里的路径随意,假设Tomcat的主目录为TomcatPath,Solr的主目录为SolrPath,Nutch的解压后的主目录为NutchPath


Ø在TomcatPath下的/webapps文件夹内新建名叫solr文件夹


Ø在SolrPath下路径/example/webapps有名为solr.war的压缩文件,将其复制到刚才新建的solr文件夹内


Ø在当前目录将solr.war解压


完成图


请各位务必注意路径,本人将截图包括路径,各位可以根据自身情况稍加推理,找到正确的位置。


Ø将SolrPath下的/example内的solr文件夹复制到TomcatPath路径下,如图

完成图2


Ø在NutchPath下的/conf中的schema.xml文件,复制到TomcatPath下的/solr/collection1/conf内覆盖原文件

第一步准备工作就算完成啦,至于为什么复制Nutch的 schema.xml文件呢?了解过MYSQL的人肯定看这个词很眼熟,这就是告知solr服务器需要保留哪些类型数据的配置文件

4.3修改Solr配置


目的在于修改solr的配置文件,使其能准确的找到core的位置,本人推测core的作用正是像处数据理机一样,里面集成了分词器等,使其可以可以建立索引。


在TomcatPath下/webapps/solr/WEB-INF的文件内修改web.xml文件,将选取部分的注释取消(将开头消除即可)


修改web.xml

正确的写法是


solr/home


/home/as/workspace/tomcat/solr


java.lang.String


标签下写的是步骤1.1的最后一步复制的solr文件所在的路径,不是solr.war的解压路径

4.4启动Solr


Ø在TomcatPath目录下执行以下命令,启动Tomcat


bash startup.sh


Ø然后在浏览器中输入以下网址 http://localhost:8080/solr/


成功启动


如果终端显示了Permission denied,这就是权限不够,拒绝执行的意思,使用su 命令 来提升权限。


如果启动为404所有配置也没错,那就是因为jar包不全,需要将solr/example/lib/以下ext下面的lib 放到 tomcat中的 solr项目的lib目录下tomcat/webapps/solr/WEB-INF/lib


4.5删除Solr索引

简单介绍一下为什么要删除,因为在实验中Nutch抓取后,将抓取到数据存储到Hbase(也可以是其他的数据库)然后由Solr来生成索引执行查询,如果不删除,那么将永远保留着第一次抓取的结果,无法查询今后的抓取结果。

在Core selector的下拉栏中选择Collection1,然后再选取Documents,将右侧的Documents Type选为XML,如下图


http://localhost:8080/solr/#/collection1/documents


然后在下面文本域中输入


*:*


点击Submit Document执行,这句话用于清除所有索引,各位可以按需要删除指定的索引


5Hbase安装

5.1Hbasep配置(单机版)


只配置所需的最低设定了,各位可以参照官方文档尝试更多的有趣的设置,在配置Hbase之前,需要确认防火墙是否关闭。


Ø在HbasePath下,在/conf下修改hbase-env.sh,添加以下设置


export JAVA_HOME=/usr/java/jdk1.7.0_79


export HBASE_MANAGES_ZK=true


Ø在相同目录下修改hbase-site.xml,在标签中添加以下设置,192.168.202.128是本人自己IP,各位可以改为自己的ip



hbase.rootdir


/home/hbase




hbase.zookeeper.quorum


192.168.202.128:2181



·


因为采用的是单机版的Hbase,hbase.rootdir代表hbase的主目录在当前hbase存储的位置,hbase.zookeeper.quorum代表hbase的zookeeper配置。

5.2Hbase启动

Ø在HbasePath目录下,启动终端输入


bash ./bin/start-hbase.sh


开启Hbase的服务,此时在浏览器输入以下网址http://localhost:60010/master-status可以看见Hbase的homepage


http://localhost:60010/master-status

这里可以看见Hbase数据库中所有的表和信息,除此之外还有一种 方式在终端内实现查看(适用于非图形界面的系统)

Ø在HbasePath目录下,在终端输入


./bin/hbase shell

启动Hbase的Shell模式,如下图


Hbase shell

5.3Hbase 查询


检查Hbase是否配置成功正常使用,执行如下操作检测。


Ø查看Hbase数据库所有的表


list


Ø创建Hbase表


Create ‘表名’,’字段’

注意!如果以上操作执行报错,请先检查配置文件是否配好,防火墙是否关闭,Hbase时间也要与系统时间同步,HBase时间同步命令如下:


时间不同步问题:在每台机子上输入sudo ntpdate time.nist.gov再起hbase集群就行了。


6Hadoop安装

6.1Hbasep配置(伪分布)


Ø在HadoopPath下 /etc/hadoop目录中的修改core-site.xml内在标签中添加以下设置



fs.default.name


hdfs://localhost:9000




dfs.replication


1




hadoop.tmp.dir


/usr/tmp


这三个属性分别是hdfs的主机位置,再连接的次数与hadoop缓存存放位置

Ø在相同目录下,将 mapred-site.xml.template在当前目录复制黏贴,并重命名为mapred-site.xml在标签中添加以下设置



mapred.job.tracker


localhost:9001



此处代表执行map phase的tracker的主机,因为是本地伪集群,故写成这样


6.2Hbasep启动


Ø在HadoopPath下,在终端输入以下命令,执行namenode初始化


bin/hadoop namenode -format


Ø在终端输入下面的命令,成功后可以下图


Bash ./sbin/start-all.sh


hadoop启动成功

我们可以清楚的看出hadoop的执行过程,dfs与yarn,hadoop成功启动,并使用jps查看进程是否都正确启动。


7Nutch安装

Ø在NutchPath目录下,把/ivy文件夹中修改ivy.xml文件,将以下内容的注释取消(将包含此代码段开头消除即可)。


这是将Nutch的默认的结果存储方式变更为Hbase,如需MySql请根据Mysql进行相关配置。


Ø在NutchPath目录下,把/conf文件夹中修改nutch-site.xml文件,在标签中添加以下设置



storage.data.store.class


org.apache.gora.hbase.store.HBaseStore


Default class for storing data




http.agent.name


My Nutch Spider




plugin.includes


protocol-http|urlfilter-regex|parse-(html|tika)|index-(basic|anchor)|urlnormalizer-(pass|regex|basic)|scoring-opic|index-anchor|index-more|languageidentifier|subcollection|feed|creativecommons|tld


Regular expression naming plugin directory names to


include. Any plugin not matching this expression is excluded.


In any case you need at least include the nutch-extensionpoints plugin. By


default Nutch includes crawling just HTML and plain text via HTTP,


and basic indexing and search plugins. In order to use HTTPS please enable


protocol-httpclient, but be aware of possible intermittent problems with the


underlying commons-httpclient library.




这三个property分别指的是Nutch的默认存储类型,爬虫的名字,和插件库的位置,如果错误中出现job-XXXXX的情况,那就是没有配置好插件库property


Ø在相同目录下,修改regex-urlfilter.txt,将最后部分修改为


# accept anything else


#+.


+^http://([a-z0-9]*/.)*nutch.apache.org/

这里写的是抓取过滤的正则表达式,各位可以按各自所需修改,网上有些说也要修改nutch-default.xml,其实没有这个必要,若在nutch-site.xml存在配置,会优先选择其中的属性

Ø在相同目录下修改gora.properties文件,添加以下配置,确保存储在Hbase上


gora.datastore.default=org.apache.gora.hbase.store.HBaseStore


Ø在NutchPath目录下,在/usr/Dzy/apache-nutch-2.3.1/runtime/local/文件下创建urls文件夹


在urls文件夹中创建seed.txt,在其中输入


http://nutch.apache.org/


这里存放的是Nutch要爬取的网页


在NutchPath路径下,启动终端,输入以下命令,开始Ant编译


Ant runtime

ant编译所需要时间很长请耐心等待,编译完成后目录结构是这样的,多出来一个runtime,如下图


runtime成功


Ø最后一步就是,将Nutch数据保存到Hbase的jar包存到local内




8Nutch使用

启动Solr


在TomcatPath目录下执行以下命令,启动Solr


bash startup.sh


启动Hadoop


在HadoopPath下,在终端输入以下命令,启动Hadoop


bash ./sbin/start-all.sh


启动Hbase


在HbasePath目录下,启动终端输入,进程要都正常启动才可以启动Nutch


bash ./bin/start-hbase.sh


启动Nutch


在NutchPath下,输入以下指令

cd /usr/Dzy/apache-nutch-2.3.1/runtime/local

./bin/crawl /usr/Dzy/apache-nutch-2.3.1/runtime/local/urls NutchTable http://localhost:8080/solr/collection1 2

这段指令的参数的意义: urls位置--- /usr/Dzy/apache-nutch- 2.3.1/runtime/local/urls Hbase中创建的表名--- NutchTable SolrCore的位置--- http://localhost:8080/solr/collection1 迭代次数--- 2


以上参数可以按照自己的需要调整,可以参考Apache Nutch的说明文档


如果一切OK的话,会出现以下状态,如果injectjob时间极长时可以检查一下Hbase版本配置


启动动画

注意!如果出现JOB时间极长,请先检查配置文件是否配好,防火墙是否关闭,Hbase时间也要与系统时间同步,HBase时间同步命令如下:


时间不同步问题:在每台机子上输入sudo ntpdate time.nist.gov再起hbase集群就行了。


9Solr使用

9.1查询Nutch抓取的数据


如果完成了Nutch的抓取流程,这里可以看见抓取的结果,当然如果没有执行过,里面是什么都查不出来的,所以耐心的跟着文档往下走完成以后的指南,保证不出大意外,一定会看见的。


在Core selector的下拉栏中选择Collection1,然后再选取Query,点击右侧Execute Query,如下图

http://localhost:8080/solr/#/collection1/query



可按需要调整查询参数,语法也是很简单的,。以上是该环境的所有配置及流程。

10Nutch总结

Nutch下Runtime 两个文件deploy及local 分别代表了nutch的两种方式。


Deploy :以hadoop方式运行


Local:以本地方式运行

主要类分析: 一、org.apache.nutch.crawl.Injector: 1,注入url.txt 2,url标准化 3,拦截url,进行正则校验(regex-urlfilter.txt) 4,对符URL标准的url进行map对构造,在构造过程中给CrawlDatum初始化得分,分数可影响url host的搜索排序,和采集优先级! 5,reduce只做一件事,判断url是不是在crawldb中已经存在,如果存在则直接读取原来CrawlDatum,如果是新host,则把相应状态存储到里边(STATUS_DB_UNFETCHED(状态意思为没有采集过))二、org.apache.nutch.crawl.Generator: 1,过滤不及格url (使用url过滤插件) 2,检测URL是否在有效更新时间里 3,获取URL metaData,metaData记录了url上次更新时间 4,对url进行打分 5,将url载入相应任务组(以host为分组) 6,计算url hash值 7,收集url, 直至到达 topN 指定量三、org.apache.nutch.crawl.Fetcher: 1,从segment中读取,将它放入相应的队列中,队列以queueId为分类,而queueId是由 协议://ip 组成,在放入队列过程中, 如果不存在队列则创建(比如javaeye的所有地址都属于这个队列:http://221.130.184.141)--> queues.addFetchItem(url, datum); 2,检查机器人协议是否允许该url被爬行(robots.txt) --> protocol.getRobotRules(fit.url, fit.datum); 3,检查url是否在有效的更新时间里 --> if (rules.getCrawlDelay() > 0) 4,针对不同协议采用不同的协议采用不同机器人,可以是http、ftp、file,这地方已经将内容保存下来(Content)。 --> protocol.getProtocolOutput(fit.url, fit.datum); 5,成功取回Content后,在次对HTTP状态进行识别(如200、404)。--> case ProtocolStatus.SUCCESS: 6,内容成功保存,进入ProtocolStatus.SUCCESS区域,在这区域里,系统对输出内容进行构造。 --> output(fit.url, fit.datum, content, status, CrawlDatum.STATUS_FETCH_SUCCESS); 7,在内容构造过程中,调取内容解析器插件(parseUtil),如mp3/html/pdf/word/zip/jsp/swf……。 --> this.parseUtil.parse(content); --> parsers.getParse(content); 8,我们现在研究html解析,所以只简略说明HtmlParser,HtmlParser中,会解析出text,title, outlinks, metadata。 text:过滤所有HTML元素;title:网页标题;outlinks:url下的所有链接;metadata:这东西分别做那么几件事情 首先检测url头部的meta name="robots" 看看是否允许蜘蛛爬行, 其次通过对meta http-equiv refresh等属性进行识别记录,看页面是否需要转向。四、org.apache.nutch.parse.ParseSegment: 1,这个类逻辑就相对简单很多了哦,它对我们也是很有价值的,它只做一件事情,就是对爬行下来的Content(原始HTML)进行解析,具体解析通过插件来实现。 比如我们要做的数据分析、数据统计都可以在这进行实现。 2,执行完成后,输出三个Map对解析内容、包含所有链接的分析后的结果 、outlinks五、org.apache.nutch.crawl.CrawlDb: 主要根据crawld_fatch输出更新crawldb。 1,map对crawld_fatch、crawldb地址进行标准化(nomalizer)和拦截操作(filte); 2,reduce在对两crawld_fatch和crawldb进行合并更新。六、org.apache.nutch.crawl.LinkDb: 这个类的作用是管理新转化进来的链接映射,并列出每个url的外部链接(incoming links)。 1,先是对每一个url取出它的outLinks,作map操作把这个url作为每个outLinks的incoming link, 2,在reduce里把根据每个key来把一个url的所有incoming link都加到inlinks里。 3,这样就把每个url的外部链接统计出来了,注意,系统对只对外部链接进行统计,什么叫外部链接呢,就是只对不同host进行统计, 记住iteye.com和biaowen.iteye.com是两个不同的host哦。 --> boolean ignoreInternalLinks = true; 4,然后一步是对这些新加进来的链接进行合并。七、org.apache.nutch.crawl.Indexer: 这个类的任务是另一方面的工作了,它是基于hadoop和lucene的分布式索引。它就是为前面爬虫抓取回来的数据进行索引好让用户可以搜索到这些数据。 这里的输入就比较多了,有segments下的fetch_dir,parseData和parseText,还有crawldb下的 current_dir和linkdb下的current_dir。 1,在这个类里,map将所有输入都装载到一个容器里边, 2,在到reduce进行分类处理, 3,实现拦截 --> this.filters.filter(doc, parse, key, fetchDatum, inlinks); 4,打分 --> this.scfilters.indexerScore(key, doc, dbDatum,fetchDatum, parse, inlinks, boost); 5,当然要把这些数据体组合成一个 lucene的document让它索引了。 6,在reduce里组装好后收集时是,最后在输出的OutputFormat类 里进行真正的索引。 doc里有如下几个field content(正文) site(所属主地址) title(标题) host(host) segement(属于哪个segement) digest(MD5码,去重时候用到) tstamp(暂时不知道什么东西) url(当前URL地址) 载了一个例子:doc ={content=[biaowen - JavaEye技术网站 首页 新闻 论坛 博客 招聘 更多 ▼ 问答 ………………(内容省略)………… biaowen 永NF/ICP备05023328号],site=[biaowen.iteye.com],title=[biaowen - JavaEye技术网站],host=[biaowen.iteye.com],segment=[20090725083125],digest=[063ba8430fa84e614ce71276e176f4ce],tstamp=[20090725003318265],url=[http://biaowen.iteye.com/]}八、org.apache.nutch.crawl.DeleteDuplicates: 这个类的作用就是这它的名字所写的意思--去重。 前面索引后(当然不是一次时的情况)会有重复,所以要去重。为什么呢,在一次索引时是不重复的,可是多次抓取后就会有重复了。 就是这个原因才要去重。当然去重的规则有两种一个是以时间为标准,一种是以内容的md5值为标准。九、org.apache.nutch.indexer.IndexMerger: 这个类就相对简单了,目的将多个indexes合并为一个index,直接调用lucene方法实现! 附带些参考资料:


目录结构,参考自《Lucene+Nutch搜索引擎开发》 一、crawldb下载的url,以及下载日期,用来进行页面更新 二、segements存放抓取页面和分析结果1、crawl_generate:待下载url2、crawl_fetch:每个下载url的状态3、content:每个下载页面的内容4、parse_text:包含每个解析过的url文本内容5、parse_data:每个url解析出的外部链接和元数据6、crawl_parse:用来更新crawl的外部链接库 三、linkdb存放url的互联关系 四、indexes:存放每次下载的独立索引目录 五、index:符合lucene格式的索引目录,是indexes里所有index合并后的完整索引

最新文章

123

最新摄影

闪念基因

微信扫一扫

第七城市微信公众平台