数据采集第一次作业
Python 网络爬虫实验博客
本次实验基于 Python 语言完成三项爬虫作业:利用
requests与BeautifulSoup定向爬取大学排名数据、利用requests与re实现商城商品比价爬虫、爬取网页中的全部图片文件。以下分别记录各作业的实验过程与心得体会。
作业①:大学排名数据定向爬取实验
1)、大学排名数据爬取实验
实验目的:掌握使用 requests 库获取网页源码、使用 BeautifulSoup 库解析 HTML 文档的方法,实现对中国最好大学排名数据的定向爬取与格式化输出。
实验环境:Python 3.13、requests 库、beautifulsoup4 库。
实验思路:向软科排名网址(http://www.shanghairanking.cn/rankings/bcur/2020 )发送 GET 请求获取页面源码;该页面为服务端渲染,排名数据直接内嵌在 HTML 表格的行(<tr>)中;使用 BeautifulSoup 定位 tbody 中的每一行,依次提取排名、学校名称、省市、学校类型、总分五个字段并打印。
核心代码(代码块形式):
# -*- coding: utf-8 -*-
"""
作业①:使用 requests 和 BeautifulSoup 库方法定向爬取软科中国大学排名数据
网址:http://www.shanghairanking.cn/rankings/bcur/2020 (2020 中国最好大学排名)
输出信息:排名 / 学校名称 / 省市 / 学校类型 / 总分
"""
import requests
from bs4 import BeautifulSoup
# 目标网址常量(作业指定)
TARGET_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"
)
}
def fetch_page(url):
"""发送 GET 请求获取网页源码。
参数:
url (str): 目标网页地址
返回:
str: 网页源码文本;请求失败时返回空字符串
"""
try:
response = requests.get(url, headers=HEADERS, timeout=15)
# 设置编码为 utf-8,保证中文正常显示
response.encoding = "utf-8"
response.raise_for_status() # 状态码非 200 时抛出异常
return response.text
except requests.RequestException as error:
print("[错误] 网页请求失败:", error)
return ""
def parse_ranking(html_text):
"""解析排名页面 HTML,提取每所大学的排名信息。
参数:
html_text (str): 排名页面源码
返回:
list[dict]: 每所大学的排名信息列表,元素包含
rank(排名)、name(学校名称)、province(省市)、
type_(学校类型)、score(总分)
"""
soup = BeautifulSoup(html_text, "html.parser")
ranking_data = []
# 定位表格主体中的所有数据行(tr)
tbody = soup.find("tbody")
if tbody is None:
return ranking_data
for row in tbody.find_all("tr"):
cells = row.find_all("td") # 获取一行中的所有单元格
if len(cells) < 5: # 数据不完整则跳过
continue
# 排名:第一个单元格内 div.ranking 中的数字
rank_div = cells[0].find("div", class_="ranking")
rank = rank_div.get_text(strip=True) if rank_div else cells[0].get_text(strip=True)
# 学校名称:第二个单元格内 span.name-cn 中的中文名
name_span = cells[1].find("span", class_="name-cn")
name = name_span.get_text(strip=True) if name_span else cells[1].get_text(strip=True)
# 省市 / 学校类型 / 总分:依次取第三、四、五个单元格
province = cells[2].get_text(strip=True)
type_ = cells[3].get_text(strip=True)
score = cells[4].get_text(strip=True)
ranking_data.append({
"rank": rank,
"name": name,
"province": province,
"type_": type_,
"score": score,
})
return ranking_data
def print_ranking(data_list):
"""按作业要求的表格格式在屏幕打印排名信息。
参数:
data_list (list[dict]): 排名信息列表
"""
print("排名\t学校名称\t省市\t学校类型\t总分")
for item in data_list:
print(f"{item['rank']}\t{item['name']}\t"
f"{item['province']}\t{item['type_']}\t{item['score']}")
def main():
"""程序入口:爬取并打印 2020 中国最好大学排名信息。"""
html_text = fetch_page(TARGET_URL)
if not html_text:
print("未获取到网页内容,程序结束。")
return
data_list = parse_ranking(html_text)
if not data_list:
print("未解析到排名数据,请检查网页结构是否变化。")
return
print(f"共解析到 {len(data_list)} 所大学:")
print_ranking(data_list)
if __name__ == "__main__":
main()
运行结果:

