从零开始构建简易AI问答系统

一、基本思路

我的想法是一个可以能够自我学习会计知识并可以问答的程序,但是我没有那么多时间去整理这些相关的资料,于是让他直接获取百度百科的数据。基本思路如下:

1、爬取百度百科相关词条的网页内容,可以使用Python中的爬虫框架,例如Scrapy或BeautifulSoup等。

2、对网页内容进行自然语言处理和数据清洗,将有用的信息提取出来,并存储在数据库或本地文件中。

3、使用机器学习算法,如神经网络、随机森林等对得到的数据进行训练。

4、开发一个问答系统,将用户问题输入进去,然后将输入的问题与训练好的模型匹配,以便回答用户问题。

当然,这个项目具体实现上还有很多需要考虑的细节,比如如何避免数据爬取被反爬虫,如何处理查询不到的问题等等,不过这个只是一个简易的AI就不考虑那么多,

二、准备工具

  1. centos服务器一台

  1. 源码

三、代码部分

根据思路写出基本的代码,然后在根据思路进行调整

# 导入需要用到的库
import requests
from bs4 import BeautifulSoup
import jieba
from gensim.models.word2vec import Word2Vec
import numpy as np
import pandas as pd
from sklearn.svm import SVC
from sklearn.metrics.pairwise import cosine_similarity
 
# 爬虫部分:爬取百度百科的网页内容,并进行数据清洗和预处理
def get_wiki_content(keyword):
    url = 'https://baike.baidu.com/item/' + keyword
    response = requests.get(url)
    soup = BeautifulSoup(response.text, 'html.parser')
    content = soup.find_all(class_="para")
    content_list = []
    for para in content:
        para = para.get_text().strip()
        if para:
            content_list.append(para)
    full_content = ''.join(content_list)
    return full_content
 
# 分词和向量化部分:使用jieba对爬取的数据进行分词,然后使用Word2Vec模型将分词结果转化为词向量
class Word2VecVectorizer:
    def __init__(self):
        self.word2vec = None
 
    def fit(self, sentences):
        w2v = Word2Vec(sentences=sentences, size=256, min_count=5, workers=4)
        self.word2vec = dict(zip(w2v.wv.index2word, w2v.wv.vectors))
 
    def transform(self, sentences):
        vectors = []
        for sentence in sentences:
            sentence_vec = []
            for word in sentence:
                if word in self.word2vec:
                    sentence_vec.append(self.word2vec[word])
            if len(sentence_vec) > 0:
                sentence_vec = np.mean(sentence_vec, axis=0)
            else:
                sentence_vec = np.zeros(256)
            vectors.append(sentence_vec)
        return np.array(vectors)
 
# 机器学习部分:使用SVM算法对文本向量训练模型,并使用余弦相似度计算输入问题与已知问题的相似度
class QuestionAnswerSystem:
    def __init__(self):
        self.vectorizer = None
        self.clf = None
        self.question_vectors = None
        self.answer_vectors = None
        self.df = None
 
    def fit(self, questions, answers):
        self.vectorizer = Word2VecVectorizer()
        self.vectorizer.fit(questions)
        self.question_vectors = self.vectorizer.transform(questions)
        self.answer_vectors = self.vectorizer.transform(answers)
        self.clf = SVC(kernel='linear', probability=True)
        self.clf.fit(self.question_vectors, range(len(questions)))
 
    def ask(self, question, n=5):
        question = list(jieba.cut(question))
        question_vec = self.vectorizer.transform([question])[0]
        probas = self.clf.predict_proba([question_vec])[0]
        top_n = np.argsort(probas)[-n:]
        sims = cosine_similarity([question_vec], self.question_vectors[top_n])[0]
        best_match = np.argmax(sims)
        return self.df.iloc[top_n[best_match]]['answer']
 
