数采实践—第一次作业
| Editor | 赖彦然 |
|---|---|
| 学号 | 102402106 |
| My Gitee | https://gitee.com/Vivian_azad/lyr.git |
作业任务目录:
① 爬取软科中国大学排名(requests + BeautifulSoup)
② 商城"书包"商品比价定向爬虫(requests + re)
③ 批量爬取网页图片并保存(requests + re)
三个作业遵循同一套爬虫基本流程:
一、爬取软科中国大学排名(主要考察:requests + BeautifulSoup)
1.1 作业代码与运行结果
爬取目标:软科中国大学排名2020,提取排名、学校名称、省市、学校类型、总分并打印。
实现思路:
- requests 携带请求头访问目标页面;
- BeautifulSoup 定位 class 为
rk-table的排名表格; - 遍历表体中每一行
tr,按列取出文本,用制表符对齐打印。
代码1:
# editor:Lai Yanran
# 2026年10月12日
import csv
import requests
from bs4 import BeautifulSoup
# 软科中国大学排名2020
url = "http://www.shanghairanking.cn/rankings/bcur/2020"
# 请求头,模拟浏览器访问
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/120.0.0.0 Safari/537.36"
}
# 发送get请求获取网页
resp = requests.get(url, headers=headers, timeout=10)
resp.encoding = "utf-8"
# 用BeautifulSoup解析网页
soup = BeautifulSoup(resp.text, "html.parser")
# 找到排名表格
table = soup.find("table", class_="rk-table")
"""
考虑到结果的可读性,在这里,我选择把结果写成csv表格文件,这样可以方便地用Excel打开查看。在实际应用中,也会更方便后续的研究
"""
# 一条条把表格里的数据取出来,存进列表
rows = []
for tr in table.find("tbody").find_all("tr"):
tds = tr.find_all("td")
rank = tds[0].get_text(strip=True) # 排名
name = tds[1].find("span", class_="name-cn").get_text(strip=True) # 学校名称
province = tds[2].get_text(strip=True) # 省市
school_type = tds[3].get_text(strip=True) # 学校类型
score = tds[4].get_text(strip=True) # 总分
rows.append([rank, name, province, school_type, score])
# 把结果写成csv表格文件
# 用utf-8-sig编码,这样用Excel打开不会乱码
with open("大学排名.csv", "w", newline="", encoding="utf-8-sig") as f:
writer = csv.writer(f)
writer.writerow(["排名", "学校名称", "省市", "学校类型", "总分"])
writer.writerows(rows)
#打印一条代码运行完成的提示
print(f"爬取成功啦!!共{len(rows)}条数据,已保存到大学排名.csv")
运行结果(csv文件截图,生成文件已经和代码一起上传):

运行结果截图:

💡 关键点:学校中文名并不直接在
td里,而是单独包裹在class="name-cn"的span中,需要在第二个td里再向下找一层,直接取td文本会混入排名数字等杂项。
1.2 作业心得
这次作业让我完整走了一遍爬虫的基本流程:requests 发请求、拿到网页文本、再用 BeautifulSoup 解析。体会最深的是,写解析代码前一定要先按 F12 打开开发者工具观察网页结构:排名数据在一个 class 为 rk-table 的表格里,每行是 tr,每列是 td,找到这些规律后代码就很好写了。
另外还有两个小收获:一是请求时要带上 User-Agent 模拟浏览器,这是爬虫的基本常识,很多网站会拦截没有请求头的访问;二是 get_text(strip=True) 可以把标签里的文本取出来并去掉多余空白,比手动处理字符串方便很多。
二、商城商品比价定向爬虫(requests + re)
2.1 作业代码与运行结果
爬取目标:选择当当网[^2],以关键词"书包"搜索,用正则提取商品名称和价格并打印。
实现思路:
- 关键词按 gb2312 编码后拼出搜索页 URL;
- requests 获取搜索结果页源码(注意设置响应编码防乱码);
- 用 re 正则分别匹配商品名和价格,按序号对齐打印。
代码2:
# editor:Lai Yanran
# 2026年10月12日
import re
import requests
from urllib.parse import quote
# 选择当当网,搜索关键词"书包"
keyword = "书包"
# 当当网的搜索关键词要用gb2312编码拼到网址里,这是由于网站机制
url = "http://search.dangdang.com/?key=" + quote(keyword.encode("gb2312"))
#请求头
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/120.0.0.0 Safari/537.36"
}
# 发送get请求获取搜索页面
resp = requests.get(url, headers=headers, timeout=10)
resp.encoding = "gb18030" # 当当网页是gb2312编码,不设置会乱码
html = resp.text
# 用正则提取商品名和价格
# 商品名在a标签的title属性里,价格数字在price_n标签里
names = re.findall(r'<p class="name" name="title"[^>]*><a title="([^"]+)"', html)
prices = re.findall(r'<span class="price_n">¥(.*?)</span>', html)
# 把商品名和价格配对存到列表里
goods = []
count = min(len(names), len(prices))
for i in range(count):
name = names[i].strip()
price = float(prices[i].strip())
goods.append([price, name])
"""
由于结果众多,所以我模拟了许多人平时的购物习惯,按价格从低到高排序,只取价格最低的前20名商品输出,这样更符合实际应用场景
"""
# 按价格从低到高排序,只取价格最低的前20名
goods.sort(key=lambda x: x[0])
top20 = goods[:20]
# 打印表头
print("序号\t价格\t商品名")
# 逐条打印价格最低的前20名商品
for i in range(len(top20)):
price = top20[i][0]
name = top20[i][1]
print(f"{i + 1}\t{price:.2f}\t{name}")
print(f"\n共爬取到{count}个商品,已按价格从低到高输出前{len(top20)}名")
运行结果截图:

