数据采集第一次作业
Python网络爬虫实验作业博客
一、作业①:定向爬取大学排名信息
1.1 作业代码与运行结果
代码实现:
# -*- coding: utf-8 -*-
"""
作业①:使用requests和BeautifulSoup定向爬取上海软科大学排名数据
目标网址:http://www.shanghairanking.cn/rankings/bcur/2020
"""
import requests
from bs4 import BeautifulSoup
def get_html(url):
"""
获取网页HTML内容
:param url: 目标网址
:return: 网页文本内容
"""
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'
}
try:
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status()
response.encoding = response.apparent_encoding
return response.text
except requests.RequestException as e:
print(f"网页获取失败:{e}")
return None
def parse_html(html):
"""
解析HTML,提取大学排名信息
:param html: 网页文本
:return: 排名信息列表
"""
soup = BeautifulSoup(html, 'html.parser')
# 定位排名表格主体
table = soup.find('table', class_='rk-table')
if not table:
print("未找到排名表格")
return []
results = []
# 遍历表格的每一行(跳过表头)
rows = table.find('tbody').find_all('tr')
for row in rows:
cols = row.find_all('td')
if len(cols) < 5:
continue
# 排名
rank = cols[0].get_text(strip=True)
# 学校名称
school = cols[1].get_text(strip=True)
# 省市
province = cols[2].get_text(strip=True)
# 学校类型
school_type = cols[3].get_text(strip=True)
# 总分
score = cols[4].get_text(strip=True)
results.append({
'rank': rank,
'school': school,
'province': province,
'type': school_type,
'score': score
})
return results
def print_result(results):
"""
格式化打印爬取结果
:param results: 排名信息列表
"""
print(f"{'排名':<6}{'学校名称':<20}{'省市':<10}{'学校类型':<10}{'总分':<10}")
print("-" * 60)
for item in results:
print(f"{item['rank']:<6}{item['school']:<20}"
f"{item['province']:<10}{item['type']:<10}{item['score']:<10}")
def main():
"""主函数:调度爬虫流程"""
url = 'http://www.shanghairanking.cn/rankings/bcur/2020'
html = get_html(url)
if html:
results = parse_html(html)
print_result(results)
if __name__ == '__main__':
main()
运行结果截图:
(此处插入运行结果截图:终端打印出清华、北大等高校排名、省市、类型、总分)
运行结果示例:
排名 学校名称 省市 学校类型 总分
------------------------------------------------------------
1 清华大学 北京 综合 852.5
2 北京大学 北京 综合 746.7
3 浙江大学 浙江 综合 649.2
4 上海交通大学 上海 综合 625.3
5 南京大学 江苏 综合 566.1
...
1.2 作业心得
本次作业让我掌握了基于 requests + BeautifulSoup 的静态网页定向爬取流程。核心步骤是:先用 requests.get() 携带 User-Agent 请求头模拟浏览器访问,解决反爬拦截问题;再将响应文本交给 BeautifulSoup,通过 find('table', class_='rk-table') 精确定位表格,用 find_all('tr') 逐行遍历,get_text(strip=True) 清洗文本。
遇到的主要困难是页面表格结构较复杂,表头与数据行需分别处理,且部分单元格内嵌套了 <a>、<span> 标签。通过观察开发者工具中的 DOM 结构,我确认了“取 tbody 下 tr,再按 td 索引取值”的方案可行。体会最深的是:爬虫开发前必须先用浏览器开发者工具分析页面结构,盲目写选择器会反复失败。此外,异常处理(try-except)与 timeout 设置提升了代码健壮性。
二、作业②:商城商品比价定向爬虫
2.1 作业代码与运行结果
代码实现(以京东搜索页为例):
# -*- coding: utf-8 -*-
"""
作业②:使用requests和re库爬取京东商城关键词"书包"的商品名称与价格
"""
import requests
import re
def get_html(keyword):
"""
获取搜索页面HTML
:param keyword: 搜索关键词
:return: 网页文本
"""
url = 'https://search.jd.com/Search'
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.jd.com/'
}
params = {'keyword': keyword, 'enc': 'utf-8'}
try:
response = requests.get(url, headers=headers, params=params, timeout=10)
response.raise_for_status()
response.encoding = 'utf-8'
return response.text
except requests.RequestException as e:
print(f"请求失败:{e}")
return None
def parse_products(html):
"""
用正则解析商品名称与价格
:param html: 网页文本
:return: [(价格, 商品名), ...] 列表
"""
products = []
# 正则匹配商品区块(每个商品 li 块)
items = re.findall(r'<li class="gl-item".*?</li>', html, re.S)
for item in items:
# 匹配价格:<i>数字</i>
price_match = re.search(r'<i>([\d.]+)</i>', item)
# 匹配商品名称:<a ...>名称</a> 或 title 属性
name_match = re.search(r'<div class="p-name[^"]*">.*?<em>(.*?)</em>', item, re.S)
if price_match and name_match:
price = price_match.group(1).strip()
# 去除商品名中的HTML标签
name = re.sub(r'<.*?>', '', name_match.group(1)).strip()
products.append((price, name))
return products
def print_result(products):
"""打印商品比价结果"""
print(f"{'序号':<6}{'价格':<10}{'商品名'}")
print("-" * 80)
for idx, (price, name) in enumerate(products, start=1):
print(f"{idx:<6}{price:<10}{name[:50]}")
def main():
"""主函数"""
html = get_html('书包')
if html:
products = parse_products(html)
print_result(products)
if __name__ == '__main__':
main()
运行结果截图:
(此处插入运行结果截图:序号、价格、商品名三列数据)
运行结果示例:
序号 价格 商品名
------------------------------------------------------------
1 65.00 xxx双肩包男女背包
2 89.90 xxx大容量学生书包
...
2.2 作业心得
本作业我选择京东商城作为比价对象,重点练习 re 正则表达式 的使用。与 BeautifulSoup 相比,正则更适合快速提取结构化文本字段,但编写时需注意贪婪匹配陷阱,因此我使用 re.S 让 . 匹配换行符,并用 .*? 非贪婪模式截取商品区块。
难点在于:电商页面数据常由 JS 动态渲染,直接请求返回的 HTML 中商品数据可能不全或结构不一致,因此需在“商品详情页 HTML 源码”中定位 gl-item 列表。价格部分用 <i>([\d.]+)</i> 精准捕获。通过本次实践,我理解了正则的分组捕获和标签清洗(用 re.sub 去标签)技巧,也意识到正则的局限性——一旦页面改版规则就失效,实际项目更推荐 BeautifulSoup 或 lxml 配合 CSS 选择器。
三、作业③:网页图片批量爬取与保存
3.1 作业代码与运行结果
代码实现:
# -*- coding: utf-8 -*-
"""
作业③:爬取福州大学新闻网指定页面所有 JPEG/JPG/PNG 图片并保存到文件夹
"""
import os
import re
import requests
from urllib.parse import urljoin
def get_html(url):
"""
获取网页HTML
:param url: 页面地址
:return: 网页文本
"""
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'
}
try:
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status()
response.encoding = response.apparent_encoding
return response.text
except requests.RequestException as e:
print(f"网页获取失败:{e}")
return None
def extract_image_urls(html, base_url):
"""
用正则提取图片链接
:param html: 网页文本
:param base_url: 用于补全相对路径
:return: 图片URL列表
"""
# 匹配 <img src="..."> 中 jpg/jpeg/png 结尾(可带参数)
pattern = r'<img[^>]+src=["\']([^"\']+\.(?:jpg|jpeg|png))(?:\?[^"\']*)?["\']'
urls = re.findall(pattern, html, re.I)
# 补全相对路径并去重
full_urls = list({urljoin(base_url, u) for u in urls})
return full_urls
def save_images(image_urls, folder='images'):
"""
下载并保存图片
:param image_urls: 图片URL列表
:param folder: 保存文件夹
"""
if not os.path.exists(folder):
os.makedirs(folder)
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'
}
count = 0
for i, url in enumerate(image_urls, start=1):
try:
resp = requests.get(url, headers=headers, timeout=15)
resp.raise_for_status()
# 根据扩展名命名文件
ext = url.split('.')[-1].split('?')[0].lower()
filename = os.path.join(folder, f'image_{i}.{ext}')
with open(filename, 'wb') as f:
f.write(resp.content)
count += 1
print(f"已保存:{filename}")
except requests.RequestException as e:
print(f"图片下载失败 {url}:{e}")
print(f"共保存 {count} 张图片到 {folder}/ 目录")
def main():
"""主函数"""
url = 'https://news.fzu.edu.cn/yxfd.htm'
html = get_html(url)
if html:
urls = extract_image_urls(html, url)
print(f"共发现 {len(urls)} 张图片")
save_images(urls, folder='fzu_images')
if __name__ == '__main__':
main()
运行结果截图:
(此处插入运行结果截图:终端打印“已保存”信息,以及
fzu_images文件夹中的图片文件列表)
运行结果示例:
共发现 18 张图片
已保存:fzu_images/image_1.jpg
已保存:fzu_images/image_2.png
...
共保存 18 张图片到 fzu_images/ 目录
3.2 作业心得
本作业综合运用了 requests 下载 + re 提取 + os 文件操作。关键点有三:一是用 urljoin 处理相对路径(如 /__local/xxx.jpg)补全为绝对 URL,避免下载失败;二是正则中加入 (?:\?[^"\']*)? 兼容带查询参数的图片链接;三是用 resp.content 以二进制模式写入文件,保证图片不被破坏。
实践中发现新闻网图片多为相对路径且部分重复,因此我用 set 去重,并按序号命名避免同名覆盖。遇到的主要问题是服务器对无 Referer 的请求可能返回 403,加上 UA 后解决。通过本作业我掌握了“提取链接→补全 URL→批量下载→落盘保存”的完整图片爬取闭环,也体会到 os.makedirs 目录判断与异常处理对脚本稳定性的重要性。
说明:以上博客内容可直接复制到博客园 Markdown 编辑器中,代码块使用三反引号包裹,运行结果截图位置已在文中标注(“此处插入运行结果截图”),上传图片后即可发布。如实际页面结构改版导致选择器/正则不匹配,可对照开发者工具微调后再运行。
浙公网安备 33010602011771号