# 主程序部分:调用以上函数完成问答系统的搭建
if __name__ == '__main__':
    # 爬取需要用到的数据
    wiki_content = get_wiki_content('会计')
    # 对数据进行预处理
    questions = ['什么是会计', '什么是账户', '什么是分录', '什么是财务报表']
    answers = ['会计是什么', '账户是什么', '分录是什么', '财务报表是什么']
    # 训练问答系统
    qas = QuestionAnswerSystem()
    qas.fit(questions, answers)
    qas.df = pd.DataFrame({'question': questions, 'answer': answers})
    # 进行测试
    while True:
        question = input('请输入你的问题:')
        if question == 'bye':
            break
        answer = qas.ask(question)
        print('答案为:', answer)

加入神经网络模型

我们可以发现他只能回答一些基础的问题。然后我们在此基础上加入神经网络模型进一步完善它。于是我使用预训练的语言模型(如BERT)来对问题和答案进行编码,然后将编码后的结果输入到一个神经网络中进行分类。代码如下,其中使用Hugging Face库来加载BERT模型。

#  导入需要用到的库
import requests
from bs4 import BeautifulSoup
import jieba
import numpy as np
import pandas as pd
from sklearn.metrics.pairwise import cosine_similarity
import torch
from transformers import BertTokenizer, BertModel
from torch.utils.data import TensorDataset, DataLoader
import torch.nn.functional as F
 
# 爬虫部分:爬取百度百科的网页内容,并进行数据清洗和预处理
def get_wiki_content(keyword):
    url = 'https://baike.baidu.com/item/' + keyword
    response = requests.get(url)
    soup = BeautifulSoup(response.text, 'html.parser')
    content = soup.find_all(class_="para")
    content_list = []
    for para in content:
        para = para.get_text().strip()
        if para:
            content_list.append(para)
    full_content = ''.join(content_list)
    return full_content
 
# 编码器部分:使用预训练的BERT模型来对文本进行编码
class BertEncoder:
    def __init__(self):
        self.tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
        self.model = BertModel.from_pretrained('bert-base-chinese')
        self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
 
    def encode(self, sentence):
        input_ids = torch.tensor([self.tokenizer.encode(sentence, add_special_tokens=True)], dtype=torch.long).to(self.device)
        with torch.no_grad():
            outputs = self.model(input_ids)
            encoded = outputs[0][:, 0, :].cpu().numpy()
        return encoded
 
# 神经网络部分:使用全连接层对文本向量训练模型,并使用余弦相似度计算输入问题与已知问题的相似度
class QuestionAnswerSystem:
    def __init__(self):
        self.encoder = None
        self.clf = None
        self.question_vectors = None
        self.answer_vectors = None
        self.df = None
 
    def fit(self, questions, answers):
        self.encoder = BertEncoder()
        self.question_vectors = self.encoder.encode(questions)
        self.answer_vectors = self.encoder.encode(answers)
        self.clf = torch.nn.Sequential(torch.nn.Linear(768, 256),
                                        torch.nn.ReLU(),
                                        torch.nn.Linear(256, len(questions)),
                                        torch.nn.Softmax())
        self.clf.to(self.encoder.device)
        train_data = TensorDataset(torch.tensor(self.answer_vectors, dtype=torch.float32), torch.tensor(range(len(questions)), dtype=torch.long))
        train_loader = DataLoader(train_data, batch_size=32)
        criterion = torch.nn.CrossEntropyLoss()
        optimizer = torch.optim.Adam(self.clf.parameters(), lr=1e-3)
        num_epochs = 5
        for epoch in range(num_epochs):
            for inputs, labels in train_loader:
                inputs, labels = inputs.to(self.encoder.device), labels.to(self.encoder.device)
                outputs = self.clf(inputs)
                loss = criterion(outputs, labels)
                optimizer.zero_grad()
                loss.backward()
                optimizer.step()
 
    def ask(self, question, n=5):
        question_vec = self.encoder.encode(question)
        inputs = torch.tensor(question_vec, dtype=torch.float32).to(self.encoder.device)
        outputs = self.clf(inputs)
        probas = F.softmax(outputs, dim=1).detach().cpu().numpy()[0]
        top_n = np.argsort(probas)[-n:]
        sims = cosine_similarity([question_vec], self.question_vectors[top_n])[0]
        best_match = np.argmax(sims)
        return self.df.iloc[top_n[best_match]]['answer']
 