2)、心得体会
通过作业①我掌握了使用 requests 库获取网页源码、使用 BeautifulSoup 库解析 HTML 的完整流程。初次接触时我曾以为排名数据是通过异步接口动态加载的,但实际抓取后发现该页面为服务端渲染,数据直接内嵌在 HTML 中,用 BeautifulSoup 定位表格行即可稳定提取,这让我认识到"先分析页面源码结构、再决定解析策略"的重要性。此外,为兼容中文显示,需要对响应设置正确的编码(utf-8);模拟浏览器请求头(User-Agent)也是避免被服务器拒绝的有效手段。实验还让我体会到规范命名与注释的必要性——把"抓取"和"解析"拆分成独立函数后,代码的可读性与可复用性明显提升。
作业②:商城商品比价爬虫实验
1)、商城商品比价爬虫实验
实验目的:掌握使用 requests 库配合 re 正则表达式实现定向爬虫的方法,完成自选商城"书包"关键词的商品名称与价格爬取。
实验环境:Python 3.13、requests 库、re 库(标准库)。
实验思路:对比多个商城后选择当当网(https://www.dangdang.com/ )作为实验商城——京东搜索页存在严格反爬(返回空壳页面),当当网可直接访问。通过正则匹配商品条目中 <span class="price_n"> 的价格与 <a title="..."> 的商品名,并对价格中的货币符号、商品名中的 HTML 标签进行清洗后输出。页面为 gb2312 编码,需按服务器返回的编码解码。
核心代码(代码块形式):
# -*- coding: utf-8 -*-
"""
作业②:使用 requests 和 re 库方法设计商城商品比价定向爬虫
商城:当当网(自选商城, https://www.dangdang.com/ )
关键词:书包,爬取搜索结果页中的商品名称和价格
输出信息:序号 / 价格 / 商品名
"""
import re
import requests
# 当当网搜索页地址(keyword=书包,URL 编码为 %E4%B9%A6%E5%8C%85)
SEARCH_URL = "https://search.dangdang.com/?key=%E4%B9%A6%E5%8C%85"
# 请求头:模拟浏览器访问,避免被服务器拒绝
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"
),
"Referer": "https://www.dangdang.com/", # 来源页,部分站点据此校验
}
# 匹配单个商品条目(li):价格 span.price_n 与标题 a[title] 成对出现
# 使用非贪婪匹配,避免跨商品误匹配
ITEM_PATTERN = re.compile(
r'<span class="price_n">(.*?)</span>.*?<a title="(.*?)"',
re.S,
)
# 清洗标题时用到的 HTML 标签正则
TAG_PATTERN = re.compile(r"<[^>]+>")
def fetch_page(url):
"""发送 GET 请求获取商城搜索页源码。
参数:
url (str): 搜索页地址
返回:
str: 网页源码文本;请求失败时返回空字符串
"""
try:
response = requests.get(url, headers=HEADERS, timeout=15)
response.raise_for_status() # 状态码非 200 时抛出异常
# 当当网页面为 gb2312 编码,按服务器返回的编码解码,保证中文正常
response.encoding = response.apparent_encoding
return response.text
except requests.RequestException as error:
print("[错误] 网页请求失败:", error)
return ""
def extract_items(html_text):
"""使用正则从搜索页源码中提取商品的价格与名称。
参数:
html_text (str): 搜索页源码
返回:
list[dict]: 商品信息列表,元素包含 price(价格) 和 name(商品名)
"""
items = []
for match in ITEM_PATTERN.finditer(html_text):
price_raw = match.group(1) # 原始价格文本,如 ¥65.00
name_raw = match.group(2) # 原始标题文本,可能含 <font> 标签
# 价格清洗:去掉货币符号、空格等非数字字符,保留小数
price = re.sub(r"[^\d.]", "", price_raw).strip()
# 商品名清洗:去掉 HTML 标签并压缩多余空白
name = re.sub(r"\s+", " ", TAG_PATTERN.sub("", name_raw)).strip()
if price and name: # 过滤无效数据
items.append({"price": price, "name": name})
return items
def print_items(items):
"""按作业要求的表格格式在屏幕打印商品信息。
参数:
items (list[dict]): 商品信息列表
"""
print("序号\t价格\t商品名")
for index, item in enumerate(items, start=1):
print(f"{index}\t{item['price']}\t{item['name']}")
def main():
"""程序入口:爬取当当网"书包"搜索页并打印商品名称与价格。"""
html_text = fetch_page(SEARCH_URL)
if not html_text:
print("未获取到网页内容,程序结束。")
return
items = extract_items(html_text)
if not items:
print("未解析到商品数据,请检查网页结构或网络状态。")
return
print(f"共解析到 {len(items)} 件商品:")
print_items(items)
if __name__ == "__main__":
main()
运行结果:


