- 姓名:sunshinewinter
- 完成日期:2021.11
- 本次实验,我完成了全部实验题。
- 《计算机网络课程设计》报告
- 目录
- 项目1 网络爬虫系统实验
- 1、实验目的
- 2、实验原理
- 3、实验思路
- 4、代码框架与分析
- 5、实验代码
- 6、遇到的问题以及解决方法
- 7、运行结果分析与评价
- 项目2 聊天室系统实现
- 1、实验目的
- 2、实验原理
- 3、实验思路
- 4、代码框架与分析
- 5、实验代码
- 6、遇到的问题以及解决方法
- 7、运行结果分析与评价
- 其他备注
1、理解计算机网络协议的基本原理和思想;
2、通过采用C++或Java等语言,实现网络爬虫抓取过程;
3、实现分析HTML语言,抽取文本;
4、实现友好化交互界面。
2、实验原理网络爬虫基本结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rYkI30XX-1652271228776)(E:study计算机网络temp1.png)]
3、实验思路本次实验使用了request和beaytifulSoup俩个库进行html的解析与内容获取。
思路:
1、确定所需爬取小说内容的网站,获取该页面的URL和其网站根目录的URL;
2、根据URL利用request库向服务器发起GET请求,从而获取该HTML网页信息;
3、解析HTML信息,通过审查该网页元素(右键网页—>检查),获取标签信息,利用BeautifulSoup查找标签,获取所需信息:小说名,章节名和章节URL;
4、在本地创建以小说名为名字的文件夹,用于存储获取的章节内容;
5、通过循环语句不断对各个章节内容发起GET请求获取HTML信息,并利用BeautifulSoup查找标签,提取该章节小说内容。创建以该章节名为名字的.txt文件,将提取出来的内容存入该文件。
4、代码框架与分析总体框架:
1、头文件与宏定义 2、get请求目标url,获取html 3、解析html,并查找其中小说名和章节名所在标签 4.根据解析内容利用for循环获取章节url并重复第2-3步,获取该章节内容并存入以该章节为名的txtx文件中。
代码分析:
#python库的引用 #宏定义 # (1)爬取的小说对应网站网址(target)和该网站根目录(index_path)(本次实验以爬取笔趣阁中小说《诡秘之主》为例) # (2)设定爬取的该小说内容的存储路径(save_path) #向target网站服务器发送请求,并根据打印出来的网页编码格式,修改该响应内容的编码格式(此处例子是UTF-8),防止后续分析时出现乱码。 #通过BeautifulSoup解析该html(soup参数记录该结果) #根据网页元素,利用findall()函数查找到标签类型为div,属性为class = listmain的标签,利用list_tag获取该标签列表 #同上述理,获取h1标签为小说名,并根据小说名创建该文件夹(如果存储路径下不存在该文件) #(由于该网站存在隐藏列表的坑,所以分开成为俩个for循环获取) #1、前十章 # 每个循环获取一个标签为‘dd’的内容,获取其中章节名和章节网址; # 访问该网址内容,获取html信息后解析获取正文文本; # 将当前章节写入文件 #2、后续(同前十章思路,仅仅是根据网页内容将list_tag内容修改为对于span标签的查找)5、实验代码
import requests
from bs4 import BeautifulSoup
import os
target = "https://www.biquge7.com/book/636/" #爬取的小说对应网站
index_path='https://www.biquge7.com' #该网站根目录
save_path = 'E:/book' #文本保存路径
req = requests.get(url = target)
req.encoding = 'utf-8'
soup = BeautifulSoup(req.text,"html.parser") #解析html
list_tag = soup.findAll(name="div", attrs={"class" :"listmain"})
story_title = str(soup.h1) #获取小说名称
print('story_title:',story_title[4:8]) #去除小说名中的h1标签内容
dir_path = save_path+'/'+story_title[4:8] #根据小说名创建文件夹
if not os.path.exists(dir_path):
os.path.join(save_path,story_title)
os.mkdir(dir_path)
i = 1
for dd_tag in list_tag[0].dl.find_all('dd'): #循获取前十章章节名称与章节对应的网址
if i < 11:
chapter_name = dd_tag.string #章节名
chapter_url = index_path+dd_tag.a.get('href') #章节网址
print(chapter_url)
chapter_req = requests.get(url = chapter_url) #访问该章节详情网址,爬取正文
chapter_req.encoding = 'utf-8'
chapter_soup = BeautifulSoup(chapter_req.text, "html.parser")
content_tag = chapter_soup.findAll(name="div", attrs={"class" :"Readarea ReadAjax_content"}) #解析出来正文所在的标签
temp = str(content_tag[0].text)
content_text = temp.replace(" ", "n") #获取正文文本
with open(dir_path+'/'+chapter_name+'.txt', 'w') as f: #将当前章节写入文件
f.write(content_text.encode("gbk", 'ignore').decode("gbk", "ignore"))
i = i + 1
#第十章之后做了隐藏操作,通过跳过该隐藏脚本进行读取后续章节
list_tag = soup.findAll(name="span", attrs={"class" :"dd_hide"}) #隐藏章节,读取类似前十章
for dd_tag in list_tag[0].find_all('dd'):
chapter_name = dd_tag.string #章节名
chapter_url = index_path+dd_tag.a.get('href') #章节网址
print(chapter_url)
chapter_req = requests.get(url = chapter_url) #访问该章节详情网址,爬取正文
chapter_req.encoding = 'utf-8'
chapter_soup = BeautifulSoup(chapter_req.text, "html.parser")
content_tag = chapter_soup.findAll(name="div", attrs={"class" :"Readarea ReadAjax_content"}) #解析出来正文所在的标签
content_text = str(content_tag[0].text) #获取正文文本
with open(dir_path+'/'+chapter_name+'.txt', 'w') as f: #将当前章节写入文件
f.write(content_text.encode("gbk", 'ignore').decode("gbk", "ignore"))
6、遇到的问题以及解决方法
1、使用soup.div(id="list")函数获取div标签下内容为空
解决方法:改为使用findAll()函数,获取信息列表并对第一个列表信息提前需要的内容。
2、爬取章节文本内容时,总是不打印后续章节内容(只显示前十章)
解决方法:根据获取的html内容的打印结果,发现在十章后出现了一个’dd’标签内容为:以下为隐藏列表的JS脚本,经过查看html代码,发现下面的章节信息都在span标签下。所以通过分开循环获取后续章节内容。
7、运行结果分析与评价1、运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vKMWZlcT-1652271228777)(E:study计算机网络temp2.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ruSqUcr0-1652271228777)(E:study计算机网络temp3.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l7LtY2LG-1652271228777)(E:study计算机网络temp4.png)]
2、结果分析:
总体来说,从准确率和爬取速度来说,本次实验十分成功。单重for循环使得时间复杂度并不高。唯一缺陷是企图通过replace()函数将空格改为换行,但是不知为何无法实现。
项目2 聊天室系统实现 1、实验目的1、熟悉计算机网络编程,掌握基于Socket编程;
2、实现聊天室的客户端和聊天室服务器段,同时实现用户友好化的交互界面;
3、实现具有一定的并发处理能力。
2、实验原理socket是“open—write/read—close”模式的一种实现。
socket中TCP的三次握手建立:
- 客户端向服务器发送一个SYN J
- 服务器向客户端响应一个SYN K,并对SYN J进行确认ACK J+1
- 客户端再想服务器发一个确认ACK K+1
当客户端调用connect时,触发了连接请求,向服务器发送了SYN J包,此时connect进入阻塞状态;服务器监听到连接请求,即收到SYN J包,调用accept函数接收请求向客户端发送SYN K ,ACK J+1,此时accept进入阻塞状态;客户端收到服务器的SYN K ,ACK J+1之后,connect返回,并对SYN K进行确认;服务器收到ACK K+1时,accept返回,至此三次握手完毕,连接建立。
3、实验思路 本次实验通过Socket通信实现功能,采用客户端/服务器(C/S)架构模式,编写聊天室程序。除此之外,本次实验运用了多线程,可实现并发与多点对多点的聊天以及一定的私聊技术。对于在线用户列表,本次实验进行了json相关数据包的处理。
客户端:注册用户名和IP地址,在登陆后的聊天室中可进行聊天功能,能看到该聊天室在线用户与聊天信息,并,并且可在该聊天室发送信息。
服务端:接收用户信息,以及完成消息的接收与转发功能,支持整个聊天室的运行。
c.py:(客户端)
1、登录界面
设置界面窗口,并与输入的ip地址建立连接,发送用户名信息。
2、聊天室界面
设置界面窗口。接受来自服务器的信息并更新列表和消息界面;将用户想要发送的信息发给服务端。
s.py:(服务端)服务端在接受到数据后,会对其进行一些处理然后发送给客户端,如下图,对于聊天内容,服务端直接发送给客户端,而对于用户列表,便由json.dumps处理后发送。
4、代码框架与分析c.py
#头文件 #宏定义 # IP,PORT,在线用户列表框,列表框开关设置,用户列表 #登录窗口图形界面 #登录设置 # 获取用户名数据 #建立连接 # socket链接,如果存在该用户,发送;否则提示无该用户 #聊天窗口界面 #消息界面 #在线用户列表界面 #发送消息 #接收来自服务器的消息 # 如果新增用户,则重载列表,否则直接打印接收到的聊天内容; #结束
s.py
#头文件 #宏定义 # ip,port,消息队列,用户信息,线程 #onlines 统计当前在线用户 #chatserver类 聊天服务功能 # 初始化 # 接收客户端发来的用户名 # 将地址和数据存入消息列表 # 处理数据后发送给客户端 # 多线程处理 #主函数5、实验代码
c.py
import socket
import tkinter
import tkinter.messagebox
import threading
import json
import tkinter.filedialog
from tkinter.scrolledtext import ScrolledText
IP = ''
PORT = ''
user = ''
listbox1 = '' #在线用户列表框
show = 1 #列表框开关设置
users = [] #在线用户列表
chat = '------ welcome -------' #聊天对象
#登陆窗口图形界面设置
root0 = tkinter.Tk()
root0.geometry("400x247")
root0.title('Meeting & Chat') #标题
root0.resizable(0,0)
one = tkinter.Label(root0, width = 400, height = 247, bg = "Ivory")
one.pack()
IP0 = tkinter.StringVar()
IP0.set('')
USER = tkinter.StringVar()
USER.set('')
labelIP = tkinter.Label(root0, text = 'IP address', bg = "Ivory") #ip地址框
labelIP.place(x = 30, y = 55, width = 100, height = 40)
entryIP = tkinter.Entry(root0, width = 60, textvariable = IP0)
entryIP.place(x = 145, y = 60, width = 200, height = 30)
labelUSER = tkinter.Label(root0,text='username',bg="Ivory") #用户名框
labelUSER.place(x = 30, y = 100, width = 100, height = 40)
entryUSER = tkinter.Entry(root0, width = 60, textvariable = USER)
entryUSER.place(x = 145, y = 105, width = 200, height = 30)
def Login(*args): #登录设置
global IP, PORT, user
IP, PORT = entryIP.get().split(':')
user = entryUSER.get()
if not user: #用户名为空
tkinter.messagebox.showwarning('warning', message='username should not be blank!!!')
else:
root0.destroy()
loginButton = tkinter.Button(root0, text = "Login In", command = Login,bg="Lavender") #登录键
loginButton.place(x = 150, y = 160, width = 110, height = 30)
root0.bind('', Login)
root0.mainloop()
#建立连接
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((IP, int(PORT)))
if user: #存在该用户时
s.send(user.encode()) #发送用户名
else:
s.send('There is no such username!!!'.encode())
user = IP + ':' + PORT
#聊天窗口
root1 = tkinter.Tk()
root1.geometry("660x517")
root1.title('ChattingRoom')
root1.resizable(0,0)
#消息界面
listbox = ScrolledText(root1, bg = 'MintCream')
listbox.place(x = 4, y = 4, width = 640, height = 312)
listbox.tag_config('tag1', foreground='Brown',background = "Lavender")
listbox.insert(tkinter.END, 'Welcome to the chatting Room!Try to talk now ~', 'tag1')
INPUT = tkinter.StringVar()
INPUT.set('')
entryIuput = tkinter.Entry(root1, width = 120, textvariable = INPUT, bg = 'MintCream')
entryIuput.place(x = 4,y = 324, width = 652, height = 152)
#在线用户列表
listbox1 = tkinter.Listbox(root1, bg = 'MintCream')
listbox1.place(x = 520, y = 4, width = 134, height = 312)
def send(*args):
message = entryIuput.get() + '~' + user + '~' + chat
s.send(message.encode())
INPUT.set('')
sendButton = tkinter.Button(root1, text ="SEND",anchor = 'n', command = send, font=('Helvetica', 10), bg = 'LightBlue')
sendButton.place(x = 560, y = 480, width = 55, height = 30)
root1.bind('', send)
#接收来自服务器的消息
def receive():
global uses
while True:
data = s.recv(1024)
data = data.decode()
print(data)
try:
uses = json.loads(data) #重载在线用户列表
listbox1.delete(0, tkinter.END)
listbox1.insert(tkinter.END, "online member")
listbox1.insert(tkinter.END, "------member list-------")
for x in range(len(uses)):
listbox1.insert(tkinter.END, uses[x])
users.append('------member list-------')
except:
data = data.split('~') #聊天内容打印
message = data[0]
userName = data[1]
chatwith = data[2]
message = 'n' + message
if chatwith == '------member list-------': #群聊
if userName == user:
listbox.insert(tkinter.END, message)
else:
listbox.insert(tkinter.END, message)
elif userName == user or chatwith == user: #私聊
if userName == user:
listbox.tag_config('tag2', foreground ='Orange')
listbox.insert(tkinter.END, message, 'tag2')
else:
listbox.tag_config('tag3', foreground ='green')
listbox.insert(tkinter.END, message, 'tag3')
listbox.see(tkinter.END)
r = threading.Thread(target = receive)
r.start() # 开始线程接收信息
root1.mainloop()
s.close()
s.py
import socket
import threading
import queue
import json #json.dumps(some):打包 json.loads(some):解包
import os
import os.path
import sys
IP = '127.0.0.1'
PORT = 8800 # 端口
messages = queue.Queue() #消息队列
users = [] #0:userName 1:connection
lock = threading.Lock()
def onlines(): #统计当前在线人员
online = [] #在线人员列表
for i in range(len(users)):
online.append(users[i][0]) #添加新用户
return online
class ChatServer(threading.Thread): #聊天服务功能
global users, que, lock
def __init__(self): #构造函数
threading.Thread.__init__(self) #初始化
self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
os.chdir(sys.path[0])
#接收来自客户端的用户名。若用户名为空,则使用用户的IP与端口作为用户名;若用户名重复,则在用户名后依此加上后缀数字2,3,4……
def receive(self, conn, addr): #接收消息
user = conn.recv(1024) #用户名称
user = user.decode()
if user == '用户名不存在': #用户名不存在时
user = addr[0] + ' : ' + str(addr[1])
tag = 1
temp = user
for i in range(len(users)): #如果重名,则加数字
if users[i][0] == user:
tag = tag + 1
user = temp + str(tag)
users.append((user, conn))
USERS = onlines()
self.Load(USERS,addr)
#接受用户端发来的消息(即聊天内容),结束后关闭连接
try:
while True:
message = conn.recv(1024) #发送消息
message = message.decode()
message = user + ':' + message
self.Load(message,addr)
conn.close()
#若用户断开连接,则将该用户从列表中删除,并更新用户列表
except:
j = 0 #断开连接
for man in users:
if man[0] == user:
users.pop(j) #服务器端删除退出的用户
break
j = j+1
USERS = onlines()
self.Load(USERS,addr)
conn.close()
#将地址与数据存入messages队列
def Load(self, data, addr):
lock.acquire() #获取列表
try:
messages.put((addr, data)) #加入列表
finally:
lock.release()
#服务端在接受到数据后,会对其进行一些处理后发送给客户端
def sendData(self): # 发送数据
while True:
if not messages.empty(): #非空信息,发送
message = messages.get()
if isinstance(message[1], str):
for i in range(len(users)):
data = ' ' + message[1]
users[i][1].send(data.encode())
print(data)
print('n')
if isinstance(message[1], list): #转发
data = json.dumps(message[1])
for i in range(len(users)):
try:
users[i][1].send(data.encode())
except:
pass
#继承threading.Thread类,从而实现多线程
def run(self):
self.s.bind((IP,PORT))
self.s.listen(5)
q = threading.Thread(target = self.sendData)
q.start()
while True:
conn, addr = self.s.accept()
t = threading.Thread(target = self.receive, args = (conn, addr))
t.start()
self.s.close()
if __name__ == '__main__': #main函数
cserver = ChatServer()
cserver.start()
6、遇到的问题以及解决方法
1、图形化界面不熟,会出现各个框图重叠的现象。
解决方案:通过不断调试完成界面设计。
2、出现了无法进行群聊只能私聊的情况。
解决方案:经过分析,发现是参数匹配错误,使得群聊情况无法跳转。
7、运行结果分析与评价运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gscjZBim-1652271228777)(E:study计算机网络temp5.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7xNTjusD-1652271228778)(E:study计算机网络temp6.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XAGBkYIv-1652271228778)(E:study计算机网络temp7.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k4nUnbXe-1652271228778)(E:study计算机网络temp8.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lfja3Yho-1652271228778)(E:study计算机网络temp9.png)]
结果分析:
本次实验基于socket和ykinter成功完成了一个带图形界面的简易聊天室程序,速度较快且支持多人,效果不错。
其他备注参考内容:
Python3网络爬虫快速入门实战解析_Jack-Cui-CSDN博客_python 爬虫快速入门
github.com
(1条消息) 手把手教你利用爬虫爬网页(Python代码)_Python大本营的博客-CSDN博客
socket–socket()、bind()、listen()、connect()、accept()、recv()、send()、select()、close()、shutdown() - 未闻花开 - 博客园 (cnblogs.com)
Python实现网络聊天室的示例代码(支持多人聊天与私聊)_python_脚本之家 (jb51.net)
tkinter模块常用参数(python3) - 覆手为云p - 博客园 (cnblogs.com)
/article/details/90206227/?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-0.no_search_link&spm=1001.2101.3001.4242.1)
socket–socket()、bind()、listen()、connect()、accept()、recv()、send()、select()、close()、shutdown() - 未闻花开 - 博客园 (cnblogs.com)
Python实现网络聊天室的示例代码(支持多人聊天与私聊)_python_脚本之家 (jb51.net)
tkinter模块常用参数(python3) - 覆手为云p - 博客园 (cnblogs.com)
(1条消息) 什么是socket?socket详解_刘德鹏的博客-CSDN博客_socket.socket



