0%

计算机网络爬虫

一、基本信息

(略)

二、实验目的

要求学生掌握Socket编程技术及令牌总线协议工作过程

三、基础实验内容

Ⅰ、traceroute

1、实验目的:熟悉traceroute的使用

2、实验内容: 用traceroute测量到163网站(www.163.com)和到微软公司(www.microsoft.com)网站的路径。分析测量结果。

3、实验流程及结果

在Ubuntu上下载安装traceroute

用traceroute测量到163网站(www.163.com)的路径

ICMP 163

用traceroute测量到微软网站(www.microsoft.com)的路径

ICMP microsoft

上述输出信息显示了跟踪到的路由地址信息。记录从序号 1 开始,每个记录就是一跳,而每一跳表示经过的一个网关。记录给出了每个网关对应的IP地址。其中,为 * 的记录表示可能被防火墙拦截的 ICMP 的返回信息。

在linux上使用traceroute,默认使用udp协议,除了第一跳,剩下的都是 *。是因为虚拟机 nat 路由器,默认丢弃port>32767的包。在命令中加入 -l 强制使用ICMP得到上图结果。

Ⅱ、wireshark

1、实验目的:熟悉wireshark的使用

2、实验内容:下载安装wireshark软件,设置捕获条件,用wireshark捕获数据包,对以太网帧和IP数据包进行分析。

3、实验流程及结果

Wireshark 是一个免费开源的网络数据包分析软件。它的功能是截取网络数据包,并尽可能显示出最为详细的网络数据包数据。

下载安装Wireshark并启动

查询网卡

打开火绒浏览器开始发包测试

TCP找目的地址,捕获数据包,对以太网帧和IP数据包进行分析。

TCP 80 端口 作为过滤条件进行筛选分析

四、工程性实验内容

Ⅰ、Robots协议

具体内容:查看淘宝网的Robots协议,了解抓取网站数据需要遵循的规则

Robots协议的全称是“网络爬虫排除标准”(Robots Exclusion Protocol),网站通过Robots协议告诉搜索引擎哪些页面可以爬取,哪些页面不可以爬取。这个协议是互联网中的道德规范,虽没写入法律,但应遵守。在网站末尾加/robots.txt可以进行查看。

image-20220427171824767

Robots协议的语法:#注释,*代表所有,/代表根目录。

无robots协议的网站,信息可以爬取。

自动或人工识别robots.txt,再进行内容爬取,Robots协议是建议但非约束性,网络爬虫可以不遵守,但存在法律风险。访问量很小的可以遵循,访问量大的建议遵循。非商业的建议遵循,涉及商业利益的必须遵循,爬取全网信息必须要遵循。如果是类似人类行为的爬取信息,可不遵循。

Ⅱ、 搜索关键词查询确定其关键词的查询接口

以豆瓣图书为例,进入豆瓣读书网站

随便点击一本书,观察其网址

观察到红框部分即为该书在豆瓣数据库中的代号

进入短评部分

观察到前一个红框部分为该书的代码,后一个红框部分为这一页短评的起始标号,豆瓣书籍网页版一页共有20条短评,所以 start = num (num从0开始并且为20的整倍数)

1
2
3
4
url = "https://book.douban.com/subject/{}/comments/?start={}&limit=20&status=P&sort=new_score"

# booknum和index分别为书籍代码和start起始下标
u = url.format(str(booknum),str(index))

所以之后对豆瓣图书评论的查询靠替换关键字完成

Ⅲ、爬取豆瓣图书评论

具体内容

抓取豆瓣读书中对某本书的前50条短评内容并计算星级评定分数的平均值(保留两位小数)

实验步骤

TCP协议:传输控制协议(Transmission Control Protocol,缩写为TCP)是一种面向连接的、可靠的、基于字节流的传输层通信协议,它能提供高可靠性通信(即数据无误、数据无丢失、数据无失序、数据无重复到达的通信)。

主要适用的场景:

适合于对传输质量要求较高,以及传输大量数据的通信。

在需要可靠数据传输的场合,通常使用TCP协议。

HTTP/HTTPS等即网络服务都采用TCP协议。

