用户
搜索
  • TA的每日心情
    擦汗
    3 天前
  • 签到天数: 258 天

    连续签到: 1 天

    [LV.8]以坛为家I

    版主

    我是表弟

    Rank: 7Rank: 7Rank: 7

    44

    主题

    330

    帖子

    386

    魔法币
    收听
    0
    粉丝
    14
    注册时间
    2015-11-20

    秦突出贡献春秋游侠核心白帽i春秋签约作者楚燕魏齐赵积极活跃奖春秋文阁春秋达人

    0nise 版主 我是表弟 秦 突出贡献 春秋游侠 核心白帽 i春秋签约作者 楚 燕 魏 楼主
    发表于 2018-2-4 11:43:11 236907
    本帖最后由 0nise 于 2018-2-4 05:01 编辑
    本文作者:0nise,本文属i春秋原创奖励计划,未经许可禁止转载

    前言
    最近看到群里的大佬们找文章找的比较麻烦,所以就想出了机器人的方案,在QQ群进行艾特机器人进行查找文章或者作者,小编不才正好会一门编程语言python,正好给各位大佬们做端茶送水的工作。
    目录
    1、环境安装
    2、分析目标
    3、爬虫开发
    4、机器人开发

    环境安装
      语言:python 2.7.13
      数据库:MySQL 5.5
      操作系统:Deepin Linux 15.5
      第三方类库:requests、re、sys、MySQLDB、DBUtils、qqbot
      小编在这里就不阐述如何安装python2.7、以及数据库。
    第三方类库安装命令
    [Python] 纯文本查看 复制代码
    sudo pip install requests
    sudo pip install MySQL-python
    sudo pip install DBUtilssudo pip install qqbot
    
    分析目标
    1、爬虫机制

    在爬虫网站之前,首先我会分析该网站,的防火墙是否有反爬虫机制,以及爬虫方案。
    判断一个网站是否有反爬虫机制,最简单的方法就是直接访问,不断的访问,知道出现ip被封等等情况。
    [Python] 纯文本查看 复制代码
    #!/usr/bin/python
    #-*- coding: UTF-8 -*-
    #coding=utf-8
    import sys
    import requests
    while True:
        url = 'https://bbs.ichunqiu.com/forum.php'
        rsp = requests.get(url)
        print url
        print 'code ----------',rsp.status_code

    深度截图_deepin-terminal_20180204002351.png
    如果在2分钟之内没有出现ip被封等情况,就可以判定该网站没有反爬虫机制。
    2、爬虫方案
    1)、确定所需数据
    在爬虫之前首先我们需要确定我们哪些数据。
    例如:文章链接,文章发布时间,文章作者
    2)、爬虫方案
    我做方案之前,我们先把我们想要的字段做一个归类。
    大概分为俩类,第一类为已知字段,第二类为未知字段。
    已知字段例如:文章链接
    为止字段例如:文章标题、文章作者、文章发布时间
    对于未知字段是我们需要考虑该如何提取(在专业方面这个工作叫做"数据清理")
    文章标题提取方案:
        在浏览器中一个文章的标题只有一个,这个可以可以根据浏览器显示的title来进行提取
    文章作者提取方案:
       哪一遍文章做例子
      https://bbs.ichunqiu.com/thread-33658-1-1.html
      在该文章中,作者为“阿普哥哥”,我们看到该页面存在有”阿普哥哥“这个ID,当鼠标移动到头像上面的时候出现蓝色的字体,比较明显。
       深度截图_选择区域_20180204003547.png
      审查元素发现该文本出现在"<strong>"标签下的"<a>"子标签,该子标签的"class='xi2'"
    深度截图_选择区域_20180204003718.png
    这样的话我们就可以先找到"<strong>"标签,然后在找"<a>"子标签并且"class='xi2'"的标签,提取里面文本就是ID名称
    文章发布时间提取:
       发布时间,出现俩种情况,第一种情况为非日期情况。
        深度截图_选择区域_20180204004201.png
    第二种情况为正常情况
    深度截图_选择区域_20180204004256.png
      这样的话就很麻烦了,如果采集的时间为"昨天,3天前",这种情况就会出现该该文章日期错误,无法正常判定准确时间。
      通过分析发现,如果存在非标准日期,会出现"<span>"标签,"title"书属性为日期信息。
       深度截图_选择区域_20180204004653.png
    爬虫开发
    1、构建请求中心
    请求中心的作用为,统一发出http请求,统一处理错误信息,配置代理池等等作用。
    [Python] 纯文本查看 复制代码
    '''
    USER_AGENTS 随机头信息
    '''
    USER_AGENTS = [
        "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
        "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)",
        "Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.35; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
        "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)",
        "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)",
        "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)",
        "Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)",
        "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)",
        "Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6",
        "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1",
        "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0",
        "Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5",
        "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Fedora/1.9.0.8-1.fc10 Kazehakase/0.5.6",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.20 (KHTML, like Gecko) Chrome/19.0.1036.7 Safari/535.20",
        "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; fr) Presto/2.9.168 Version/11.52",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.11 TaoBrowser/2.0 Safari/536.11",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER",
        "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; LBBROWSER)",
        "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E; LBBROWSER)",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.84 Safari/535.11 LBBROWSER",
        "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)",
        "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; QQBrowser/7.0.3698.400)",
        "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)",
        "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SV1; QQDownload 732; .NET4.0C; .NET4.0E; 360SE)",
        "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)",
        "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)",
        "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1",
        "Mozilla/5.0 (iPad; U; CPU OS 4_2_1 like Mac OS X; zh-cn) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5",
        "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0b13pre) Gecko/20110307 Firefox/4.0b13pre",
        "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:16.0) Gecko/20100101 Firefox/16.0",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11",
        "Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10"
    ]
    # 请求超时时间
    REQUEST_TIME_OUT = 20
    MAX_THREAD = 20
    """
    reuqst请求发送
    :param url: 需要请求的url
    """
    def request_url(url='',method='',data=''):
        HEADER = {
            'User-Agent': random.choice(USER_AGENTS),
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
            'Accept-Language': 'en-US,en;q=0.5',
            'Accept-Encoding': 'gzip, deflate',
            'Connection':'keep-alive',
        }
        #返回内容
        result_text = '请求错误'
        break_flag = True
        break_count = 0
        while break_flag:
            try:
                if 'get' in method:
                    rs = requests.get(url, data=data, headers=HEADER, timeout=REQUEST_TIME_OUT,verify=False)
                    print url
                    print 'code ----------',rs.status_code
                    if rs.status_code == 200:
                        result_text = rs.text
                    else:
                        while True:
                            print 'url ',url
                            print 'start sleep ',60
                            time.sleep(60)
                            rs = requests.get(url, data=data, headers=HEADER, timeout=REQUEST_TIME_OUT,verify=False)
                            if rs.status_code == 200:
                                result_text = rs.text
                                break
                elif 'post' in method:
                    rs = requests.get(url, data=data, headers=HEADER, timeout=REQUEST_TIME_OUT,verify=False)
                    if rs.status_code == 200:
                        result_text = rs.text
                break_flag = False
            except Exception, e:
                print e
                break_count = break_count + 1
                break_flag = False
            if break_count > 10:
                break
        
        return result_text

    在请求过程可能会出现,网站崩溃、ip被封等等情况造成的数据丢失。所以我们要统一处理请求,如果请求发生错误,就再次请求知道成功为止(具体按照网站来确定,必要的时候线程休眠一段时间进行访问)。
    2、数据爬虫,拥有了统一请求中心,剩下的就是如何提取请求成功返回过来的内容,以及数据入库操作。
    [Python] 纯文本查看 复制代码
    """
    ichunqiu爬虫
    @param url 爬虫url
    """
    def ichunqiu_sipder(url):
        content = request_url(url,'get','')
        soup = BeautifulSoup(content,'lxml')
        content_id = url.replace('https://bbs.ichunqiu.com/thread-','').replace('-1-1.html','')
        title = soup.title.string
        if '提示信息' == title:
            return
        author = ''
        content_date = ''
        for string_tag in soup.find_all('strong'):
            if string_tag.find('a',class_='xi2'):
                author = string_tag.find('a',class_='xi2').string
                break
        for date_div in soup.find_all('div',class_='cl',attrs={'style':'font-size: 12px; color: #888888;'}):
            tmp_str = str(date_div)
            date_arr =  re.findall('\d{4}-\d{1,2}-\d{1,2} \d{1,2}:\d{1,2}:\d{1,2}',tmp_str)
            if len(date_arr) > 0:
                content_date = date_arr[0]
                break
        content_id = MySQLdb.escape_string(content_id)
        title = MySQLdb.escape_string(title)
        author = MySQLdb.escape_string(author)
        content_date = MySQLdb.escape_string(content_date)
        connection = pool.connection()
        cursor = connection.cursor()
        sql = "INSERT IGNORE INTO ichunqiu_content(id,content_id,title,url,author,content_date,create_date,update_date) VALUES (DEFAULT,'"+content_id+"','"+title+"','"+url+"','"+author+"','"+content_date+"',NOW(),NOW())"
        #print sql
        cursor.execute(sql)
        connection.commit()
        cursor.close()
        connection.close()
        print 'content_id -->',content_id,' ok'

    3、数据采集。我们需要确定从哪一篇文章开始,哪一篇文章结束。通过访问分析网站发现文章链接为 "thread-1234-1-1.html" ,中的1234为自动增长的,其中20为最早的第一篇文章,最新发表的文章为33742。
    [Python] 纯文本查看 复制代码
    if __name__ == '__main__':
        main_pool = ThreadPool(MAX_THREAD)
        for i in range(20,33742+1):
        url = 'https://bbs.ichunqiu.com/thread-'+str(i)+'-1-1.html'
        main_pool.run(ichunqiu_sipder,(url,), callback=None)
    
        main_pool.close()
    
    4、线程池配置,为提升爬虫效率,我们使用线程池来进行处理。
        [mw_shl_code=python,true]import sys
    if sys.version > '3':
        import queue
    else:
        import Queue as queue
    import threading
    import contextlib
    import time
    
    StopEvent = object()  # 终止线程信号
    
    class ThreadPool(object):
        """
        1、解决线程重用问题,当前线程执行完任务后,不杀掉,放到空闲线程列表,继续执行下个任务
        2、根据任务量开启线程,如果设置10个线程,只有2个任务,最多只会开启两个线程
        3、如果有500个任务,任务执行非常快,2个线程就能完成,如果设置开启10个线程,
            只会开启两个线程
        """
    
        def __init__(self, max_num, max_task_num = None):
            if max_task_num:
                self.q = queue.Queue(max_task_num)  # 指定任务最大数,默认为None,不限定
            else:
                self.q = queue.Queue()
            self.max_num = max_num  # 最多多少线程
            self.cancel = False  # 执行完所有任务,终止线程信号
            self.terminal = False  # 无论执行完毕与否,都终止所有线程
            self.generate_list = []  # 已创建多少线程
            self.free_list = []  # 空闲多少线程
    
        def run(self, func, args, callback=None):
            """
            线程池执行一个任务
            :param func: 任务函数
            :param args: 任务函数所需参数
            :param callback: 任务执行失败或成功后执行的回调函数,回调函数有两个参数1、任务函数执行状态;2、任务函数返回值
            :return: 如果线程池已经终止,则返回True否则None
            """
            if self.cancel:
                return
            # 没有空闲线程 并且已创建线程小于最大线程数才创建线程,
            if len(self.free_list) == 0 and len(self.generate_list) < self.max_num:
                self.generate_thread()  # 满足则创建线程,并将任务放进队列
            w = (func, args, callback,)
            # 函数,元组,函数 ,将这三个参数放在元组里面,当成一个整体放到队列里面
            self.q.put(w)  # 满足条件则创建线程,并把任务放队列里面
    
    
        def generate_thread(self):
            """
            创建一个线程
            """
            t = threading.Thread(target=self.call)  # 每一个线程被创建,执行call方法
            t.start()
    
        def call(self):
            """
            循环去获取任务函数并执行任务函数
            """
            current_thread = threading.currentThread()
            self.generate_list.append(current_thread)  # 每创建一个线程,将当前线程名加进已创建的线程列表
    
            event = self.q.get()  # 在队列中取任务, 没任务线程就阻塞,等待取到任务,线程继续向下执行
            while event != StopEvent:  # 是否满足终止线程
    
                func, arguments, callback = event  # 取出队列中一个任务
                try:
                    result = func(*arguments)  # 执行函数,并将参数传进去
                    success = True
                except Exception as e:
                    success = False
                    result = None
    
                if callback is not None:
                    try:
                        callback(success, result)
                    except Exception as e:
                        pass
    
                with self.worker_state(self.free_list, current_thread):  # 当前线程执行完任务,将当前线程置于空闲状态,
                    #这个线程等待队列中下一个任务到来,如果没来,一直处于空闲, 如果到来,去任务
                    if self.terminal:
                        event = StopEvent
                    else:
                        event = self.q.get()   # 将当前任务加入到空闲列表后,如果有任务,取到,没有阻塞 取到后,移除当前线程
            else: # 满足终止线程,在创建的线程列表中移除当前线程
                self.generate_list.remove(current_thread)
    
        def close(self):
            """
            执行完所有的任务后,杀掉所有线程
            """
            self.cancel = True   # 标志设置为True
            full_size = len(self.generate_list) + 1  # 已生成线程个数, +1 针对python2.7
            while full_size:
                self.q.put(StopEvent)  #
                full_size -= 1
    
        def terminate(self):
            """
            无论是否还有任务,终止线程
            """
            self.terminal = True
    
            while self.generate_list:
                self.q.put(StopEvent)
    
            self.q.queue.clear()
    
        @contextlib.contextmanager
        def worker_state(self, state_list, worker_thread):
            """
            用于记录线程中正在等待的线程数
            """
            state_list.append(worker_thread)  # 将当前空闲线程加入空闲列表
            try:
                yield
            finally:
                state_list.remove(worker_thread)  # 取到任务后,将当前空闲线程从空闲线程里移除,
    机器人开发
        小编使用是qqbot,进行与QQ群的各位大佬进行交流的。

       github地址为: https://github.com/pandolia/qqbot
       1、qqBot体验:
       安装完成qqbot之后,执行

      
    [AppleScript] 纯文本查看 复制代码
    qqbot

        深度截图_选择区域_20180204010623.png
      扫描二维码登录,就可以监控到QQ的聊天记录,
    深度截图_deepin-terminal_20180204010931.png
       2、插件开发
    QQBot 收到群消息时,会先根据消息内容判断是否有人 @ 自己。如果是,则在消息内容的开头加一个 [@ME] 的标记,再传递给 onQQMessage 函数;否则,将消息内容中的所有 @ME 替换成 @Me 再传给 onQQMessage 。因此,在 onQQMessage 函数内,只需要判断 content 内是否含有 @ME 就知道自己是否被消息发送者 @ 了。

    [Python] 纯文本查看 复制代码
    #!/usr/bin/python
    #-*- coding: UTF-8 -*-
    #coding=utf-8
    import sys
    import MySQLdb
    from DBUtils.PooledDB import PooledDB
    pool = PooledDB(MySQLdb,20,host='127.0.0.1',user='root',passwd='root',db='ichunqiu',port=3306,charset='utf8')
    def onQQMessage(bot, contact, member, content):
        if '@ME' in content:
            content = str(content).replace('[@ME]  ','')
            if '[author]' in content:
                content = content.replace('[author] ','')
                content = MySQLdb.escape_string(content)
                content = "%"+content+"%"
                connection = pool.connection()
                cursor = connection.cursor()
                cursor.execute("SELECT title,url,author,content_date FROM ichunqiu_content WHERE author LIKE %s ORDER BY content_date DESC LIMIT 0,3",[content])
                all_data = cursor.fetchall()
                if len(all_data) == 0:
                    bot.SendTo(contact,'无数据')
                else:
                    send_str = ''
                    for data_tup in all_data:
                        title = data_tup[0]
                        url = data_tup[1]
                        author = data_tup[2]
                        content_date = data_tup[3]
                        tmp_str = ''
                        tmp_str = title + '\n' + url + '\n'
                        send_str = send_str +'\n'+tmp_str
                    bot.SendTo(contact,send_str)
            elif '[content]' in content:
                content = content.replace('[content] ','')
                if len(content) > 2:
                    content = "%"+content+"%"
                    content = MySQLdb.escape_string(content)
                    connection = pool.connection()
                    cursor = connection.cursor()
                    cursor.execute( "SELECT title,url,author,content_date FROM ichunqiu_content WHERE title LIKE %s ORDER BY content_date DESC  LIMIT 0,3",[content])
                    all_data = cursor.fetchall()
                    if len(all_data) == 0:
                        bot.SendTo(contact,'无数据')
                    else:
                        send_str = ''
                        for data_tup in all_data:
                            title = data_tup[0]
                            url = data_tup[1]
                            author = data_tup[2]
                            content_date = data_tup[3]
                            tmp_str = ''
                            tmp_str = title + '\n' + url + '\n'
                            send_str = send_str +'\n'+tmp_str
                        bot.SendTo(contact,send_str)

    按照官方提供的Demo,修改修改实现自己的需求,插件写好之后需要将插件拷贝到qqbot-tmp目录下,
    注:
    配置文件为 ~/.qqbot-tmp/v2.x.conf ( ~ 代表用户主目录, win7 下为 C:\Users\xxx , linux 下为 /home/xxx )

    拷贝到该目录下之后,执行
    [AppleScript] 纯文本查看 复制代码
    qq plug sendQQ


    注意: qq plug 脚本名称
    5、体验
    深度截图_选择区域_20180204011751.png
    深度截图_选择区域_20180204011726.png
    代码
    github:https://github.com/0nise/ichunqiu_qqbot
    求star

    BUG更新
    2018-02-04 处理机器人回复是出现的sql注入漏洞

    发表于 2018-2-6 18:34:13
    loveyou 发表于 2018-2-6 09:47
    我没看懂 这个要在那里运行。。。

    运行是单独运行的,需要先运行qqBot扫码登录,然后通过qq plug 插件名称 加载插件
    使用道具 举报 回复
    原来那个谁就是这样做的啊   得学习了  感谢楼主  感谢春秋  求楼主QQ  
    使用道具 举报 回复
    滴,您的爬取方法已被爱春秋记录并且增加了爬虫拦截机制
    使用道具 举报 回复
    121b2e37a1747a0af7457f94480c2f83[/img]
    使用道具 举报 回复
    666666666666666666666666666666666
    使用道具 举报 回复
    发表于 2018-2-6 14:12:57
    good,great,awesome,excellent!
    使用道具 举报 回复
    可以 看文章很方便了
    使用道具 举报 回复
    发表于 2018-2-4 13:28:03
    前排支持我大表哥
    一位特爱收藏Supreme的大哥哥....
    使用道具 举报 回复
    感谢分享!
    使用道具 举报 回复
    发表于 2018-2-5 01:27:15
    6666,很好很强大
    使用道具 举报 回复
    发表于 2018-2-5 10:35:36
    给大佬递茶……NB
    求知若饥,虚心若愚。
    使用道具 举报 回复
    发表于 2018-2-5 13:27:01
    感谢分享
    使用道具 举报 回复

    感谢分享
    使用道具 举报 回复
    发表于 2018-2-5 14:31:33
    666,打call
    使用道具 举报 回复
    感谢分享
    使用道具 举报 回复
    发表于 2018-2-5 16:08:22
    这个值得学习
    个人博客:http://www.cnblogs.com/anbus/
    使用道具 举报 回复
    发表于 2018-2-6 09:26:49
    厉害厉害。。。先mark了。
    使用道具 举报 回复
    12下一页
    发新帖
    您需要登录后才可以回帖 登录 | 立即注册