2.2 作业心得
这次作业比作业一难,主要卡在两个地方。第一个是编码问题:当当网用的是 gb2312 编码,搜索关键词要先 encode("gb2312") 再 quote 才能拼出正确的网址,拿到响应后也要设置 resp.encoding = "gb18030",不然打印出来全是乱码。
第二个是正则的编写,我一开始照着渲染后的页面结构写,结果一个都匹配不到,后来改为对照网页源代码逐段排查,才定位到商品名在 a 标签的 title 属性里、价格数字在 price_n 标签里。这让我明白正则没有捷径,核心就是反复观察目标文本的前后特征。另外用 min(len(names), len(prices)) 取两个列表的较小长度,可以避免商品名和价格数量不一致时索引越界报错。
三、爬取网页图片(requests + re)
3.1 作业代码与运行结果
爬取目标:福州大学新闻网页中所有 JPEG、JPG、PNG 格式的图片,统一保存到 3_images 文件夹。
实现思路:
- requests 获取网页源码,用
apparent_encoding自动识别编码; - 正则匹配所有
src中后缀为 jpg / jpeg / png 的图片地址;
代码3:
# editor:Lai Yanran
# 2026年10月12日
import os
import re
import requests
from urllib.parse import urljoin
# 福州大学新闻网页
url = "https://news.fzu.edu.cn/yxfd.htm"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/120.0.0.0 Safari/537.36"
}
# 创建保存图片的文件夹
save_dir = "3_images"
if not os.path.exists(save_dir):
os.makedirs(save_dir)
# 发送get请求获取网页
resp = requests.get(url, headers=headers, timeout=10)
resp.encoding = resp.apparent_encoding
html = resp.text
# 正则找出所有jpg、jpeg、png格式的图片地址
img_srcs = re.findall(r'src="([^"]+\.(?:jpg|jpeg|png))"', html, re.I)
print(f"找到{len(img_srcs)}张图片")
count = 0
for src in img_srcs:
img_url = urljoin(url, src)
ext = os.path.splitext(img_url.split("?")[0])[1]
try:
# 下载图片并保存到文件夹
img_data = requests.get(img_url, headers=headers, timeout=10).content
file_path = os.path.join(save_dir, f"{count + 1}{ext}")
with open(file_path, "wb") as f:
f.write(img_data)
print(f"已保存:{file_path}")
count += 1
except Exception as e:
print(f"{img_url} 下载失败:{e}")
print("已经下载了%d张图片,保存在%s文件夹中" % (count, save_dir))
运行结果截图(控制台输出 + 保存图片的文件夹):


测试一下,图片是否都能正常打开

3.2 作业心得
这次作业让我学会了爬取并保存二进制文件。和爬文本不同,下载图片要用 response.content 拿到二进制内容,写文件时也要用 "wb" 二进制写入模式。正则部分用 (?:jpg|jpeg|png) 同时匹配三种格式,加上 re.I 忽略大小写,因为网页里的图片后缀写法不统一。
最容易踩的坑是相对路径:网页里很多图片的 src 是类似 /_upload/... 这样的相对地址,直接拿去请求会报错,需要用 urljoin 把相对路径补全成完整 URL。另外我还加了 try/except,这样个别图片下载失败时程序不会整体崩溃,而是跳过继续下载下一张;保存前先用 os.path.exists 判断文件夹是否存在,避免重复建目录报错。
总结
三个task对比,用到的技术各有侧重:
| 作业 | 目标网站 | 解析方式 | 最难的一点 |
|---|---|---|---|
| ① 大学排名 | 软科 | BeautifulSoup | 多层标签的逐层定位 |
| ② 商品比价 | 当当网 | re 正则 | gb2312 编码与原始 HTML 结构确认 |
| ③ 图片下载 | 福州大学新闻网 | re 正则 | 相对路径补全与二进制保存 |
通过这次作业,我对"发送请求 → 获取源码 → 解析提取 → 输出保存"这条爬虫主线有了完整的实践。
浙公网安备 33010602011771号