TCP通信需要经过创建连接、数据传送、终止连接三个步骤。

  • 初始化 url ,并通过 get() 函数逐网页爬取网页源代码

    1
    2
    3
    4
    5
    url = "https://book.douban.com/subject/{}/comments/?start={}&limit=20&status=P&sort=new_score"
    for index in range(0, 60, 20):
    # booknum 为豆瓣书籍编号,本实验选用的是35593780
    u = url.format(str(booknum),str(index))
    r = get(u)
  • 创建 socket 实例,处理 https 的库

    1
    2
    3
    4
    def get(url):
    host = 'book.douban.com'
    path = url[23:]
    s = ssl.wrap_socket(socket.socket())
  • 初始化端口,与主机建立起连接

    1
    2
    port = 443  # 端口 https 默认端口443
    s.connect((host, port)) # 建立起连接,连接主机
  • 设置报文格式

    1
    2
    # 报文格式: GET+空格+com后面的部分+空格+HTTP/1.1(或1.0)+\r\n+Host:+域名+\r\n\r\n
    request = 'GET {} HTTP/1.1\r\nHost: {}\r\n\r\n'.format(path, host) # 构建请求
  • 发送请求并接受数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    s.send(request.encode('utf-8'))  # 发送请求  转码,将str 转成bytes类型
    response = b''
    while True:
    # 接受数据
    r = s.recv(1024)
    response += r
    if len(r) < 1024:
    break
    response = response.decode('utf-8') # 解码
  • 对数据进行查找分析

    此处主要使用 str 类型所带的 find() 函数

    Python find() 方法检测字符串中是否包含子字符串 str

    如果指定 beg(开始) 和 end(结束) 范围,则检查是否包含在指定范围内,如果包含子字符串返回开始的索引值,否则返回-1。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # html 为字符串
    # userstart 为每个用户所在源代码的开始位置
    # userend 每个用户所在源代码的结束位置
    # start_str为所查询字符串的前部分
    # end_str为所查询字符串的后部分
    def getdata(html, userstart, userend, start_str, end_str):
    start = html.find(start_str, userstart, userend)
    end = html.find(end_str, start)
    return 0 if start == -1 else html[start + len(start_str):end]

    对于星级的平均分计算,考虑到有些用户没有打分,计算平均分时需将其删除。对于上面的数据查询函数 getdata() ,如果未能查询到该用户所打的分数,则在star[] 数组中写入0,若查询到所打的分数,则写入该分数。

  • 将结果进行整理输出

    1
    2
    # douban.txt 文件和当前py文件位于同一目录之下
    output = open("douban.txt",'w',encoding='UTF—8')

将50条评论逐行输入至 douban.txt 文件中,在第51行输入平均分数,第52行输入书名。

可视化界面

该可视化界面采用 Unity3D 制作,版本为 2021.2.7f1c1,所用语言为 C#

【展示】

image-20220427223747102

[1] 处按钮为显示前页评论

[2] 处按钮为显示后页评论

[3] 处为书名显示区域

[4] 处为所求得的平均分数(保留两位小数)

[5] [6] [7] [8] 处为评论内容显示区域,可上下拖拽查看全部内容

代码及主要制作思路

创建一个string类型的数组,用以存储评论、书名、分数等数据

1
string[] txt

使用绝对路径读取上述 douban.txt 文件

1
2
using System.IO;
txt = File.ReadAllLines("C:\\Users\\EKKO\\Desktop\\ComputerNetwork\\CNLab\\douban.txt");

此时 txt 数组共有52个元素,存储50条评论、一个书名、一个平均分数

新建 TEXT 类型数组 comments 并与UI上的组件进行绑定,将书名和分数直接显示。

1
2
3
4
5
6
7
8
comments[0] = transform.GetChild(7).GetComponent<Text>();
comments[0].text = "平均分数:"+txt[50];
comments[1] = transform.GetChild(1).GetChild(0).GetComponent<Text>();
comments[2] = transform.GetChild(2).GetChild(0).GetComponent<Text>();
comments[3] = transform.GetChild(3).GetChild(0).GetComponent<Text>();
comments[4] = transform.GetChild(4).GetChild(0).GetComponent<Text>();
bookname = transform.GetChild(9).GetComponent<Text>();
bookname.text = "《"+txt[51] +"》";

调用按钮点击相应事件,为 ‘前页’ ‘后页’ 按钮绑定相关操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
upbtn.onClick.AddListener(setindexup);
downbtn.onClick.AddListener(setindexdown);

void setindexup()
{
for (int i = 1;i <5;i++)
if(index[i] < 49)
index[i] += 4;
}
void setindexdown()
{
for (int i = 1;i <5;i++)
if(index[i] > 4)
index[i] -= 4;
}

最后在 Update() 函数中逐帧进行赋值操作,达到动态刷新的效果

1
2
3
4
5
6
7
8
void Update()
{
for(int i = 1;i < 5 ;i++)
if( index[i]-1 > 49 )
comments[i].text = null;
else
comments[i].text = txt[index[i] - 1];
}

Ⅳ、抓取道指成分股数据

具体内容

https://money.cnn.com/data/dow30/ 上抓取道指成分股数据并解析其中30家公司的代码、名称和最近一次成交价,将结果放到一个列表中输出。

实验步骤

采用 request 和 re 两个包来解决

1
2
import requests
import re

确定查询网址路径

1
url = 'https://money.cnn.com/data/dow30/'

由于网址发生变化,所以实际进入的网址为

通过r=request.get(url)构造一个向服务器请求资源的url对象。

这个对象是Request库内部生成的。

这时候的r返回的是一个包含服务器资源的Response对象。包含从服务器返回的所有的相关资源。

1
r = requests.get(url)

根据网页源代码,构建查匹配对应数据所需的正则表达式

image-20220428173724771

1
2
pattern = re.compile(
'wsod_symbol">(.*?)</a> <span title="(.*?)">(.*?)</span></td><td class="wsod_aRight"><span stream="last_(.*?)<')

进行匹配查询

1
2
# data 为一个含有4个元素的元组,包含所需数据
data = re.findall(pattern, r.text)

数据处理:对数据进行筛选比对,选取所需要的 【公司的代码】、【名称】、【最近一次成交价】

1
2
3
4
5
for i in data:
# print("i:",i)
m = (i[0], i[1], i[3][-6:])
# 将其添加到列表中
dowdata.append(m)

输出示例:

1
2
for i in dowdata:
print(i)

image-20220428174108983

该结果对应时间:2022.4.28 -17:41