第一次作业
• 作业①:
o 要求:用requests和BeautifulSoup库方法定向爬取给定网址(http://www.shanghairanking.cn/rankings/bcur/2020)的数据,屏幕打印爬取
o 的大学排名信息。
o 输出信息:
排名 学校名称 省市 学校类型 总分``
1 清华大学 北京 综合 852.5
2......
代码`import requests
from bs4 import BeautifulSoup
def crawl_university_ranking(url):
"""
爬取软科中国大学排名数据
Args:
url: 目标网址
"""
try:
# 1. 发送HTTP请求
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status()
# 2. 解析HTML内容
soup = BeautifulSoup(response.content, 'html.parser')
# 3. 查找排名表格
table = soup.find('tbody')
if not table:
print("未找到排名表格")
return
rows = table.find_all('tr')
# 4. 打印表头
print(f"{'排名':<8}{'学校名称':<20}{'省市':<10}{'学校类型':<12}{'总分':<8}")
print("-" * 60)
# 5. 提取并打印每行数据
for row in rows:
columns = row.find_all('td')
if len(columns) >= 5:
# 提取排名
rank = columns[0].get_text(strip=True)
# 提取学校名称(只取中文名)
name_cell = columns[1]
# 方法1:直接提取中文名称(根据网页结构)
# 中文名通常在a标签内,且有class='name-cn'
name_element = name_cell.find('a', class_='name-cn')
# 方法2:如果找不到,则获取整个文本并分割
if name_element:
name = name_element.get_text(strip=True)
else:
# 获取所有文本,然后取第一行(中文名)
all_text = name_cell.get_text('\n', strip=True)
name = all_text.split('\n')[0] if '\n' in all_text else all_text
# 提取省市
province = columns[2].get_text(strip=True)
# 提取学校类型
school_type = columns[3].get_text(strip=True)
# 提取总分
total_score = columns[4].get_text(strip=True)
# 打印格式化后的数据
print(f"{rank:<8}{name:<20}{province:<10}{school_type:<12}{total_score:<8}")
except requests.exceptions.RequestException as e:
print(f"网络请求失败: {e}")
except Exception as e:
print(f"解析数据时出错: {e}")
if name == "main":
# 目标网址
url = "http://www.shanghairanking.cn/rankings/bcur/2020"
# 执行爬取
crawl_university_ranking(url)
`
结果
排名 学校名称 省市 学校类型 总分
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
• 作业②:
o 要求:用requests和re库方法设计某个商城(自已选择)商品比价定向爬虫,爬取该商城,以关键词“书包”搜索页面的数据,爬取商品名称和价格。
o 输出信息:
序号 价格 商品名
1 65.00 xxx
2......
代码`import requests
import re
import time
def jd_price_spider(keyword="书包", pages=2):
"""
京东商城书包比价爬虫
Args:
keyword: 搜索关键词,默认为"书包"
pages: 爬取页数,默认为2页
"""
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',
'Referer': 'https://www.jd.com/'
}
all_products = []
product_count = 0
print(f"{'序号':<6}{'价格':<10}{'商品名':<50}")
print("-" * 70)
# 京东搜索URL模板
for page in range(1, pages + 1):
try:
# 京东搜索页URL(通过搜索接口)
url = f"https://search.jd.com/Search?keyword={keyword}&page={page}"
response = requests.get(url, headers=headers, timeout=10)
response.encoding = 'utf-8'
if response.status_code != 200:
print(f"第{page}页请求失败,状态码:{response.status_code}")
continue
html_content = response.text
# 使用正则表达式提取商品信息
# 匹配商品价格 - 京东价格通常在 class="p-price" 的标签中
price_pattern = r'<div class="p-price">.*?<i>(.*?)</i>.*?</div>'
# 匹配商品名称 - 通常在 class="p-name" 的标签中
name_pattern = r'<div class="p-name".*?<em>(.*?)</em>.*?</div>'
prices = re.findall(price_pattern, html_content, re.S)
names = re.findall(name_pattern, html_content, re.S)
# 清洗数据
cleaned_names = []
for name in names:
# 移除HTML标签和多余空格
name = re.sub(r'<.*?>', '', name)
name = re.sub(r'\s+', ' ', name).strip()
cleaned_names.append(name)
# 匹配价格和商品名称
min_len = min(len(prices), len(cleaned_names))
for i in range(min_len):
product_count += 1
price = prices[i].strip()
name = cleaned_names[i]
# 输出结果
print(f"{product_count:<6}{price:<10}{name[:48]:<50}")
all_products.append({"序号": product_count, "价格": price, "商品名": name})
print(f"第{page}页爬取完成,获取到{min_len}个商品")
# 避免请求过快
time.sleep(1)
except requests.exceptions.RequestException as e:
print(f"第{page}页请求出错: {e}")
continue
except Exception as e:
print(f"第{page}页解析出错: {e}")
continue
# 统计信息
print("\n" + "=" * 70)
print(f"爬取完成!共获取 {product_count} 个商品")
return all_products
def alternative_spider():
"""
备用方案:使用更简单的正则匹配模式
"""
print("尝试使用备用爬取方案...")
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
}
url = "https://search.jd.com/Search?keyword=书包"
try:
response = requests.get(url, headers=headers, timeout=10)
response.encoding = 'utf-8'
# 更通用的正则表达式匹配模式
product_pattern = r'<div class="gl-i-wrap".*?<div class="p-price">.*?<i>(.*?)</i>.*?<div class="p-name".*?<em>(.*?)</em>.*?</div>'
products = re.findall(product_pattern, response.text, re.S)
print(f"{'序号':<6}{'价格':<10}{'商品名':<50}")
print("-" * 70)
for idx, (price, name) in enumerate(products[:20], 1): # 只显示前20个
# 清洗商品名
name = re.sub(r'<.*?>', '', name)
name = re.sub(r'\s+', ' ', name).strip()
print(f"{idx:<6}{price.strip():<10}{name[:48]:<50}")
print(f"\n共找到 {len(products)} 个商品")
except Exception as e:
print(f"备用方案也失败了: {e}")
print("这可能是因为:")
print("1. 网站结构发生了变化")
print("2. 需要更新正则表达式模式")
print("3. 需要处理反爬虫机制")
def main():
"""
主函数
"""
print("京东商城书包比价爬虫")
print("=" * 70)
# 方法1:主要爬虫
products = jd_price_spider(keyword="书包", pages=2)
# 如果主要爬虫没有获取到数据,尝试备用方案
if not products:
print("\n主要方案未能获取数据,尝试备用方案...")
alternative_spider()
if name == "main":
main()`
结果
京东商城书包比价爬虫
序号 价格 商品名
第1页爬取完成,获取到0个商品
第2页爬取完成,获取到0个商品
======================================================================
爬取完成!共获取 0 个商品
• 作业③:
o 要求:爬取一个给定网页(https://news.fzu.edu.cn/yxfd.htm)或者自选网页的所有JPEG、JPG或PNG格式图片文件
o 输出信息:将自选网页内的所有JPEG、JPG或PNG格式文件保存在一个文件夹中
代码`import requests
from bs4 import BeautifulSoup
import os
import re
from urllib.parse import urljoin, urlparse
def download_images_from_url(url, save_folder='F:/采集'):
"""
从指定网页下载所有JPEG、JPG、PNG格式的图片到F:/采集文件夹
Args:
url: 目标网页地址
save_folder: 保存图片的文件夹路径,默认为F:/采集
"""
# 创建保存图片的文件夹(完整路径)
if not os.path.exists(save_folder):
os.makedirs(save_folder)
print(f"创建文件夹: {save_folder}")
else:
print(f"使用现有文件夹: {save_folder}")
# 设置请求头,模拟浏览器访问
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'
}
downloaded_count = 0
visited_urls = set() # 用于记录已访问的URL,避免重复
def download_from_page(page_url, is_main_page=True):
"""从单个页面下载图片"""
nonlocal downloaded_count
if page_url in visited_urls:
return
visited_urls.add(page_url)
try:
print(f"\n访问页面: {page_url}")
response = requests.get(page_url, headers=headers, timeout=10)
response.encoding = 'utf-8' # 设置编码
if response.status_code != 200:
print(f" 页面访问失败,状态码: {response.status_code}")
return
soup = BeautifulSoup(response.text, 'html.parser')
# 在当前页面查找所有图片链接
img_tags = soup.find_all('img')
for img in img_tags:
# 获取图片URL
img_url = img.get('src') or img.get('data-src')
if not img_url:
continue
# 处理相对URL
img_url = urljoin(page_url, img_url)
# 检查图片格式
img_url_lower = img_url.lower()
if not (img_url_lower.endswith('.jpg') or
img_url_lower.endswith('.jpeg') or
img_url_lower.endswith('.png') or
'jpg' in img_url_lower or
'jpeg' in img_url_lower or
'png' in img_url_lower):
continue
# 下载图片
try:
img_response = requests.get(img_url, headers=headers, timeout=5)
# 检查是否是有效的图片响应
if 'image' not in img_response.headers.get('content-type', '').lower():
continue
img_data = img_response.content
# 从URL提取图片文件名
img_name = os.path.basename(urlparse(img_url).path)
if not img_name or img_name == '/':
# 如果没有文件名,使用时间戳
import time
img_name = f"image_{int(time.time())}_{downloaded_count + 1}.jpg"
# 确保文件名有正确的扩展名
content_type = img_response.headers.get('content-type', '').lower()
if '.jpg' not in img_name.lower() and '.jpeg' not in img_name.lower() and '.png' not in img_name.lower():
if 'jpeg' in content_type or 'jpg' in content_type:
img_name = f"{img_name}.jpg" if not img_name.endswith('.jpg') else img_name
elif 'png' in content_type:
img_name = f"{img_name}.png" if not img_name.endswith('.png') else img_name
else:
img_name = f"{img_name}.jpg"
# 清理文件名中的非法字符
img_name = re.sub(r'[<>:"/\\|?*]', '_', img_name)
# 保存图片到指定路径
save_path = os.path.join(save_folder, img_name)
# 如果文件名已存在,添加数字后缀
base_name, ext = os.path.splitext(img_name)
counter = 1
while os.path.exists(save_path):
save_path = os.path.join(save_folder, f"{base_name}_{counter}{ext}")
counter += 1
with open(save_path, 'wb') as f:
f.write(img_data)
downloaded_count += 1
print(f" 下载图片 {downloaded_count}: {os.path.basename(save_path)}")
except Exception as img_e:
continue
# 如果是主页面,查找并进入子页面
if is_main_page:
# 查找所有可能包含图片的新闻链接
link_tags = soup.find_all('a', href=True)
news_links = []
for link in link_tags:
href = link['href']
# 筛选出可能是新闻文章的链接
if href and not href.startswith(('#', 'javascript:', 'mailto:')):
full_url = urljoin(page_url, href)
# 筛选条件:确保是同一网站的链接
if 'news.fzu.edu.cn' in full_url and full_url not in visited_urls:
# 排除一些非文章链接
if 'html' in full_url.lower() or 'htm' in full_url.lower():
news_links.append(full_url)
# 限制进入的子页面数量,避免请求过多
for news_url in news_links[:15]: # 增加子页面数量
download_from_page(news_url, is_main_page=False)
except Exception as e:
print(f" 处理页面时出错: {e}")
# 开始下载
print("=" * 60)
print(f"开始从 {url} 下载图片")
print(f"保存路径: {save_folder}")
print("=" * 60)
download_from_page(url, is_main_page=True)
print("\n" + "=" * 60)
print(f"下载完成! 共下载 {downloaded_count} 张图片")
print(f"图片保存位置: {os.path.abspath(save_folder)}")
# 显示下载的图片列表
if downloaded_count > 0:
print("\n下载的图片文件:")
image_files = [f for f in os.listdir(save_folder) if f.lower().endswith(('.jpg', '.jpeg', '.png'))]
for i, img_file in enumerate(image_files[:10], 1): # 显示前10个文件
print(f" {i}. {img_file}")
if len(image_files) > 10:
print(f" ... 以及 {len(image_files) - 10} 个其他文件")
print("=" * 60)
return downloaded_count
def check_disk_space():
"""检查F盘空间"""
try:
import shutil
total, used, free = shutil.disk_usage("F:/")
print(f"F盘空间信息:")
print(f" 总空间: {total // (2 ** 30):.1f} GB")
print(f" 已使用: {used // (2 ** 30):.1f} GB")
print(f" 可用空间: {free // (2 ** 30):.1f} GB")
return free
except Exception as e:
print(f"无法获取F盘空间信息: {e}")
return None
使用示例
if name == "main":
# 指定要爬取的网页URL
target_url = "https://news.fzu.edu.cn/yxfd.htm"
# 指定保存路径为F盘的采集文件夹
save_path = "F:/采集/1.3图片"
# 检查磁盘空间
free_space = check_disk_space()
if free_space is not None and free_space < 100 * 1024 * 1024: # 小于100MB
print("警告: F盘可用空间不足100MB!")
response = input("是否继续? (y/n): ")
if response.lower() != 'y':
print("程序退出")
exit()
# 调用函数下载图片
try:
download_images_from_url(target_url, save_folder=save_path)
except Exception as e:
print(f"程序运行出错: {e}")
print("可能的解决方案:")
print("1. 检查F盘是否存在")
print("2. 检查是否有写入F盘的权限")
`
结果
开始从 https://news.fzu.edu.cn/yxfd.htm 下载图片
保存路径: F:/采集/1.3图片
访问页面: https://news.fzu.edu.cn/yxfd.htm
下载图片 1: top_search.png
下载图片 2: logo.png
下载图片 3: footer_logo.png
下载图片 4: icon_bnt_back2.png
下载图片 5: bnt_sub_nav2.png
下载图片 6: n_left_ico1.png


浙公网安备 33010602011771号