# 主程序部分:调用以上函数完成问答系统的搭建
if __name__ == '__main__':
    # 爬取需要用到的数据
    wiki_content = get_wiki_content('会计')
    # 对数据进行预处理
    questions = ['什么是会计', '什么是账户', '什么是分录', '什么是财务报表']
    answers = ['会计是什么', '账户是什么', '分录是什么', '财务报表是什么']
    # 训练问答系统
    qas = QuestionAnswerSystem()
    qas.fit(questions, answers)
    qas.df = pd.DataFrame({'question': questions, 'answer': answers})
    # 进行测试
    while True:
        question = input('请输入你的问题:')
        if question == 'bye':
            break
        answer = qas.ask(question)
        print('答案为:', answer)

我们利用BERT模型来对问题和答案进行编码,然后使用全连接层对编码后的文本向量进行分类。这样的神经网络效果较好,可以更准确地回答问题。但是,由于BERT模型比较复杂,因此训练和推断速度都比较慢,需要耐心等待。

添加将训练结果保存到excel文件中的功能

为了实现将训练结果保存到Excel文件中的功能,我们可以使用pandas库。在下面的代码中,我们对问答系统类(QuestionAnswerSystem)进行一些改进,添加了一个新的方法 save_result_to_excel。这个方法接受一个Excel文件名作为参数,并将问题和答案保存到该文件中。在方法中,我们将DataFrame类型的数据转换为Excel文件,并将其保存在指定的文件名中。下面是修改后的代码

# 导入需要用到的库
import requests
from bs4 import BeautifulSoup
import jieba
import numpy as np
import pandas as pd
from sklearn.metrics.pairwise import cosine_similarity
import torch
from transformers import BertTokenizer, BertModel
from torch.utils.data import TensorDataset, DataLoader
import torch.nn.functional as F
 
# 爬虫部分:爬取百度百科的网页内容,并进行数据清洗和预处理
def get_wiki_content(keyword):
    url = 'https://baike.baidu.com/item/' + keyword
    response = requests.get(url)
    soup = BeautifulSoup(response.text, 'html.parser')
    content = soup.find_all(class_="para")
    content_list = []
    for para in content:
        para = para.get_text().strip()
        if para:
            content_list.append(para)
    full_content = ''.join(content_list)
    return full_content
 
# 编码器部分:使用预训练的BERT模型来对文本进行编码
class BertEncoder:
    def __init__(self):
        self.tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
        self.model = BertModel.from_pretrained('bert-base-chinese')
        self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
 
    def encode(self, sentence):
        input_ids = torch.tensor([self.tokenizer.encode(sentence, add_special_tokens=True)], dtype=torch.long).to(self.device)
        with torch.no_grad():
            outputs = self.model(input_ids)
            encoded = outputs[0][:, 0, :].cpu().numpy()
        return encoded
 
# 神经网络部分:使用全连接层对文本向量训练模型,并使用余弦相似度计算输入问题与已知问题的相似度
class QuestionAnswerSystem:
    def __init__(self):
        self.encoder = None
        self.clf = None
        self.question_vectors = None
        self.answer_vectors = None
        self.df = None
 
    def fit(self, questions, answers):
        self.encoder = BertEncoder()
        self.question_vectors = self.encoder.encode(questions)
        self.answer_vectors = self.encoder.encode(answers)
        self.clf = torch.nn.Sequential(torch.nn.Linear(768, 256),
                                        torch.nn.ReLU(),
                                        torch.nn.Linear(256, len(questions)),
                                        torch.nn.Softmax())
        self.clf.to(self.encoder.device)
        train_data = TensorDataset(torch.tensor(self.answer_vectors, dtype=torch.float32), torch.tensor(range(len(questions)), dtype=torch.long))
        train_loader = DataLoader(train_data, batch_size=32)
        criterion = torch.nn.CrossEntropyLoss()
        optimizer = torch.optim.Adam(self.clf.parameters(), lr=1e-3)
        num_epochs = 5
        for epoch in range(num_epochs):
            for inputs, labels in train_loader:
                inputs, labels = inputs.to(self.encoder.device), labels.to(self.encoder.device)
                outputs = self.clf(inputs)
                loss = criterion(outputs, labels)
                optimizer.zero_grad()
                loss.backward()
                optimizer.step()
 
    def ask(self, question, n=5):
        question_vec = self.encoder.encode(question)
        inputs = torch.tensor(question_vec, dtype=torch.float32).to(self.encoder.device)
        outputs = self.clf(inputs)
        probas = F.softmax(outputs, dim=1).detach().cpu().numpy()[0]
        top_n = np.argsort(probas)[-n:]
        sims = cosine_similarity([question_vec], self.question_vectors[top_n])[0]
        best_match = np.argmax(sims)
        return self.df.iloc[top_n[best_match]]['answer']
 
    # 新增方法:将问答结果保存到Excel文件中
    def save_result_to_excel(self, filename='qa_result.xlsx'):
        df_result = self.df.copy()
        df_result.columns = ['Question', 'Answer']
        df_result.to_excel(filename, index=False)
 
