学爬虫?一篇文章就够了!

阅读: 评论:0

学爬虫?一篇文章就够了!

学爬虫?一篇文章就够了!

前言:
——想入门爬虫?想要爬一批数据毕业?想要爬一整个网站做数据分析?只要你有python基本功,看完这篇文章,就可以达到上述目的。
——想要进阶学习,做爬虫工程师?大可以看完文章,再有指向性的,针对薄弱处深入学习,实现月薪X万的目标。
5年前写过一篇博客 《python Scrapy 框架做爬虫 ——入门地图》 ,现在看来已经比较局限。所以,接着最近做的事情,重新总结爬虫中的林林总总,作为比较完整的新地图,给后来的爬虫学习者一些便利。

本文的使用方法是,第一遍通读全文了解概况,建立整体认知框架。第二遍逐个链接进入,细致学习和实践。

Happy Hacking!

干货列表

  • 1,认识爬虫
    • 一个比较完善的爬虫
      • 网络交互工具:requests
      • 内容提取工具:正则表达式
    • 爬虫小结
  • 2,网页分析:让抓取内容丰富起来
    • xpath
    • beautiful soup
    • 进阶NLP
  • 3,数据库:给抓取内容一个家
    • MySQL
    • MongoDB
  • 4,异步:让速度飞起来
  • 5,使用框架:站在巨人的肩膀上
    • Scrapy
      • scrapy架构
      • scrapy拓展
        • 使用代理
        • 更换UA
        • 提供Cookie
        • 控制下载错误重试次数
  • 6,分布式:让巨人飞起来
    • Scrapy-Redis
  • 7,初遇大数据:用布隆过滤器判重
    • BloomFilter
  • 8,登录:向反爬虫工程师亮剑
    • 动态网页
    • 验证码破解
    • 进阶网页加解密
  • 9,爬虫工程师必备百宝箱
    • User-agent池
    • Cookie池
    • 代理池
    • 崔庆才
  • 站在爬虫的山顶上:进阶指南
    • 爬虫专家
    • 数据科学家
    • 机器学习专家
    • 前端工程师

1,认识爬虫

网络是一个比喻。分散在地球不同角落的机器中存储的文档、音乐、视频、图片等资源,可以在初始地被拆散分解成线缆或空气中的能量波动,再在目的地重新组装成原来的形式。这种无处不达的资源联通系统,被我们称为互联网。
作为网上冲浪的弄潮儿,通常只关心一部电影、一首音乐或一部小说。但如果我们关注某一类信息,比如全部的科幻电影、全部的言情小说、全部的摇滚音乐、甚至是关注全世界所有的信息(搜索引擎),我们就需要一个自动化的程序,在互联网上不停歇的来回移动,这便是“爬虫”。

友情提示,以下内容,需要python才能上车。

一个比较完善的爬虫

# An Crawler
start_url = '/first.html';
tobe_visited_list = [start_url]
have_visited_list = []
while(tobe_visited_list):url = tobe_visited_list.pop()if url not in have_visited_list:have_visited_list.append(url)page = (url)for new_url in page:tobe_visited_list.append(new_url)for resource in page:save(resource)

上面就是一个完整的爬虫(python伪代码):从待访问列表中取出链接,获取链接中的页面,将想要的数据存储下来,再将页面中的新链接放进待访问列表中,循环往复。
有了框架,下面我们介绍几个工具,用于替代掉伪代码中的伪函数,让爬虫真正可以运行。

网络交互工具:requests

requests
据说如果用Python代码访问网络,只能学一个库,就学这个。打开上面的超链接,你会发现安装和使用的方法;

>>> r = ('', auth=('user', 'pass'))
>>> r.status_code
200
>>> r.headers['content-type']
'application/json; charset=utf8'
>>> r.encoding
'utf-8'
>>> r.text
u'{"type":"User"...'
>>> r.json()
{u'private_gists': 419, u'total_private_repos': 77, ...}

用()替代伪代码中的(),就可以真正获取到链接中的内容!