2)、心得体会
作业②让我体会最深的是"正则表达式 + 编码处理"在实际爬虫中的关键作用。实验中我先后尝试了多个商城:京东搜索页返回的响应内容极少,是典型的风控空壳页面,最终换用当当网才稳定获取到完整数据,这让我认识到选对目标站点、处理好反爬是爬虫落地的前提。当当网页面采用 gb2312 编码,若不按服务器返回的编码解码就会中文乱码,这提醒我编码问题必须放在请求阶段解决。正则提取时还遇到了商品名内嵌 <font> 高亮标签、价格带货币符号等问题,通过二次清洗(去标签、去符号)才得到干净数据。此外,正则中的非贪婪匹配与 re.S 标志避免了跨商品误匹配,这些小细节让我对正则的运用更加熟练。
作业③:网页图片批量下载实验
1)、网页图片下载实验
实验目的:掌握使用 requests 库与正则表达式从网页中批量提取并下载指定格式(JPEG/JPG/PNG)图片文件的方法。
实验环境:Python 3.13、requests 库、re 库(标准库)、urllib 库(标准库)。
实验思路:以福州大学新闻网·院系风采(https://news.fzu.edu.cn/yxfd.htm )为给定网页,用正则匹配所有 <img> 标签的 src 属性;仅保留扩展名为 .jpg、.jpeg、.png 的图片,并将相对路径(如 /__local/...jpg)通过 urljoin 拼接为完整地址;逐个下载并保存到统一文件夹中,同时用 URL 末段作为文件名。
核心代码(代码块形式):
# -*- coding: utf-8 -*-
"""
作业③:爬取网页中的所有 JPEG、JPG 或 PNG 格式图片文件并保存到文件夹
网页:https://news.fzu.edu.cn/yxfd.htm (福州大学新闻网·院系风采)
输出信息:将网页内的所有 JPEG、JPG 或 PNG 格式图片保存在一个文件夹中
"""
import os
import re
from urllib.parse import urljoin, urlparse
import requests
# 目标网页地址(作业给定网页)
TARGET_URL = "https://news.fzu.edu.cn/yxfd.htm"
# 图片保存文件夹(与脚本同目录)
SAVE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "fzu_images")
# 匹配 img 标签中 src 属性的正则
IMG_SRC_PATTERN = re.compile(r'<img[^>]+src=["\']([^"\']+)["\']', re.I)
# 匹配图片文件扩展名的正则(jpeg / jpg / png)
IMG_EXT_PATTERN = re.compile(r"\.(jpe?g|png)$", re.I)
# 请求头:模拟浏览器访问
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"
)
}
def fetch_page(url):
"""发送 GET 请求获取网页源码。
参数:
url (str): 目标网页地址
返回:
str: 网页源码文本;请求失败时返回空字符串
"""
try:
response = requests.get(url, headers=HEADERS, timeout=15)
response.raise_for_status()
response.encoding = response.apparent_encoding
return response.text
except requests.RequestException as error:
print("[错误] 网页请求失败:", error)
return ""
def collect_image_urls(html_text, base_url):
"""从网页源码中提取所有 jpeg/jpg/png 图片的完整下载地址。
参数:
html_text (str): 网页源码
base_url (str): 页面基准地址,用于拼接相对路径图片
返回:
list[str]: 图片完整 URL 列表(已去重)
"""
image_urls = []
for match in IMG_SRC_PATTERN.finditer(html_text):
src = match.group(1).strip()
if not src: # 跳过空 src
continue
# 将相对路径(如 /__local/xxx.jpg)拼接为完整地址
full_url = urljoin(base_url, src)
# 只保留扩展名为 jpg / jpeg / png 的图片
if IMG_EXT_PATTERN.search(urlparse(full_url).path):
image_urls.append(full_url)
# 去重并保持原有顺序
return list(dict.fromkeys(image_urls))
def download_image(image_url, save_dir):
"""下载单张图片到指定文件夹。
参数:
image_url (str): 图片完整地址
save_dir (str): 保存图片的文件夹路径
返回:
str | None: 保存成功的文件名;失败时返回 None
"""
try:
response = requests.get(image_url, headers=HEADERS, timeout=15)
response.raise_for_status()
except requests.RequestException as error:
print("[错误] 图片下载失败:", image_url, "->", error)
return None
# 以 URL 最后一段作为文件名,如 F84E4.jpg
file_name = os.path.basename(urlparse(image_url).path)
file_path = os.path.join(save_dir, file_name)
with open(file_path, "wb") as file_obj:
file_obj.write(response.content)
return file_name
def main():
"""程序入口:爬取指定网页的全部图片并保存到文件夹。"""
os.makedirs(SAVE_DIR, exist_ok=True) # 创建保存图片的文件夹
html_text = fetch_page(TARGET_URL)
if not html_text:
print("未获取到网页内容,程序结束。")
return
image_urls = collect_image_urls(html_text, TARGET_URL)
print(f"在网页中找到 {len(image_urls)} 张 jpg/jpeg/png 图片,开始下载……")
success_count = 0
for image_url in image_urls:
file_name = download_image(image_url, SAVE_DIR)
if file_name:
success_count += 1
print(f"已保存:{file_name} <- {image_url}")
print(f"下载完成:成功 {success_count} 张,保存目录:{SAVE_DIR}")
if __name__ == "__main__":
main()
运行结果:

2)、心得体会
作业③是对前两个作业的综合运用,也让我遇到了新的问题:页面中的图片地址多为相对路径,直接拼接字符串容易出错,使用 urljoin 按基准地址解析才是稳妥做法;同时页面里还混有 logo、图标等小图片以及空 src 占位,需要先用正则过滤出扩展名为 .jpg、.jpeg、.png 的图片再去重下载。下载完成后,我还通过读取文件二进制头部(JPEG 以 FF D8 FF 开头、PNG 以 89 50 4E 47 开头)逐一校验了图片有效性,避免了"文件存在但已损坏"的假成功。这一作业让我体会到:一个健壮的爬虫不仅要"抓得到",还要"抓得对、验得住",对数据的完整性负责。
总结:三次实验由浅入深,从静态表格解析(BeautifulSoup)、到正则定向提取(re),再到图片资源的批量下载,覆盖了爬虫开发的核心环节。我也养成了良好的代码习惯:函数职责单一、命名规范、关键处添加注释,并注重编码、请求头、异常处理与结果校验等细节。

浙公网安备 33010602011771号