# 主程序部分:调用以上函数完成问答系统的搭建
if __name__ == '__main__':
    # 爬取需要用到的数据
    wiki_content = get_wiki_content('会计')
    # 对数据进行预处理
    questions = ['什么是会计', '什么是账户', '什么是分录', '什么是财务报表']
    answers = ['会计是什么', '账户是什么', '分录是什么', '财务报表是什么']
    # 训练问答系统
    qas = QuestionAnswerSystem()
    qas.fit(questions, answers)
    qas.df = pd.DataFrame({'question': questions, 'answer': answers})
    # 将结果保存到Excel文件中
    qas.save_result_to_excel('qa_result.xlsx')
    # 进行测试
    while True:
        question = input('请输入你的问题:')
        if question == 'bye':
            break
        answer = qas.ask(question)
        print('答案为:', answer)

在主程序部分中,新增了一行代码 qas.save_result_to_excel('qa_result.xlsx'),将问答结果保存到Excel文件中。可以将文件名设置为自己喜欢的名称,并在文件保存位置执行该代码。执行完毕后,在指定的文件夹中会生成一个名为 qa_result.xlsx 的文件,其中包含了训练结果的问题和答案。

想要更好的学习,我们需要对该程序在对话过程中就对数据进行数据标注和检验

实现问答系统的数据标注和检验功能,我们需要对 QuestionAnswerSystem 进行一些改进。

首先,为了方便用户对答案进行标注,我们需要在 ask 方法中添加一个新参数 is_correct,用于接收用户对答案是否正确的标注。我们还需要将问题、答案和标注组成的列表存储到 df 属性中,以便后续使用。

其次,我们可以新增一个方法 check_accuracy,用于计算问答系统的准确率。该方法会基于已标注数据计算平均准确率,并将准确率打印出来。同时,我们也可以通过 save_result_to_excel 方法保存标注后的结果到Excel文件中。下面是修改后的代码

# 导入需要用到的库
import requests
from bs4 import BeautifulSoup
import jieba
import numpy as np
import pandas as pd
from sklearn.metrics.pairwise import cosine_similarity
import torch
from transformers import BertTokenizer, BertModel
from torch.utils.data import TensorDataset, DataLoader
import torch.nn.functional as F
 
# 爬虫部分:爬取百度百科的网页内容,并进行数据清洗和预处理
def get_wiki_content(keyword):
    url = 'https://baike.baidu.com/item/' + keyword
    response = requests.get(url)
    soup = BeautifulSoup(response.text, 'html.parser')
    content = soup.find_all(class_="para")
    content_list = []
    for para in content:
        para = para.get_text().strip()
        if para:
            content_list.append(para)
    full_content = ''.join(content_list)
    return full_content
 
# 编码器部分:使用预训练的BERT模型来对文本进行编码
class BertEncoder:
    def __init__(self):
        self.tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
        self.model = BertModel.from_pretrained('bert-base-chinese')
        self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
 
    def encode(self, sentence):
        input_ids = torch.tensor([self.tokenizer.encode(sentence, add_special_tokens=True)], dtype=torch.long).to(self.device)
        with torch.no_grad():
            outputs = self.model(input_ids)
            encoded = outputs[0][:, 0, :].cpu().numpy()
        return encoded
 