内容提取工具:正则表达式

正则表达式
获取到页面的内容,就要从里面提取我们想要的resource了。我们想要的数据,通常会存在固定的格式,比如手机号码通常是11个数字组成的字符串;
正则表达式就是描述这种格式的语言,点上面的链接,可以看一下正则表达式的语法,比如手机号的11个数字可以表示成:[0-9]{11}

爬虫小结

通过理清爬虫的思路,学习用requests工具获取网络上的资源,正则表达式提取网页中的元素,你已经掌握了爬虫,恭喜!
下面让我们来用爬虫爬取一个页面中的网址,让爬虫跑起来吧:

# An Crawler
import requests
import restart_url = '/';
tobe_visited_list = [start_url]
have_visited_list = []
while(tobe_visited_list):url = tobe_visited_list.pop()if url not in have_visited_list:have_visited_list.append(url)page = (url)pattern = repile(r'"[a-zA-Z]+://[^s]*[|]"')for new_url in pattern.):tobe_visited_list.append(new_url.split(""")[1])print(new_url)
#		for resource in page:
#			save(resource)

爬虫输出一系列网址

""
".htm"
".htm"
...

Tips:你想要的正则表达式可能有很多人都分享过,搜索一下可以在起步阶段省下不少时间。

有了上面的工具基础,幸运的话,爬几万条数据毕业的任务就可以顺利完成了。但如同爬上景山和爬上珠穆朗玛峰都是“爬山”,爬虫之间的差距也可以大到令人咋舌。

2,网页分析:让抓取内容丰富起来

上面我们介绍的爬虫,抓取到的内容是一个html页面,用html详细的描述了资源的排布。对于浏览器来说,html是他的母语,但是对于想要爬数据的我们,获取到的html是非常杂乱的。我们需要一些好工具,把html安排妥当,方便我们上下其手随心所欲。

xpath

通过xpath提供的初始化方法,把html变成一个可以操作的selector对象,再用该对象提供的多种多样的内建函数取出我们想要取出的东西。
学习xpath语法
看python使用xpath的实例

beautiful soup

一个强大的网页分析工具,xpath不好用,正则不好拼凑,试试这个。
比较细致的教程

进阶NLP

通用爬虫中,要匹配的信息形式千奇百怪,成千上万的网站又是由不同的人打造,有没有那种不用手工做选择的工具,直接拿到想要的内容呢?
有。目前还不成熟,基于NLP的自动内容提取,但未来可期。所以,现在,还是去研究页面吧。

3,数据库:给抓取内容一个家

如果我们想要抓取的内容,不是一个txt就可以存下来,不是一个文件夹就可以保存好:比如我想抓知乎上的用户和问题,但是用户和问题又有交叉,怎么存好?用MySQL。
如果我不在乎字段间的关系,但是获取的信息特别多,我想要把这大量的信息快速的存起来,又怎么办?用MongoDB。

MySQL

学习 MySQL

MongoDB

学习 MongoDB

4,异步:让速度飞起来

如果我们要爬取的数据量巨大,比如1亿,再如果每次通过url获取信息需要1s,那么爬完全部信息,需要的时间就是1亿秒,即3年左右。有没有什么办法,可以加速获取信息呢?

有。请求传送到远程服务器、资源从服务器传回本地都需要时间,在这漫长的网络传输时间里,cpu一直在等待资源收齐,再进行解析资源、存储资源动作。如果我们可以让cpu发送完一条请求,继续发送下一条请求,而等资源传输完成,信息收集好时,再提醒cpu进行处理的话,cpu是没有等待时间的。

上述的这种把「发送请求」和「处理资源」拆分的爬虫,就是异步爬虫。异步爬虫将网络IO的时间排除,可以榨干cpu的效率,大大加速爬虫爬取速度!

Tips:异步是一个相当通用的概念,要深刻理解异步,需要比较全面的了解计算机工作的原理。对于Python来说,线程和进程、阻塞和非阻塞交相辉映,事件驱动的异步编程写法比较复杂,容易出错。从目的考量选择出发的道路,站在巨人的肩膀上非常明智。
理解Python异步

5,使用框架:站在巨人的肩膀上

前面几节,针对基础爬虫的拓展方向,我们给出了工具说明和学习链接。网页析取工具Xpath、beautiful soup可以帮助我们从获取到的页面中收集想要的数据,MySQL、MongoDB可以帮我们把数据很妥善的保存。但是,如果我们在开始的小爬虫的基础上,添加页面分析和数据库读写,一个超大的ugly函数就要出现,难读、难维护、难扩展,难上加难。

此外,对于加速,我们知道有一种叫做异步爬虫的机制可以让速度起飞,但是学会编写这种异步爬虫好像很费时(学不会)。

因为已经21世纪了,爬虫作为几十年前伴随互联网发展的技术,已经有了许多积累了前人心血的成果可以复用。目标比较出色的,就是Scrapy框架。

Scrapy

关于scrapy,这里有一个稍显过时,但依然很有借鉴意义的doc
我们这里仅对scrapy为什么好用(架构),能够怎么用(插件),做比较宏观的描述,并给出具体使用可以参考的链接。

scrapy架构

对scrapy的初步使用,就是了解架构,写自己的spider和item_pipeline。

scrapy梳理了爬虫中的关键流程,将爬虫分为几个组件,用引擎engine串联起整个流程:
1,spider组件:用来分析网页,将其中的新链接作为request对象输出给engine,将其中的数据作为item输出给engine;
2,downloader组件:用engine给予的请求对外获取信息,包装成response对象传递回来作为输出还给engine;
3,scheduler组件:持续从engine收集request对象放入队列,同时engine不断的从中按需拿取request对象;
4,item pipeline组件:当engine收到item对象就传递给该组件,该组件进行处理;

在实际的应用中,通常我们只要用前面学过的xpath、beautiful soup定义好自己的spider,然后写一个将数据存入数据库的pipeline,框架就可以飞快的跑起来,你就拥有了一个单机最强异步爬虫。

一个使用案例

scrapy拓展

机构虽定,功能却依然可以拓展。scrapy中,spider和engine质检的交互要通过spiker_middleware,downloader和engine之间的交互要通过downloader_middleware。通过启用框架提供或自己写的spiker_middleware、downloader_middleware,我们可以控制request的细致参数和下载的细致动作。

使用代理

代理是一个ip:port对,类似123.345.456.789:1234,代理的作用是把你的请求发送到代理服务器上,代理服务器获取到信息后,再反向传输给你。所以代理服务是一种计算和网络资源,因此通常需要去网上付费购买。(免费的通常非常慢)
购买到代理服务后,可以将获取代理IP的接口(例如5秒钟取到5个ip:port对)进一步封装。如果每个代理地址的有效时间1分钟,那么1分钟内每隔5秒我们能够获取到5个代理ip,存储积累起来我们就有了规模为60个代理ip的代理池。成熟的代理池需要:不断从接口获取代理ip并存储,不间断测试删除过期代理ip,提供对外接口返回代理池中的一个可用代理。(工具见后续工具链接)
有了自己的代理池服务,我们就可以随时通过接口获取到一个代理ip了,那么,如何在scrapy中使用呢?
可以在middlewares.py中实现一个自己的中间件:

class ProxyMiddleware():def __init__(self, proxy_url):self.logger = Logger(__name__)self.proxy_url = proxy_urldef process_request(self, request, spider):a['proxy'] = self.proxy_url# 使用classmethod,通过crawler初始化,方便调用settings    @classmethod def from_crawler(cls, crawler):settings = crawler.settingsreturn cls(# 获取在settings中设置的key:valproxy_url&#('PROXY_URL'))

同时在setting.py中添加一行:

# 启用代理中间件
DOWNLOADER_MIDDLEWARES = {'YOUR-PROJECT-NAME.middlewares.ProxyMiddleware': 555,
}
# 你自己的代理服务地址
PROXY_URL = '127.0.0.1:5000'
更换UA

user-agent是request中的一个字段,用来标识请求,告知服务器你的操作系统类型,浏览器版本等等信息。通过更换user-agent,每一个请求都告诉服务器不同的信息,可以降低请求被禁止的概率。
再实现一个middleware:

class RotateUserAgentMiddleware(UserAgentMiddleware):"""a useragent middleware which rotate the user agent when crawl websitesif you set the USER_AGENT_LIST in settings,the rotate with it,if not,then use the default user_agent_list attribute instead."""#the default user_agent_list composes chrome,I E,firefox,Mozilla,opera,netscape#for more user agent strings,you can find it in .phpuser_agent_list = ['Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.43 Safari/537.31','Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.60 Safari/537.17','Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1309.0 Safari/537.17','Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.2; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0)','Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 6.0; en-US)','Mozilla/5.0 (Windows; U; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)','Mozilla/6.0 (Windows NT 6.2; WOW64; rv:16.0.1) Gecko/20121011 Firefox/16.0.1','Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:15.0) Gecko/20100101 Firefox/15.0.1','Mozilla/5.0 (Windows NT 6.2; WOW64; rv:15.0) Gecko/20120910144328 Firefox/15.0.2','Mozilla/5.0 (Windows; U; Windows NT 6.1; rv:2.2) Gecko/20110201','Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9a3pre) Gecko/20070330','Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.13; ) Gecko/20101203','Opera/9.80 (Windows NT 6.0) Presto/2.12.388 Version/12.14','Opera/9.80 (X11; Linux x86_64; U; fr) Presto/2.9.168 Version/11.50','Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; de) Presto/2.9.168 Version/11.52','Mozilla/5.0 (Windows; U; Win 9x 4.90; SG; rv:1.9.2.4) Gecko/20101104 Netscape/9.1.0285','Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.1.7pre) Gecko/20070815 Firefox/2.0.0.6 Navigator/9.0b3','Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.12) Gecko/20080219 Firefox/2.0.0.12 Navigator/9.0.0.6','Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36',]def __init__(self, user_agent=''):self.user_agent = user_agentdef _user_agent(self, spider):if hasattr(spider, 'user_agent'):return spider.user_agentreturn random.choice(self.user_agent_list)def process_request(self, request, spider):ua = self._user_agent(spider)if ua:request.headers.setdefault('User-Agent', ua)

在settings.py中启用这个中间件:

DOWNLOADER_MIDDLEWARES = {'YOUR-PROJECT-NAME.middlewares.RotateUserAgentMiddleware': 400,
}
提供Cookie

登录网站之后,我们是不是很久都不用重新登录?这种记忆机制就是cookie和服务器“会话”的交互完成的。

cookie是request的另外一种特殊字段,用来标识request是否来自某个特定会话,服务器会根据这个字段选择会话再进行应答。如果没有cookie,就不能找到特定会话,服务器就会不知所措。

因此如果我们爬取的内容需要登录之后,由特定建立的会话来提供,那么可以先登录一批账号,把登录之后的cookie保存下来,存到一个数据库里,对外提供一个访问接口返回随机cookie,就形成了自己的cookie池服务。

有了自己的cookie池服务,类似接入代理池服务,再写一个中间件,给request添加cookie:

class CookiesMiddleware():def __init__(self, cookies_url):self.logger = Logger(__name__)kies_url = cookies_urldef get_random_cookies(self):try:response = (kies_url)if response.status_code == 200:cookies = json.)return cookiesexcept requests.ConnectionError:return Falsedef process_request(self, request, spider):self.logger.debug('正在获取Cookies')cookies = _random_cookies()if kies = cookiesself.logger.debug('使用Cookies ' + json.dumps(cookies))@classmethoddef from_crawler(cls, crawler):settings = crawler.settingsreturn cls(cookies_url&#('COOKIES_URL'))

同样在settings.py中添加:

# 启用cookie中间件
DOWNLOADER_MIDDLEWARES = {'YOUR-PROJECT-NAME.middlewares.CookiesMiddleware': 555,
}
# cookie池地址
COOKIES_URL = 'localhost:5000/random'
控制下载错误重试次数

response 返回之后,不已定总是好消息,网络抖动或者是代理过期都会造成访问失败。失败了怎么办?当然是继续干了!Retry!
下面的retry中间件,对418错误进行无限retry。你可以根据自己的需求,更改重试行为。

from  import *class MyRetry(RetryMiddleware):def process_response(self, request, response, spider):a.get('dont_retry', False):return responseif response.status _http_codes:reason = response_status_message(response.status)return self._retry(request, reason, spider, response.status) or responsereturn responsedef _retry(self, request, reason, spider, response_status = 0):retries = ('retry_times', 0) + 1retry_times = self.max_retry_timesif 'max_retry_times' a:retry_times = a['max_retry_times']stats = awler.statsif response_status == 418 or retries <= retry_times:logger.debug("Retrying %(request)s (failed %(retries)d times): %(reason)s",{'request': request, 'retries': retries, 'reason': reason},extra={'spider': spider})retryreq = py()a['retry_times'] = retriesretryreq.dont_filter = Trueretryreq.priority = request.priority + self.priority_adjustif isinstance(reason, Exception):reason = global_object_name(reason.__class__)stats.inc_value('retry/count')stats.inc_value('retry/reason_count/%s' % reason)return retryreqelse:stats.inc_value('retry/max_reached')logger.debug("Gave up retrying %(request)s (failed %(retries)d times): %(reason)s",{'request': request, 'retries': retries, 'reason': reason},extra={'spider': spider})

同样对settings.py更改,让修改生效:

DOWNLOADER_MIDDLEWARES = {#禁用原来的retry中间件'RetryMiddleware': None,#启用自己的retry中间件'YOUR-PROJECT-NAME.middlewares.MyRetry': 301,
}

6,分布式:让巨人飞起来

通过上面介绍中的案例,我们已经可以熟练的利用scrapy框架,定制爬虫,安排数据处理和存储,更换代理、UA、Cookie来隐匿我们的行踪,也可以通过控制下载错误代码控制下载行为。另外作为scrapy的优势,我们已经可以做到非常高的并发量,把单机的资源榨干。

但是回到开始的大数据问题,如果我们要获取1亿数据需要同步爬虫运行3年,就算异步爬虫可以加速10倍,依然需要4个月的时间,还是错过了毕业季!

如果我们能够拥有4台机器一起运行,是不是可以进一步把时间降低到1个月,保证顺利毕业呢?前人又已经搞定了相关的方案,可以让多台机器上的scrapy一起配合完成同一个任务。

Scrapy-Redis

我们把视线回到架构图上,engine/downloader没有什么值得改的地方,spider是我们自己写的,spider middleware可以自定义,pipline是我们自己写的,downloader middleware可以自定义。所以我们把焦点放到scheduler上。
这个scheduler可以接受engine给出的request对象,判重后,放到队列中备用,等待engine获取。如果我们在同一个机器上启动很多个scrapy进程,这很多个进程能够共享一个scheduler,就可以单机做并行了。进一步如果在很多机器上运行的多个scrapy进程,都用一个scheduler来缓存request对象,那么分布式就实现了。

思路明白了之后,我们就可以安装Scrapy-Redis到本地了。
第一步,scrapy-redis 依赖redis,所以需要安装和启动redis;
安装和启动redis
第二步,scrapy-redis 重写了spider,我们只要在定义爬虫时把自建爬虫的父类改成"RedisSpider";
第三步,scrapy-redis启动后,会等待redis库中的start_url,所以需要手工push一条url进redis;
下面是具体攻略:scrapy-redis安装和使用的攻略

Tips:因为python存在GIS,所以根据自己机器CPU的个数多跑几个scrapy进程,可以有加速效果!

7,初遇大数据:用布隆过滤器判重

使用scrapy会遇到时间瓶颈,所以我们用了分布式的scrapy-redis,解决了规模问题,用机器资源换取时间。但是我们的redis是中心化的,虽然request对象来来回回可能可以保持平衡,但是去重队列却会一直增长,那么数据量大了之后,redis一定会爆掉。此时我们的老朋友登场:哈希!
有一种重复散列的高效查重方法叫做bloomfiter,很多人做了实现,但是这个叫崔庆才的小哥做了讲解、实现之后,还做成了安装包。大家可以一行命令安装好,更改settings即可生效,非常方便。
bloomfilter加强的scrapy-redis

BloomFilter

8,登录:向反爬虫工程师亮剑

众所周知,大清已经亡了。21世纪的今天,想通过requests获取网页的text抓取信息,有可能根本看不到信息,或者看到一堆乱码,或者不是乱码,但是被层层加密。

动态网页

最现实的问题是,随着软件技术的发展,静态网页越来越少,动态渲染的东西越来越多。如果我们需要抓取的东西是Ajax动态获取的,我们可以通过浏览器自带的工具进行网络分析,构造请求直接获取数据。
学习分析ajax构造爬虫请求

如果是需要和网页进行进一步的交互,那么仅仅通过ajax请求可能比较复杂,此时需要模拟浏览器来解救你。模拟浏览器就是一个工具库,它向浏览器一样解析渲染网页,又对编程非常友好——你可以用代码实现点击、拖拽等等动作,和网页进行交互。模拟浏览器对验证码破解也有重要意义,因为现在的验证码很多都在分析你的鼠标移动轨迹。
关于模拟浏览器,只给一个关键词,需求很多样,有了入口就不迷茫:selenium

验证码破解

如果没登录,获取到的信息很有可能是这样的:

<option value="/">中国气象新闻网</option>
<option value="">中国防雷信息网</option>
<option value="/">世界气象中心(北京)</option>
<option value="/">国家减灾网</option>

很感人的画面,催人泪下。但是也很容易理解,人家做一个网站要买服务器和带宽,自然不愿意被非用户占用了计算和网络资源,大家爬取时,也要注意限速。

通常情况下,除了对被爬取的网站心存感激之外,我们需要把我们的需求做比较好的封装,让拒绝爬虫访问的服务器认为你就是用户,这就需要登录。

登录最大的拦路虎就是验证码,在爬和反爬不断升级的道路上,机器学习也参与进来,鉴别爬虫流量,也鉴别验证码字符。

验证码破解的第一招,就是不要碰到验证码。
1,找防护力量比较薄弱的站点,比如爬微博,爬移动站可能就比PC站点容易一些,也许都用不到Cookie;
2,如果需要登录,多换马甲,不要让人家看出来你是在用多个账户登录获取Cookie。
破解验证码的第二招,就是见招拆招。此处不设讲解,不给链接,因为世界变化太快。

进阶网页加解密

就算登录,网站依然可以有方法让你获取不到信息,这就是网页加密。如果你喜爱前端技术和福尔摩斯,你就可以成为此间大神。
Tips:此为进阶方向,可顺势成为前端工程师。

9,爬虫工程师必备百宝箱

前面已经学会如何在request中变换UA,添加、更改Cookie和代理了,那么如何能够拥有UA、Cookie、代理的强大后援呢?

User-agent池

Cookie池


代理池

崔庆才

在爬虫领域非常活跃,知识全面,讲解清晰。但因为提供的内容太全,容易让新人眼花缭乱,所以不适合快速入门,可进阶使用。
博客

站在爬虫的山顶上:进阶指南

我们在文中已经提到了可能的进阶方向,当然事实上大家很可能跟我一样本来是那几个方向的人,只是偶尔客串一下,养个虫虫。

爬虫专家

养虫养成专家,例如崔庆才;

数据科学家

分析爬取来的数据,形成决策和报告;

机器学习专家

机器学习分析爬虫流量,NLP分析爬取字段,CV分析验证码;

前端工程师

网页加解密;

本文发布于:2024-02-05 00:18:25,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170719690261157.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:爬虫   一篇文章   就够了
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23