数据采集第一次作业
网络爬虫第一次作业:requests + BeautifulSoup / re 定向爬虫
实验环境:Windows + Python 3.x,第三方库 requests、beautifulsoup4、lxml。
本次作业共三个小作业,分别练习 BeautifulSoup 解析网页表格、正则表达式提取商城商品信息、以及批量下载网页图片。
作业① 爬取软科 2020 中国大学排名
1)大学排名爬虫实验
实验目标:用 requests 库请求网页,用 BeautifulSoup 库解析 HTML,定向爬取软科 2020 中国大学排名(http://www.shanghairanking.cn/rankings/bcur/2020 ),输出排名、学校名称、省市、学校类型、总分。
爬取前检查网页结构:
先用 Chrome 打开目标网址,按 F12 打开开发者工具,切到 Elements(元素)面板,点左上角箭头,再用鼠标去点网页上的排名表格。可以看到整个表格在 <table class="rk-table"> 下的 <tbody> 里,每一行是一个 <tr>,每行里的 <td> 依次对应排名、学校名称、省市、学校类型、总分。其中学校名称那一格里混有英文名和"双一流/985/211"标签,真正的中文校名单独包在 <span class="name-cn"> 里,所以取校名时要单独定位这个标签。


完整代码:
# -*- coding: utf-8 -*-
"""
作业①:用 requests + BeautifulSoup 定向爬取软科 2020 中国大学排名
目标网址:http://www.shanghairanking.cn/rankings/bcur/2020
输出:排名 / 学校名称 / 省市 / 学校类型 / 总分
"""
import requests
from bs4 import BeautifulSoup
# 目标网址
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/124.0.0.0 Safari/537.36"
}
def crawl_rank():
"""爬取大学排名并打印"""
# 1. 发起请求获取网页 HTML
resp = requests.get(URL, headers=HEADERS, timeout=20)
# 注意:该站响应头错误声明为 ISO-8859-1,手动按 utf-8 解码,否则中文乱码
resp.encoding = "utf-8"
# 2. 用 BeautifulSoup 解析 HTML
soup = BeautifulSoup(resp.text, "lxml")
# 3. 定位表格中的每一行 <tr>
rows = soup.select("table tr")
# 4. 打印表头
print("{:<6}{:<12}{:<8}{:<8}{:<8}".format("排名", "学校名称", "省市", "学校类型", "总分"))
count = 0
for tr in rows:
tds = tr.find_all("td")
if len(tds) < 5: # 跳过没有数据的表头行
continue
rank = tds[0].get_text(strip=True) # 排名
# 学校名称:tds[1] 里混有英文名和标签,只取中文名 span.name-cn
name_tag = tds[1].find("span", class_="name-cn")
school = name_tag.get_text(strip=True) if name_tag else tds[1].get_text(strip=True)
province = tds[2].get_text(strip=True) # 省市
school_type = tds[3].get_text(strip=True) # 学校类型
score = tds[4].get_text(strip=True) # 总分
print("{:<8}{:<14}{:<8}{:<8}{:<8}".format(rank, school, province, school_type, score))
count += 1
print("-" * 40)
print("共爬取到 %d 所大学信息" % count)
if __name__ == "__main__":
crawl_rank()
运行结果:

2)心得体会
这次实验最大的收获是学会了"先看网页结构,再写代码"。我一开始直接写好 requests + BeautifulSoup 就运行,结果打印出来一堆乱码,后来才发现是这个网站响应头把编码错标成了 ISO-8859-1,必须手动指定 resp.encoding = "utf-8" 才行。另一个坑是学校名称那一格,网页上看着是"清华大学",但源码里其实还混着英文校名和标签文字,如果直接整格取文本会把它们一起带出来,所以要精确定位到 <span class="name-cn"> 这个中文标签。这让我明白,写爬虫前用 F12 在浏览器里点选目标数据、看清它在哪个标签里,是最关键的一步。
作业② 当当网"书包"商品比价爬虫
1)当当商品比价爬虫实验
实验目标:用 requests 库请求网页,用 re 库(正则表达式)从 HTML 中提取数据,自选一个商城,以关键词"书包"搜索,爬取商品名称和价格。
商城选择:我选的是当当网(search.dangdang.com)。京东的价格是 JS 动态加载的、网页源码里看不到,淘宝又要登录反爬严格,而当当的搜索结果页是静态 HTML,价格和商品名直接写在源码里,最适合用正则提取。
爬取前检查网页结构:
打开 http://search.dangdang.com/?key=书包,按 F12 用元素选择器点商品价格,可以看到价格在 <p class="price"><span class="price_n">¥146.00</span> 里;再点商品标题,可以看到标题在 <p class="name" name="title"><a title="商品标题"> 里。两条正则分别对应这两处即可。