# 神经网络部分:使用全连接层对文本向量训练模型,并使用余弦相似度计算输入问题与已知问题的相似度
class QuestionAnswerSystem:
    def __init__(self):
        self.encoder = None
        self.clf = None
        self.question_vectors = None
        self.answer_vectors = None
        self.df = None
 
    def fit(self, questions, answers):
        self.encoder = BertEncoder()
        self.question_vectors = self.encoder.encode(questions)
        self.answer_vectors = self.encoder.encode(answers)
        self.clf = torch.nn.Sequential(torch.nn.Linear(768, 256),
                                        torch.nn.ReLU(),
                                        torch.nn.Linear(256, len(questions)),
                                        torch.nn.Softmax())
        self.clf.to(self.encoder.device)
        train_data = TensorDataset(torch.tensor(self.answer_vectors, dtype=torch.float32), torch.tensor(range(len(questions)), dtype=torch.long))
        train_loader = DataLoader(train_data, batch_size=32)
        criterion = torch.nn.CrossEntropyLoss()
        optimizer = torch.optim.Adam(self.clf.parameters(), lr=1e-3)
        num_epochs = 5
        for epoch in range(num_epochs):
            for inputs, labels in train_loader:
                inputs, labels = inputs.to(self.encoder.device), labels.to(self.encoder.device)
                outputs = self.clf(inputs)
                loss = criterion(outputs, labels)
                optimizer.zero_grad()
                loss.backward()
                optimizer.step()
 
    def ask(self, question, n=5, is_correct=None):
        question_vec = self.encoder.encode(question)
        inputs = torch.tensor(question_vec, dtype=torch.float32).to(self.encoder.device)
        outputs = self.clf(inputs)
        probas = F.softmax(outputs, dim=1).detach().cpu().numpy()[0]
        top_n = np.argsort(probas)[-n:]
        sims = cosine_similarity([question_vec], self.question_vectors[top_n])[0]
        best_match = np.argmax(sims)
        if is_correct is not None:
            self.df = self.df.append({'question': question, 'answer': self.df.iloc[top_n[best_match]]['answer'], 'is_correct': bool(is_correct)}, ignore_index=True)
        return self.df.iloc[top_n[best_match]]['answer']
 
    # 新增方法:将问答结果保存到Excel文件中
    def save_result_to_excel(self, filename='qa_result.xlsx'):
        df_result = self.df.copy()
        df_result.columns = ['Question', 'Answer', 'Is Correct']
        df_result.to_excel(filename, index=False)
 
    # 新增方法:检验问答系统的准确率
    def check_accuracy(self):
        accuracy = self.df['is_correct'].mean()
        print('问答系统的准确率为: {:.2f}%'.format(accuracy*100))
 
# 主程序部分:调用以上函数完成问答系统的搭建
if __name__ == '__main__':
    # 爬取需要用到的数据
    wiki_content = get_wiki_content('会计')
    # 对数据进行预处理
    questions = ['什么是会计', '什么是账户', '什么是分录', '什么是财务报表']
    answers = ['会计是什么', '账户是什么', '分录是什么', '财务报表是什么']
    # 训练问答系统
    qas = QuestionAnswerSystem()
    qas.fit(questions, answers)
    qas.df = pd.DataFrame({'question': questions, 'answer': answers})
    # 进行测试
    while True:
        question = input('请输入你的问题:')
        if question == 'bye':
            break
        is_correct = input('答案是否正确?(y/n)')
        if is_correct.lower() == 'y':
            is_correct = True
        elif is_correct.lower() == 'n':
            is_correct = False
        else:
            is_correct = None
        answer = qas.ask(question, is_correct=is_correct)
        print('答案为:', answer['answer'])
    # 检验准确率并保存标注后的数据到Excel文件中
    qas.check_accuracy()
    qas.save_result_to_excel('qa_result.xlsx')

