数据采集实践作业1
作业1
学号:102302129 姓名:何玮鑫
作业①:
1、爬取中国大学排名实验
我们先打开网页,对网页源码进行阅读,然后定位我们所需要的排名和学校名称的标签。目标位置有明显的“data-v-309300f0”字样,根据特征,我们可以设计正则表达式精确定位。

完整代码:
先是通过request爬取文本,然后利用clean_text函数对HTML文本清洗一下,然后利用bs解析文本:根据"data-v-309300f0"进行有效筛选,再从中提取排名和学校名称,最后汇总可视化。
点击查看代码
import requests
from bs4 import BeautifulSoup
import re
def clean_text(text):
# 移除HTML注释(如<!--注释内容-->)
text = re.sub(r'<!.*?>', '', text)
# 将连续空白字符(空格、换行、制表符等)替换为单个空格
text = re.sub(r'\s+', ' ', text)
# 去除处理后文本首尾的空格
return text.strip()
def crawl_university_ranking(url):
# 模拟Mac浏览器的User-Agent,避免被服务器识别为爬虫
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.1 Safari/605.1.15'
}
try:
# 发送GET请求,verify=False跳过SSL证书验证(仅用于测试环境)
with requests.get(url, headers=headers, verify=False) as response:
response.encoding = 'utf-8' # 强制指定编码为UTF-8,避免中文乱码
# 使用BeautifulSoup解析HTML内容
soup = BeautifulSoup(response.text, 'html.parser')
# 定位包含排名数据的表格主体(通过特定属性筛选,确保准确性)
tbody = soup.find('tbody', attrs={'data-v-389300f0': ''})
if not tbody:
print("未找到排名数据区域")
return
# 打印表头并格式化输出
print(f"{'排名':<6}{'学校名称':<20}{'省市':<8}{'学校类型':<8}{'总分':<8}")
print("-" * 50)
# 遍历表格中的每一行数据(通过特定属性筛选有效行)
for tr in tbody.find_all('tr', attrs={'data-v-389300f0': ''}):
# 获取当前行的所有单元格
tds = tr.find_all('td', attrs={'data-v-389300f0': ''})
if len(tds) >= 5: # 确保单元格数量充足,避免索引错误
# 提取排名(从第一个单元格的特定class中获取)
rank_div = tds[0].find('div', class_=re.compile(r'ranking'))
rank = clean_text(rank_div.text) if rank_div else '未知'
# 提取学校名称(从第二个单元格的中文名称标签中获取)
name_span = tds[1].find('span', class_='name-cn')
name = clean_text(name_span.text) if name_span else '未知'
# 提取省市、学校类型、总分(直接从对应单元格提取文本并清理)
province = clean_text(tds[2].text)
univ_type = clean_text(tds[3].text)
score = clean_text(tds[4].text)
# 格式化输出一行数据
print(f"{rank:<6}{name:<20}{province:<8}{univ_type:<8}{score:<8}")
except Exception as e:
print(f"爬取过程中出错: {str(e)}")
if __name__ == "__main__":
# 忽略urllib3库的SSL证书验证警告(消除运行时的冗余提示)
import warnings
warnings.filterwarnings("ignore", category=UserWarning, module='urllib3')
# 目标爬取的大学排名页面URL
target_url = "http://www.shanghairanking.cn/rankings/bcur/2020"
crawl_university_ranking(target_url)
核心代码解释:
tbody.find_all('tr', ...):遍历表格主体中所有的 行(每一行对应一所大学的排名数据)。
tds = tr.find_all('td', ...):在每一行中,找到所有的 单元格(每一列对应排名、学校名称等一个属性)。
排名:在第 0 个单元格中,通过 class 包含 ranking 的 <div 提取排名文本。
学校名称:在第 1 个单元格中,通过 class="name-cn" 的 提取中文校名。
# 遍历每一行数据
for tr in tbody.find_all('tr', attrs={'data-v-389300f0': ''}):
tds = tr.find_all('td', attrs={'data-v-389300f0': ''})
if len(tds) >= 5: # 确保有足够的列
# 提取排名(第一个单元格)
rank_div = tds[0].find('div', class_=re.compile(r'ranking'))
rank = clean_text(rank_div.text) if rank_div else '未知'
# 提取学校名称(第二个单元格中的中文名称)
name_span = tds[1].find('span', class_='name-cn')
name = clean_text(name_span.text) if name_span else '未知'
# 提取省市(第三个单元格)
province = clean_text(tds[2].text)
# 提取学校类型(第四个单元格)
univ_type = clean_text(tds[3].text)
# 提取总分(第五个单元格)
score = clean_text(tds[4].text)
# 打印完整信息
print(f"{rank:<6}{name:<20}{province:<8}{univ_type:<8}{score:<8}")
运行结果:
| 排名 | 学校名称 | 省市 | 学校类型 | 总分 |
|---|---|---|---|---|
| 1 | 清华大学 | 北京 | 综合 | 852.5 |
| 2 | 北京大学 | 北京 | 综合 | 746.7 |
| 3 | 浙江大学 | 浙江 | 综合 | 649.2 |
| 4 | 上海交通大学 | 上海 | 综合 | 625.9 |
| 5 | 南京大学 | 江苏 | 综合 | 566.1 |
| 6 | 复旦大学 | 上海 | 综合 | 556.7 |
| 7 | 中国科学技术大学 | 安徽 | 理工 | 526.4 |
| 8 | 华中科技大学 | 湖北 | 综合 | 497.7 |
| 9 | 武汉大学 | 湖北 | 综合 | 488.0 |
| 10 | 中山大学 | 广东 | 综合 | 457.2 |
| 11 | 西安交通大学 | 陕西 | 综合 | 452.5 |
| 12 | 哈尔滨工业大学 | 黑龙江 | 理工 | 450.2 |
| 13 | 北京航空航天大学 | 北京 | 理工 | 445.1 |
| 14 | 北京师范大学 | 北京 | 师范 | 440.9 |
| 15 | 同济大学 | 上海 | 理工 | 439.0 |
| 16 | 四川大学 | 四川 | 综合 | 435.7 |
| 17 | 东南大学 | 江苏 | 综合 | 432.7 |
| 18 | 中国人民大学 | 北京 | 综合 | 409.7 |
| 19 | 南开大学 | 天津 | 综合 | 402.1 |
| 20 | 北京理工大学 | 北京 | 理工 | 395.6 |
| 21 | 天津大学 | 天津 | 理工 | 390.3 |
| 22 | 山东大学 | 山东 | 综合 | 387.9 |
| 23 | 厦门大学 | 福建 | 综合 | 383.3 |
| 24 | 吉林大学 | 吉林 | 综合 | 379.5 |
| 25 | 华南理工大学 | 广东 | 理工 | 379.4 |
| 26 | 中南大学 | 湖南 | 综合 | 378.6 |
| 27 | 大连理工大学 | 辽宁 | 理工 | 365.1 |
| 28 | 西北工业大学 | 陕西 | 理工 | 359.6 |
| 29 | 华东师范大学 | 上海 | 师范 | 358.0 |
| 30 | 中国农业大学 | 北京 | 农业 | 351.5 |
1、实验心得:
原始 HTML 文本中包含大量冗余信息(如注释、空白字符),在具体实践中,我们应该先合理分析具体网页文本,然后剖析其具体特征,再使用requests库发送 HTTP 请求获取页面内容,通过BeautifulSoup解析 HTML 结构,定位并提取目标数据。通过本次实践我掌握了从分析文本到利用request爬取网页文本再用BeautifulSoup解析文本的爬取方法。
作业②:
2、爬取网站商品信息实验
根据实验2的要求,我们选择“当当网”(https://search.dangdang.com)作为爬取的商城网站,我们进入主页,并搜索关键词“书包”,然后读取其网页源码。在其中定位分析发现其关键信息price和name是在 <li ddt-pit 这个标签下的,所以我们可以根据此设计正则表达式精确定位。

完整代码:
代码先利用request提取文本,然后利用re正则表达式匹配文本中的<li字段,然后根据price等关键字匹配价格,商品名,ID等信息,然后进行存储,最后将其转化为excel存储在本地文件夹。
点击查看代码
import requests
import re
import pandas as pd
from pathlib import Path
import time
def get_dangdang_products(keyword="书包", page=1):
"""爬取当当网指定关键词和页码的商品数据"""
url = f"http://search.dangdang.com/?key={keyword}&page_index={page}"
headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Referer": "http://search.dangdang.com/",
"Cookie": "ddscreen=2; __permanent_id=20240520152023782467139532500001647; __visit_id=20240520152023783133139532500002392; __out_refer=1716204023783"
}
try:
print(f"正在爬取第{page}页:{url}")
response = requests.get(url, headers=headers, timeout=10)
response.encoding = "gbk"
if response.status_code != 200:
print(f"第{page}页请求失败,状态码:{response.status_code}")
return []
# 正则逻辑:匹配商品li标签,提取ID、标题、链接、价格、评论数、店铺名
product_pattern = r'''
<li[^>]*?id="(\d+)"[^>]*?> # 商品ID
.*?
<a[^>]*?title="([^"]+)"[^>]*?href="(//product\.dangdang\.com/[^"]+)" # 商品标题和链接
.*?
<span\s+class="price_n">([^<]+)</span> # 售价
.*?
(\d+)条评论 # 评论数
.*?
(?:<a[^>]*?title="([^"]+)"|) # 店铺名称(非捕获组,无则为空)
.*?</li>
'''
pattern = re.compile(product_pattern, re.DOTALL | re.VERBOSE)
matches = pattern.findall(response.text)
product_list = []
for m in matches:
product_list.append({
"商品ID": m[0],
"商品名称": m[1].strip(),
"商品链接": "https:" + m[2] if m[2].startswith("//") else m[2],
"售价": m[3].strip().replace('¥', '¥'),
"评论数": m[4] + "条",
"店铺名称": m[5].strip() if m[5] else "当当自营"
})
print(f"第{page}页爬取成功,获取{len(product_list)}条商品")
return product_list
except Exception as e:
print(f"爬取失败:{str(e)}")
return []
def save_to_excel(products, keyword="书包"):
"""将商品数据保存为Excel文件"""
if not products:
print("\n无爬取到的商品数据")
return
save_path = f"当当网_{keyword}_商品爬取结果.xlsx"
df = pd.DataFrame(products)
try:
df.to_excel(save_path, index=False, engine='openpyxl')
print(f"\n爬取完成!共获取{len(products)}条商品数据")
print(f"结果保存路径:{Path(save_path).absolute()}")
print("\n预览示例:")
for i, p in enumerate(products[:2], 1):
print(f" 第{i}条:{p['商品名称']} | {p['售价']} | {p['店铺名称']}")
except ImportError:
print("\n请先安装openpyxl:pip install openpyxl")
except Exception as e:
print(f"\n保存失败:{str(e)}")
if __name__ == "__main__":
SEARCH_KEYWORD = "书包"
products = get_dangdang_products(SEARCH_KEYWORD, page=1)
save_to_excel(products, SEARCH_KEYWORD)
核心代码解释:
首先匹配商品的<li>标签,通过id="(\d+)"捕获数字格式的商品 ID(第一个捕获组);
用.?非贪婪匹配任意字符(跨换行,因re.DOTALL模式),跳过无关内容;
接着匹配<a>标签,通过title="([^"]+)"捕获商品标题(第二个捕获组),通过href="(//product\.dangdang\.com/[^"]+)"捕获当当网商品详情页链接(第三个捕获组,限定链接域名);
继续跳过无关内容后,通过([^<]+)捕获售价(第四个捕获组,匹配内除<外的所有字符);
再通过(\d+)条评论捕获评论数(第五个捕获组,提取数字部分);
最后处理店铺名:(?:<a[^>]*?title="([^"]+)"|)表示匹配带title属性的<a>标签并捕获店铺名(第六个捕获组),若不存在则返回空(|表示 “或空”,(?:...)为非捕获组,仅用于分组不保存结果);
最终以.?</li>匹配到<li>标签结束,确保完整覆盖一个商品的 HTML 结构。
# 正则逻辑:匹配商品li标签,提取ID、标题、链接、价格、评论数、店铺名
product_pattern = r'''
<li[^>]*?id="(\d+)"[^>]*?> # 商品ID
.*?
<a[^>]*?title="([^"]+)"[^>]*?href="(//product\.dangdang\.com/[^"]+)" # 商品标题和链接
.*?
<span\s+class="price_n">([^<]+)</span> # 售价
.*?
(\d+)条评论 # 评论数
.*?
(?:<a[^>]*?title="([^"]+)"|) # 店铺名称(非捕获组,无则为空)
.*?</li>
'''
pattern = re.compile(product_pattern, re.DOTALL | re.VERBOSE)
matches = pattern.findall(response.text)
代码结果:
| 商品ID | 商品名称 | 商品链接 | 售价 | 评论数 | 店铺名称 |
|---|---|---|---|---|---|
| 11743855795 | 新款儿童书包 男生小学生女童包一到六年级 减负护脊双肩包大容量 | https://product.dangdang.com/11797550288.html | ¥70.50 | 84条 | 当当自营 |
| 1478019908 | 书包男女生 1-6年级大容量新款小学生耐磨透气书包儿童双肩包小学生男女孩双肩书包 | https://product.dangdang.com/1478019908.html | ¥99.80 | 24条 | 当当自营 |
| 11435928562 | 双肩包男商务背包户外旅行休闲男款笔记本电脑包时尚学生书包中学wrj | https://product.dangdang.com/11435928572.html | ¥148.00 | 2条 | 当当自营 |
| 11435885632 | 小学生英伦风书包男6-12岁轻便大容量女童书包儿童双肩包6xn | https://product.dangdang.com/11435885662.html | ¥120.00 | 8条 | 当当自营 |
| 11435928802 | 新款儿童休闲书包轻便透气中小学男生大容量双肩背包7xt | https://product.dangdang.com/11435928862.html | ¥117.00 | 3条 | 当当自营 |
| 11021783589 | 儿童书包一到三年级女小学生男童男孩轻护脊减负女童背包bl6 | https://product.dangdang.com/11021783649.html | ¥149.80 | 5545条 | 当当自营 |
| 29219374 | 桌面速查-中国地图 世界地图 书包版 学生专用版 加厚 尺寸32.4*23.5厘米 地理学习、家庭 | https://product.dangdang.com/29219374.html | ¥1.70 | 6879条 | 当当自营 |
| 29583136 | 中国地图世界地图 桌面速查 书包版 学生专用 地理学习 历史年表 中国地形 世界地形 政区地图折叠地图 学生地理学习 | https://product.dangdang.com/29583136.html | ¥11.70 | 9041条 | 当当自营 |
| 29803120 | 中华古诗文经典诵读(全19册)北大版海淀小红书 套书包含:诵读本14册+导读1册+素养训练1册+北大logo摘抄笔记本1 | https://product.dangdang.com/29803120.html | ¥92.40 | 6692条 | 当当自营 |
| 27849335 | 小世界童书馆 真的要守住一年级的书包:入学前一定要养成的好习惯 | https://product.dangdang.com/27849335.html | ¥22.50 | 2398条 | 当当自营 |
| 29595016 | 桌面速查中国地图+世界地图 学生专用 书包版套装 赠水擦笔 | https://product.dangdang.com/29595016.html | ¥5.90 | 827条 | 当当自营 |
| 27939270 | 花田小学的属鼠班5-小书包里的秘密(全彩美绘注音) | https://product.dangdang.com/27939270.html | ¥14.30 | 3853条 | 当当自营 |
| 29763182 | 【星球款】世界3D凹凸地形立体地图 书包款(16开) 0.29*0.2(米) | https://product.dangdang.com/29763182.html | ¥6.00 | 1条 | 当当自营 |
| 27931548 | 我的小小单词书(走进大自然)全套书包含六大主题,内容涉及吃、穿、住、行、娱乐、休闲等日常生活的方方面面,通过生活场景再现 | https://product.dangdang.com/27931548.html | ¥7.70 | 50条 | 当当自营 |
| 29166914 | 沈石溪画本(新版)·虎女蒲公英 本书包含了《虎女蒲公英》《猎狐》两个短篇动物小说 | https://product.dangdang.com/29166914.html | ¥11.20 | 1410条 | 当当自营 |
| 27908106 | 书包里的秘密 | https://product.dangdang.com/27908106.html | ¥8.70 | 84条 | 当当自营 |
| 29602901 | 我和米粒系列—书包里的猫 | https://product.dangdang.com/29602901.html | ¥9.20 | 19条 | 当当自营 |
| 25298858 | 藏在书包里的玫瑰 | https://product.dangdang.com/25298858.html | ¥28.70 | 14945条 | 当当自营 |
| 23703752 | 书包里的魔法师之二:会飞的滑板(你的书包里藏了什么?快打开来,参加布小丁和萌宠龙猪小七的奇幻之旅!) | https://product.dangdang.com/23703752.html | ¥9.90 | 47条 | 当当自营 |
| 27916184 | 宝葫芦的秘密 百年百部精装典藏版 3-4年级阅读拓展书目,本书包含《大林和小林》 | https://product.dangdang.com/27916184.html | ¥15.60 | 2289条 | 当当自营 |
| 27904076 | 装进书包的秘密 | https://product.dangdang.com/27904076.html | ¥14.00 | 4404条 | 当当自营 |
| 29469502 | 大书包和大力士(一年级二班) | https://product.dangdang.com/29469502.html | ¥10.90 | 45条 | 当当自营 |
| 29469511 | 书包里的电话号码(一年级二班) | https://product.dangdang.com/29469511.html | ¥10.90 | 38条 | 当当自营 |
| 29281360 | 月光岛 少儿科普名人名著·典藏版 本书包含《月光岛》《马小哈奇遇记》两部科幻小说 | https://product.dangdang.com/29281360.html | ¥12.00 | 169条 | 当当自营 |
| 25578550 | 全新修订 中国地图 高清彩印展开0.87米*0.58米 袋装折叠方便携带 学生教室家用商务办公室地图贴图 | https://product.dangdang.com/25578550.html | ¥2.30 | 68130条 | 当当自营 |
| 23600848 | 启发童话小巴士桥梁书(第二辑 ,全5册 )幽默童话故事绘本书 书包去远足 电饭锅参加运动会 冰箱放暑假 吸尘器去钓鱼 暖 | https://product.dangdang.com/23600848.html | ¥42.70 | 29401条 | 当当自营 |
| 25578545 | 全新修订 世界地图 高清彩印展开0.87米*0.58米 袋装折叠方便携带 学生教室家用商务办公室地图贴图 | https://product.dangdang.com/25578545.html | ¥2.20 | 31368条 | 当当自营 |
| 25214227 | 藏在书包里的玫瑰:青春期男孩女孩专属读本,真诚修订第二版 | https://product.dangdang.com/25214227.html | ¥17.10 | 1920条 | 当当自营 |
| 25578549 | 全新修订 中国地图+世界地图 升级版 0.87米*0.58米(袋装 学生教室家用商务办公室地图 袋装) | https://product.dangdang.com/25578549.html | ¥4.30 | 25881条 | 当当自营 |
| 1444157898 | 双肩包女士2018新款韩版百搭潮背包包软皮休闲时尚旅行大容量书包 | https://product.dangdang.com/1444157898.html | ¥69.00 | 1条 | 当当自营 |
| 25578568 | 中国地图 1.06米*0.76米(袋装 学生教室家用商务办公室地图 袋装) | https://product.dangdang.com/25578568.html | ¥4.30 | 4947条 | 当当自营 |
| 25578567 | 全新修订 中国地图+世界地图 升级版 1.06米*0.76米(袋装 学生教室家用商务办公室地图 袋装) | https://product.dangdang.com/25578567.html | ¥8.60 | 44466条 | 当当自营 |
| 23484039 | 宝宝认动物-宝宝认世界 牧场里有鸡、鸭、鹅、牛、羊、猪;沙漠里有跳鼠、蝎子、沙鸡、大耳狐……全书包括几百种动物,赠60个 | https://product.dangdang.com/23484039.html | ¥4.00 | 429条 | 当当自营 |
| 11112328437 | 雷锋的故事注音版彩图正版书包邮儿童版一二三年级小学生课外阅读语文雷峰书籍日记6-7-8-10岁阅读的课外书 | https://product.dangdang.com/11112328437.html | ¥5.50 | 8条 | 当当自营 |
| 25578566 | 世界地图 1.06米*0.76米(袋装 学生教室家用商务办公室地图 袋装) | https://product.dangdang.com/25578566.html | ¥4.30 | 4120条 | 当当自营 |
| 23910809 | 亲子旅行科普绘本(3-6岁)·小小背包客游北京 | https://product.dangdang.com/23910809.html | ¥5.00 | 91条 | 当当自营 |
| 29575178 | 谜语(宝宝咿呀学说话)大图大字我爱读系列彩图注音版儿童睡前故事书3-6岁幼儿早教语言启蒙绘本一二年级课外阅读拼音睡前读物 | https://product.dangdang.com/29575178.html | ¥6.70 | 184条 | 当当自营 |
| 27888659 | 墨点英文字帖小学生英语提高卷面分意大利斜体英语单词字母描红本 | https://product.dangdang.com/27888659.html | ¥6.30 | 3671条 | 当当自营 |
| 29741454 | 好的孤独(畅销书作家陈果代表作《好的孤独》新版归来,内容全新升级,陈果亲笔校订,增加全新人生感悟) | https://product.dangdang.com/29741454.html | ¥27.80 | 18976条 | 当当自营 |
| 28995809 | 欧洲民间故事:聪明的牧羊人 五年级上册课外阅读(中小学生课外阅读指导丛书)无障碍阅读 快乐读书吧5上适合小学生课外阅读书 | https://product.dangdang.com/28995809.html | ¥7.59 | 383条 | 当当自营 |
| 27938231 | 共和国70年儿童文学短篇精选集·永远天真,永远爱(平装) | https://product.dangdang.com/27938231.html | ¥7.70 | 447条 | 当当自营 |
| 29348507 | 彷徨(鲁迅作品 单行本) | https://product.dangdang.com/29348507.html | ¥13.20 | 4678条 | 当当自营 |
| 11861801541 | 预售7.30 百年百部新版 正版书包邮 天使的花房 吴然 著 长江少年儿童出版社 中国儿童文学经典书系中小学生的散文集青 | https://product.dangdang.com/11861801541.html | ¥7.98 | 7条 | 当当自营 |
| 29890138 | 中华人民共和国民营经济促进法(实用版) | https://product.dangdang.com/29890138.html | ¥7.60 | 19条 | 当当自营 |
| 11415826494 | 有声伴读汤姆叔叔的小屋 斯托夫人正版书包邮原著世界文学名著五六年级小学生课外阅读 汤姆大叔的小屋新 | https://product.dangdang.com/11415826494.html | ¥8.00 | 1条 | 当当自营 |
| 11415971254 | 雾都孤儿 正版书包邮狄更斯五六年级小学生课外阅读 班主任书目人民文学全集 世界名著小说辽海出版社ys | https://product.dangdang.com/11415971254.html | ¥8.00 | 8条 | 当当自营 |
| 11415819574 | 格列佛游记正版书包邮原版 乔纳森 世界文学名著 小学生初中生版课外阅读 四五六年级九年级语文 格列夫游记ys | https://product.dangdang.com/11415819574.html | ¥8.00 | 1条 | 当当自营 |
| 11981417870 | 佛说雨宝陀罗尼经简体注音版弘化常诵佛经读诵本经书结缘书包邮 任选备注 | https://product.dangdang.com/11981417870.html | ¥5.00 | 1条 | 当当自营 |
| 11415813804 | 大卫.科波菲尔正版书包邮狄更斯原著高中生课外 世界文学名著中文版中小学生课外 ys | https://product.dangdang.com/11415813804.html | ¥8.00 | 4条 | 当当自营 |
| 22932228 | 一个背包客的光影六城记 | https://product.dangdang.com/22932228.html | ¥8.60 | 96条 | 当当自营 |
| 27888655 | 墨点字帖 提高卷面分正楷初中语文中考提分模拟楷书描红字帖精选中考试题练字 | https://product.dangdang.com/27888655.html | ¥6.30 | 1910条 | 当当自营 |
| 11134460676 | 书包里的秘密 李迪 著 一个关于奉献与坚守的故事 《枪从背后打来》的最新演绎;经典再现 作家出版社 | https://product.dangdang.com/11134460676.html | ¥8.80 | 1条 | 当当自营 |
2、心得体会:
在这个实验中我们通过requests库模拟浏览器发送 HTTP 请求,获取目标网页的 HTML 内容接着使用正则表达式匹配 HTML 中的商品信息(ID、名称、价格等),然后对数据进行存储,然后借用pandas将数据存储入excel文件方便阅读。
作业③
3、爬取福州大学新闻网照片实验
首先第一步我们依旧打开网站阅读源码,我们可以轻松找到图片的地址,分析发现通过<div class=”img slow”>和<img src可以精准定位,同时提取地址时我们发现jpg和png格式同样存在,所以我们在正则表达式中添加|进行筛选。
完整代码:
依旧设计利用request对网站进行文本爬取,然后根据其特征设计正则表达式,然后提取照片地址,然后在本地进行下载保存。另外在提取所有jpg和png格式的图片时,我们发现出现一些箭头表情图片,是一些认为无用的图片,我希望能够提取一个页面中所有的视频封面,因此通过观察,我们通过第二次正则表达式进行了一次过滤。
点击查看代码
import urllib.request
import re
import os
import time
from urllib.parse import urljoin
def download_images(url, save_dir='fzu_images'):
# 创建保存图片的目录
if not os.path.exists(save_dir):
os.makedirs(save_dir)
try:
# 添加请求头,模拟浏览器访问
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
req = urllib.request.Request(url, headers=headers)
# 发送请求获取页面内容
print(f"正在获取页面内容: {url}")
with urllib.request.urlopen(req) as response:
html = response.read().decode('utf-8', errors='ignore')
# 匹配包含图片和标题的完整<li>元素
li_pattern = r'<li>\s*<a[^>]+title="[^"]+">\s*<div class="img slow">\s*<img src="(/__local/.*?\.(jpg|png))"[^>]*>\s*</div>\s*<div class="con">\s*<h5[^>]+>[^<]+</h5>\s*</div>\s*</a>\s*</li>'
img_matches = re.findall(li_pattern, html, re.DOTALL | re.IGNORECASE)
# 提取图片路径
img_srcs = [match[0] for match in img_matches]
if not img_srcs:
print("未找到符合条件的图片")
return
# 去重处理
img_srcs = list(set(img_srcs))
print(f"找到 {len(img_srcs)} 张符合条件的图片,准备下载...")
# 下载每张图片
for i, src in enumerate(img_srcs):
# 构建完整的图片URL
img_url = urljoin(url, src)
# 提取文件名并处理可能的参数
file_name = src.split('/')[-1].split('?')[0]
save_path = os.path.join(save_dir, file_name)
# 调整过滤规则:只排除特定模式的图片
# 分析不需要的图片,它们的文件名格式是:[短字符集]_[短字符集]_[3位数字].png
if re.search(r'^[0-9A-F]{16,20}_[0-9A-F]{8}_\d{3}\.png$', file_name, re.IGNORECASE):
print(f"跳过不需要的图片: {file_name}")
continue
try:
print(f"正在下载 {i + 1}/{len(img_srcs)}: {file_name}")
# 下载图片时也添加请求头
img_req = urllib.request.Request(img_url, headers=headers)
with urllib.request.urlopen(img_req) as img_response, open(save_path, 'wb') as f:
f.write(img_response.read())
print(f"下载成功: {file_name}")
# 适当延迟,避免给服务器造成过大压力
time.sleep(1)
except Exception as e:
print(f"下载失败 {file_name}: {str(e)}")
except Exception as e:
print(f"获取页面失败: {str(e)}")
if __name__ == "__main__":
# 目标页面URL
target_url = "https://news.fzu.edu.cn/yxfd.htm"
download_images(target_url)
print("爬取完成!")
核心代码解释:
以<li>标签为匹配起点,依次匹配内部带title属性的<a>标签、类名为img slow的图片容器<div>,在<img>标签的src属性中,通过捕获组锁定以/__local/开头、格式为jpg或png的图片链接,接着匹配类名为con的标题容器<div>及内部的<h5>标题标签,最终以</a>和</li>标签结束匹配
# 匹配包含图片和标题的完整<li>元素
li_pattern = r'<li>\s*<a[^>]+title="[^"]+">\s*<div class="img slow">\s*<img src="(/__local/.*?\.(jpg|png))"[^>]*>\s*</div>\s*<div class="con">\s*<h5[^>]+>[^<]+</h5>\s*</div>\s*</a>\s*</li>'
img_matches = re.findall(li_pattern, html, re.DOTALL | re.IGNORECASE)
正则表达式 r'^[0-9A-F]{16,20}_[0-9A-F]{8}_\d{3}.png$' 用于匹配文件名,规则是:文件名需以 16-20 位的数字或大写字母(A-F)开头,接着是一个下划线,然后是 8 位数字或大写字母(A-F),再跟一个下划线,之后是 3 位数字,用于筛选并跳过符合特定命名格式的 “不需要的图片”
# 分析不需要的图片,它们的文件名格式是:[短字符集]_[短字符集]_[3位数字].png
if re.search(r'^[0-9A-F]{16,20}_[0-9A-F]{8}_\d{3}\.png$', file_name, re.IGNORECASE):
print(f"跳过不需要的图片: {file_name}")
continue
运行结果:
这是爬取的所有的照片的合集,我将他们汇总成一张图。

3、实验心得:
通过本次实验,我熟练了request的提取和如何使用re正则表达式筛选自己想要的结果,这是一次收获颇丰的实验。

浙公网安备 33010602011771号