完整代码:
# -*- coding: utf-8 -*-
"""
作业②:用 requests + re 设计商城比价定向爬虫
自选商城:当当网 search.dangdang.com,以关键词“书包”搜索
输出:序号 / 价格 / 商品名
"""
import requests
import re
# 当当网搜索接口,关键词用 params 自动拼接到 URL
URL = "http://search.dangdang.com/"
KEYWORD = "书包"
# 请求头:伪装成浏览器
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"
}
def crawl_dangdang():
"""爬取当当网“书包”搜索结果,提取商品名与价格"""
# 1. 发起请求,params 会自动把中文关键词编码到 URL
resp = requests.get(URL, params={"key": KEYWORD}, headers=HEADERS, timeout=20)
resp.encoding = "gbk" # 当当网搜索页是 gbk 编码
html = resp.text
# 2. 正则提取价格:<span class="price_n">¥146.00</span>
prices = re.findall(r'<span class="price_n">¥([\d.]+)</span>', html)
# 3. 正则提取商品名:<p class="name" name="title"><a title="商品标题">
names = re.findall(r'<p class="name" name="title"[^>]*>\s*<a[^>]*title="([^"]+)"', html)
# 4. 价格与商品名在页面中顺序一一对应,打包输出
print("{:<6}{:<10}{}".format("序号", "价格", "商品名"))
for i in range(min(len(prices), len(names))):
print("{:<8}{:<12}{}".format(i + 1, prices[i], names[i].strip()))
print("-" * 50)
print("共爬取到 %d 件商品(关键词:%s)" % (min(len(prices), len(names)), KEYWORD))
if __name__ == "__main__":
crawl_dangdang()
运行结果:


2)心得体会
这次作业让我真正体会到"选对网站"有多重要。一开始我想爬京东,结果翻遍网页源码都找不到价格,后来才知道京东的价格是页面加载后再用 JS 请求回来的,requests 直接拿到的 HTML 里根本没有。换成当当之后顺利很多。另外当当的页面是 gbk 编码,如果不设置 resp.encoding = "gbk",打印出来的中文商品名全是乱码。用正则提取时,我先在 F12 里看清价格和商品名各自的标签结构,再照着写表达式,这样写出来的正则命中率很高。正则的好处是灵活,但也要求标签结构稳定,一旦网页改版,正则就要跟着改。
作业③ 爬取福州大学新闻网图片
1)福大新闻网图片爬虫实验
实验目标:爬取福州大学新闻网"影像福大"页面(https://news.fzu.edu.cn/yxfd.htm )中的所有 JPEG、JPG、PNG 格式图片,保存到本地文件夹。
爬取前检查网页结构:
打开目标网页,按 F12 用元素选择器点页面上的某张图片,可以看到它是一个 <img> 标签,src 属性就是图片地址。页面里的图片分两类:一类是 images/ 开头的界面小图标(logo、按钮),一类是 /__local/ 开头的新闻内容大图。爬虫只要把所有 <img> 的 src 取出来、补全成绝对地址、判断扩展名是 jpg/jpeg/png 就下载。


完整代码:
# -*- coding: utf-8 -*-
"""
作业③:爬取福州大学新闻网“影像福大”页面所有 JPEG/JPG/PNG 图片
目标网址:https://news.fzu.edu.cn/yxfd.htm
输出:把页面内所有图片保存到本地 fzu_images 文件夹
"""
import os
from urllib.parse import urljoin, urlparse
import requests
from bs4 import BeautifulSoup
# 目标网址
URL = "https://news.fzu.edu.cn/yxfd.htm"
# 保存图片的文件夹
SAVE_DIR = "fzu_images"
# 请求头
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"
}
def is_image(path):
"""判断 URL 是否为 jpg/jpeg/png 图片(去掉查询参数、忽略大小写)"""
ext = os.path.splitext(urlparse(path).path)[1].lower()
return ext in (".jpg", ".jpeg", ".png")
def crawl_images():
"""抓取页面所有图片并下载到本地文件夹"""
# 1. 创建保存文件夹
if not os.path.exists(SAVE_DIR):
os.makedirs(SAVE_DIR)
# 2. 请求网页并解析
resp = requests.get(URL, headers=HEADERS, timeout=20)
resp.encoding = "utf-8"
soup = BeautifulSoup(resp.text, "lxml")
# 3. 取出所有 <img> 的 src,补全成绝对地址并过滤出图片
img_urls = []
for img in soup.find_all("img"):
src = img.get("src")
if not src:
continue
abs_url = urljoin(URL, src) # 相对路径转绝对路径
if is_image(abs_url):
img_urls.append(abs_url)
img_urls = list(dict.fromkeys(img_urls)) # 去重
print("页面中共发现 %d 张 jpg/jpeg/png 图片:" % len(img_urls))
# 4. 逐张下载
for i, img_url in enumerate(img_urls, 1):
r = requests.get(img_url, headers=HEADERS, timeout=20)
filename = os.path.basename(urlparse(img_url).path)
filepath = os.path.join(SAVE_DIR, filename)
with open(filepath, "wb") as f:
f.write(r.content)
print("[%d/%d] 已保存:%s" % (i, len(img_urls), filename))
print("完成,共下载 %d 张图片" % len(img_urls))
if __name__ == "__main__":
crawl_images()
运行结果:



2)心得体会
这次作业让我搞懂了相对路径和绝对路径的区别。网页上图片的 src 有的是 images/logo.png 这种相对路径,直接拿去下载会报错,必须用 urljoin() 把它和网页地址拼成完整的 https://news.fzu.edu.cn/images/logo.png 才能请求到。另外有些图片地址后面带着 ?e=.png 这样的查询参数,取文件名时要先用 urlparse 把路径部分单独拿出来再判断扩展名,否则会被误判。最后把图片用二进制模式 wb 写入本地文件夹,就完成了从网页批量下载图片的完整流程,整个过程比想象中清晰。
总结
通过这次作业,我练习了 requests 发送请求、用 BeautifulSoup 解析表格、用正则表达式提取商品信息、以及批量下载图片这四种最常见的爬虫场景。最大的体会是:写爬虫之前一定要先用浏览器 F12 看清目标数据到底在哪个标签里,编码问题和动态渲染问题是最容易踩的两个坑。
*

浙公网安备 33010602011771号