在主程序部分中,我们新增了两个输入提示,以便用户标注答案是否正确,同时也新增了两行代码来调用 check_accuracy 和 save_result_to_excel 方法,以检验问答系统的准确率,并将标注后的结果保存到Excel文件中。现在,当运行程序并回答每个问题时,程序会提示你输入答案是否正确,你可以输入 "y" 或 "n" 来对答案进行标注。程序会将问题、答案和标注组成的记录存储到 df 属性中,并在每次执行 ask 方法时更新 df。执行 check_accuracy 方法时,程序将基于已标注数据计算准确率,并将其打印出来。执行 save_result_to_excel 方法时,程序将标注后的数据写入到Excel文件中以便后续使用。到此为止,我们的程序的一些功能也就基本实现了。当然还存在许多的问题,希望大家可以一起学习,一起进步。

最后的最后

为了更好的让他学习方便,于是最后增加一个适配的Webj界面。要将该程序适配为web界面,需要使用web框架来搭建一个网站,并将问答系统集成到网站中。可以使用Python中的Flask框架。以下是实现步骤:

1、导入Flask和其他需要用到的库:

from flask import Flask, render_template, request
import pandas as pd
from qa_system import QuestionAnswerSystem

其中qa_system.py文件是之前所提到的问答系统代码。

2、创建Flask应用:

app = Flask(__name__)

3、加载问答系统:

qas = QuestionAnswerSystem()
qas.fit(questions, answers)
qas.df = pd.DataFrame({'question': questions, 'answer': answers})

其中,questions和answers是之前已经定义过的问题列表和答案列表。

4、创建路由函数:

@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == 'POST':
        question = request.form.get('question')
        is_correct = request.form.get('is_correct')
        if is_correct.lower() == 'y':
            is_correct = True
        elif is_correct.lower() == 'n':
            is_correct = False
        else:
            is_correct = None
        answer = qas.ask(question, is_correct=is_correct)
        return render_template('index.html', answer=answer['answer'])
    return render_template('index.html')

这个路由函数包含了两个请求方法:GET和POST。当用户访问网页时,会发送一个GET请求,会渲染index.html页面;当用户提交问题时,会发送一个POST请求,会通过问答系统获取答案,并将答案渲染到页面中。question和is_correct是从表单中获取的参数。

5、编写HTML模板:

这里写了一个简单的模板作为参考,可以根据需求进行修改。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>问答系统</title>
</head>
<body>
    <form method="post">
        <input type="text" name="question" placeholder="请输入你的问题" required>
        <input type="radio" name="is_correct" value="y" required>
        <label for="y">正确</label>
        <input type="radio" name="is_correct" value="n" required>
        <label for="n">错误</label>
        <button type="submit">提交</button>
    </form>
    {% if answer %}
    <p>{
    
    { answer }}</p >
    {% endif %}
</body>
</html>

这个模板包含了一个表单,用户可以在这里输入问题。表单中还包含了两个单选框,用户在回答问题后可以选择是否正确。如果问答系统能够成功获取到答案,就会将答案渲染到页面中。

6、运行Flask应用:

if __name__ == '__main__':
    app.run()

7、运行这个应用之后,通过浏览器访问网址http://127.0.0.1:5000/,即可看到刚才编写的页面。最终大功告成,,即可看到刚才编写的页面。最终大功告成,

高防护IP段(TCP_SYN,ACK,RST)展示:

 103.88.32.1

103.88.32.2

103.88.32.3

103.88.32.4

103.88.32.5

103.88.32.6

103.88.32.7

103.88.32.8

103.88.32.9

103.88.32.10

103.88.32.11

103.88.32.12

103.88.32.13

103.88.32.14

103.88.32.15

103.88.32.16

103.88.32.17

103.88.32.18

103.88.32.19

103.88.32.20

103.88.32.21

103.88.32.22

103.88.32.23

103.88.32.24

103.88.32.25

103.88.32.26

103.88.32.27

103.88.32.28

103.88.32.29

103.88.32.30

103.88.32.31

103.88.32.32

103.88.32.33

103.88.32.34

103.88.32.35

103.88.32.36

103.88.32.37

103.88.32.38

103.88.32.39

103.88.32.40

103.88.32.41

103.88.32.42

103.88.32.43

103.88.32.44

103.88.32.45

103.88.32.46

103.88.32.47

103.88.32.48

103.88.32.49

103.88.32.50

103.88.32.51

103.88.32.52

103.88.32.53

103.88.32.54

