数据采集第一次作业

数据采集与融合技术 第一次作业
作业①
实验
实验目标:使用 requests + BeautifulSoup 定向爬取软科2020中国大学排名页面,控制台输出:排名、学校名称、省市、学校类型、总分
目标URL:http://www.shanghairanking.cn/rankings/bcur/2020
依赖安装:pip install requests beautifulsoup4 lxml
import requests
from bs4 import BeautifulSoup
def get_html_text(url: str):
"""
发起网页请求,获取页面HTML文本,处理反爬、异常、编码
:param url: 目标网页链接
:return: html字符串,失败返回空
"""
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
"Accept-Language": "zh-CN,zh;q=0.9"
}
try:
# 增加超时,防止卡死
resp = requests.get(url, headers=headers, timeout=25)
resp.raise_for_status()
resp.encoding = "utf-8"
return resp.text
except requests.exceptions.HTTPError as he:
print(f"HTTP访问错误:{he}")
return ""
except Exception as e:
print(f"页面请求异常:{e}")
return ""
def parse_university_rank(html: str):
"""
解析软科排名页面,提取 排名、校名、省市、学校类型、总分
:param html: 网页源码
:return: 列表,每一项是一所学校信息
"""
soup = BeautifulSoup(html, "lxml")
# 定位数据表格主体
table_body = soup.find("tbody")
if not table_body:
print("未找到数据表格tbody标签!")
return []
rows = table_body.find_all("tr")
res_list = []
for row in rows:
tds = row.find_all("td")
# 校验单元格数量,防止页面结构变动报错
if len(tds) >= 5:
rank = tds[0].get_text(strip=True)
school_name = tds[1].get_text(strip=True)
province = tds[2].get_text(strip=True)
school_kind = tds[3].get_text(strip=True)
score = tds[4].get_text(strip=True)
res_list.append([rank, school_name, province, school_kind, score])
return res_list
if __name__ == "__main__":
target_url = "http://www.shanghairanking.cn/rankings/bcur/2020"
html_content = get_html_text(target_url)
data = parse_university_rank(html_content)
# 格式化打印,和题目样例格式对齐
print(f"{'排名':<6}{'学校名称':<18}{'省市':<8}{'学校类型':<10}{'总分':<8}")
for item in data:
print(f"{item[0]:<6}{item[1]:<18}{item[2]:<8}{item[3]:<10}{item[4]:<8}")

心得体会
本次定向静态爬虫实验,目标页面是软科2020中国大学排名。一开始踩坑点:软科网站有基础反爬,不带User-Agent直接访问会403拒绝;之前版本没有校验td单元格长度,页面部分行结构不一样,会出现下标越界。
我理解了静态网页爬虫的标准流程:构造请求头伪装浏览器、获取源码、BeautifulSoup定位tbody主体、循环遍历tr行提取td单元格。
通过这个实验,我区分了strip()去除空白换行的作用,学会增加异常捕获提升鲁棒性;同时明白爬虫要遵守robots协议,控制访问频率,仅用于课程学习,不高频批量请求给网站造成负担。静态解析适合结构固定、不使用JS渲染的页面,是数据采集最基础的手段。
作业②

实验
实验目标:requests + re正则表达式(禁止bs4),商城比价爬虫,搜索关键词“书包”,提取【序号、价格、商品名称】
选择京东搜索页(稳定性强,适合正则抓取)
依赖:只需要requests
import requests
import re
def get_page_source(url: str):
"""获取网页源码文本"""
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
}
try:
r = requests.get(url, headers=headers, timeout=25)
r.raise_for_status()
r.encoding = "utf-8"
return r.text
except Exception as e:
print(f"商城页面请求失败:{e}")
return ""
def parse_goods_by_re(html: str):
"""
纯正则匹配:商品名称、商品价格,不使用BeautifulSoup
非贪婪匹配 .*? 防止跨标签抓取错误
"""
# 匹配京东商品价格
pattern_price = re.compile(r'<em class="J_price">¥<span>(\d+\.?\d*)</span></em>')
# 匹配商品标题
pattern_title = re.compile(r'<a target="_blank" title="(.*?)" href')
price_result = pattern_price.findall(html)
title_result = pattern_title.findall(html)
# 两个列表打包对齐
goods_data = list(zip(title_result, price_result))
return goods_data
if __name__ == "__main__":
search_url = "https://search.jd.com/Search?keyword=书包"
page_html = get_page_source(search_url)
goods = parse_goods_by_re(page_html)
print(f"{'序号':<6}{'价格':<8}{'商品名'}")
for idx, (title, price) in enumerate(goods, start=1):
print(f"{idx:<6}{price:<8}{title}")
心得体会
本实验和上一个实验最大区别:放弃标签解析库,使用正则表达式直接在原始字符串里匹配内容。写正则的时候很容易踩贪婪匹配的坑,一开始写.*会一口气匹配到很远的结束标签,改成.*?非贪婪模式才可以精准捕获。
正则适合页面标签简单、结构固定的场景,缺点非常明显:网站前端只要修改class名字、调整标签顺序,整个正则直接失效,兼容性弱。而BeautifulSoup依靠标签层级,容错更好。比价爬虫的思路可以拓展,同时爬取多个电商平台,实现横向比价;但是一定要控制请求间隔,避免IP封禁。这次也加深了我对字符串匹配、正则分组捕获的理解。
作业③

实验
实验目标:爬取福大新闻页面 https://news.fzu.edu.cn/yxfd.htm ,抓取所有jpg/jpeg/png图片,自动新建文件夹保存图片,过滤其他格式
依赖:requests、beautifulsoup4
import requests
from bs4 import BeautifulSoup
import os
from urllib.parse import urljoin
def get_html(url: str):
"""请求页面源码"""
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
}
try:
resp = requests.get(url, headers=headers, timeout=20)
resp.raise_for_status()
resp.encoding = "utf-8"
return resp.text
except Exception as e:
print(f"福大新闻页面请求失败:{e}")
return ""
def download_image(img_link: str, save_folder: str):
"""
下载单张图片,只保留 jpg / jpeg / png
:param img_link: 图片完整url
:param save_folder: 保存文件夹名称
"""
try:
img_resp = requests.get(img_link, timeout=15)
# 获取后缀小写,统一判断
suffix = img_link.split(".")[-1].lower()
if suffix not in ["jpg", "jpeg", "png"]:
return
img_filename = os.path.basename(img_link)
save_full_path = os.path.join(save_folder, img_filename)
# 图片是二进制,wb模式写入
with open(save_full_path, "wb") as f:
f.write(img_resp.content)
print(f"✅ 已保存图片:{img_filename}")
except Exception as e:
print(f"❌ 图片下载失败 {img_link},错误:{e}")
if __name__ == "__main__":
page_url = "https://news.fzu.edu.cn/yxfd.htm"
save_dir_name = "fzu_news_pic"
# 文件夹不存在就自动创建
if not os.path.exists(save_dir_name):
os.mkdir(save_dir_name)
html_text = get_html(page_url)
soup = BeautifulSoup(html_text, "lxml")
img_tags = soup.find_all("img")
for img in img_tags:
src = img.get("src")
if not src:
continue
# 重点:网页内很多src是相对路径,urljoin拼接成完整绝对地址,否则404
absolute_img_url = urljoin(page_url, src)
download_image(absolute_img_url, save_dir_name)
print("\n==== 全部图片处理完毕 ====")
心得体会
图片采集和文本采集有很大区别:文本读取resp.text,图片必须读取二进制的resp.content,以wb二进制模式写入本地文件。这个页面大量图片是相对路径,是本次最大难点,如果不使用urljoin拼接,直接拿src去请求,全部404访问失败。
同时我增加了后缀过滤,严格只保存题目指定的JPG/JPEG/PNG。课程实验仅用于学习网页多媒体采集;图片本身存在版权,不可以商用传播。这个实验让我掌握了资源类爬虫的思路,也理解了url相对路径、绝对路径的转换逻辑。
整体总结
三次爬虫实验覆盖了静态文本解析、正则匹配、二进制资源下载三类典型数据采集场景,分别练习BeautifulSoup标签解析、re正则表达式、图片资源爬取,掌握请求头伪装、异常捕获、路径拼接、编码处理这些通用爬虫基础知识点。

浙公网安备 33010602011771号