103.88.32.55

103.88.32.56

103.88.32.57

103.88.32.58

103.88.32.59

103.88.32.60

103.88.32.61

103.88.32.62

103.88.32.63

103.88.32.64

103.88.32.65

103.88.32.66

103.88.32.67

103.88.32.68

103.88.32.69

103.88.32.70

103.88.32.71

103.88.32.72

103.88.32.73

103.88.32.74

103.88.32.75

103.88.32.76

103.88.32.77

103.88.32.78

103.88.32.79

103.88.32.80

103.88.32.81

103.88.32.82

103.88.32.83

103.88.32.84

103.88.32.85

103.88.32.86

103.88.32.87

103.88.32.88

103.88.32.89

103.88.32.90

103.88.32.91

103.88.32.92

103.88.32.93

103.88.32.94

103.88.32.95

103.88.32.96

103.88.32.97

103.88.32.98

103.88.32.99

103.88.32.100

103.88.32.101

103.88.32.102

103.88.32.103

103.88.32.104

103.88.32.105

103.88.32.106

103.88.32.107

103.88.32.108

103.88.32.109

103.88.32.110

103.88.32.111

103.88.32.112

103.88.32.113

103.88.32.114

103.88.32.115

103.88.32.116

103.88.32.117

103.88.32.118

103.88.32.119

103.88.32.120

103.88.32.121

103.88.32.122

103.88.32.123

103.88.32.124

103.88.32.125

103.88.32.126

103.88.32.127

103.88.32.128

103.88.32.129

103.88.32.130

103.88.32.131

103.88.32.132

103.88.32.133

103.88.32.134

103.88.32.135

103.88.32.136

103.88.32.137

103.88.32.138

103.88.32.139

103.88.32.140

103.88.32.141

103.88.32.142

103.88.32.143

103.88.32.144

103.88.32.145

103.88.32.146

103.88.32.147

103.88.32.148

103.88.32.149

103.88.32.150

103.88.32.151

103.88.32.152

103.88.32.153

103.88.32.154

103.88.32.155

103.88.32.156

103.88.32.157

103.88.32.158

103.88.32.159

103.88.32.160

103.88.32.161

103.88.32.162

103.88.32.163

103.88.32.164

103.88.32.165

103.88.32.166

103.88.32.167

103.88.32.168

103.88.32.169

103.88.32.170

103.88.32.171

103.88.32.172

103.88.32.173

103.88.32.174

103.88.32.175

103.88.32.176

103.88.32.177

103.88.32.178

103.88.32.179

103.88.32.180

103.88.32.181

103.88.32.182

103.88.32.183

103.88.32.184

103.88.32.185

103.88.32.186

103.88.32.187

103.88.32.188

103.88.32.189

103.88.32.190

103.88.32.191

103.88.32.192

103.88.32.193

103.88.32.194

103.88.32.195

103.88.32.196

103.88.32.197

103.88.32.198

103.88.32.199

103.88.32.200

103.88.32.201

103.88.32.202

103.88.32.203

103.88.32.204

103.88.32.205

103.88.32.206

103.88.32.207

103.88.32.208

103.88.32.209

103.88.32.210

103.88.32.211

103.88.32.212

103.88.32.213

103.88.32.214

103.88.32.215

103.88.32.216

103.88.32.217

103.88.32.218

103.88.32.219

103.88.32.220

103.88.32.221

103.88.32.222

103.88.32.223

103.88.32.224

103.88.32.225

103.88.32.226

103.88.32.227

103.88.32.228

103.88.32.229

103.88.32.230

103.88.32.231

103.88.32.232

103.88.32.233

103.88.32.234

103.88.32.235

103.88.32.236

103.88.32.237

103.88.32.238

103.88.32.239

103.88.32.240

103.88.32.241

103.88.32.242

103.88.32.243

103.88.32.244

103.88.32.245

103.88.32.246

103.88.32.247

103.88.32.248

103.88.32.249

103.88.32.250

103.88.32.251

103.88.32.252

103.88.32.253

103.88.32.254

103.88.32.255

猜你喜欢

转载自blog.csdn.net/chiwang_andy/article/details/129689454