数据采集第一次作业

作业①:使用requests+BeautifulSoup爬取软科中国大学排名

1.1 实验代码与运行截图

先去目标网址check一下

06

不错,列表很整齐一个个元素的

07

里面也确实是我们要的信息,开爬

08

奇怪,只有30条。查看发现一面就是30条,需要到第二面,检查按钮,发现不是请求新的html,只能尝试点击2什么的,说是要用selenium,但是我觉得应该暂时用不到这么麻烦的

09

用network排查Ajax,发现也不是一般ajax

10

科软你这家伙,好像有点超出我的知识储备了。请出搜索大法:搜一个排名40的兰州大学

是在一个payload.js里面,而且在页面刷新还没点2的时候就有了,好多payload.js

11

终于定位到原payload.js,不负人工寻找,这个里面包含了所有排名信息。其实它就是一个json

12

搞它,掏url,请求后py解析即可

13

代码

👉 点击查看代码
import urllib.request
import json
import re

# 浏览器网络面板中抓到的排名数据文件(JavaScript,不是 JSON)
url = "https://www.shanghairanking.cn/_nuxt/static/1789974610/rankings/bcur/2020/payload.js"

# 模拟浏览器访问
request = urllib.request.Request(
    url,
    headers={
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                      "AppleWebKit/537.36 (KHTML, like Gecko) "
                      "Chrome/140.0.0.0 Safari/537.36",
        "Referer": "https://www.shanghairanking.cn/rankings/bcur/2020",
    }
)

# 获取数据
with urllib.request.urlopen(request, timeout=30) as response:
    text = response.read().decode("utf-8")

# payload.js 用函数参数压缩重复值,例如 province:p,其中 p 对应“北京”。
# 读取参数名和文件末尾传入的值,建立对应关系,不执行 JavaScript。
parameters = re.search(r"function\(([^)]*)\)", text)
arguments = re.search(r"\}\((.*)\)\)\);\s*$", text, re.S)

names = parameters.group(1).split(",")
values = json.loads("[" + arguments.group(1) + "]")
variables = dict(zip(names, values))

# 每所大学的基本字段位于 univUp 和 indData 之间。
# 字段值可能是字符串、数字,也可能是上面定义的变量名。
token = r'"(?:\\.|[^"\\])*"|[A-Za-z_$][\w$]*|-?\d+(?:\.\d+)?'
schools = []
for match in re.finditer(r"\{univUp:.*?,indData:", text, re.S):
    school = {}
    for field in ["ranking", "univNameCn", "province", "univCategory", "score"]:
        result = re.search(r"(?:\{|,)" + field + r":(" + token + r")(?=,)", match.group())
        value = result.group(1)
        school[field] = variables[value] if value in variables else json.loads(value)
    schools.append(school)

# 打印表头
print(f"{'排名':<6}{'学校名称':<18}{'省市':<10}{'学校类型':<10}{'总分':>8}")
print("-" * 60)

# 输出前36名
for school in schools[:36]:

    rank = school["ranking"]
    name = school["univNameCn"]
    province = school["province"]
    school_type = school["univCategory"]
    score = school["score"]

    print(f"{rank:<6}{name:<18}{province:<10}{school_type:<10}{score:>8}")


结果

成功!输出36个

15

1.2 心得体会

很神奇的一个过程。
怎么会把数据都写在那些payload.js里啊,而且甚至很多payload.js连名字都一毛一样但是内容完全不同。
发现原来其实网站的设计很复杂,很多东西其实我见都没见过。
然后解析这个js也是挺麻烦的。


作业②:使用requests+re正则实现商城商品比价爬虫(关键词:书包)

2.1 实验代码与运行截图

商城选定与网页勘探

爬当当网,因为不用登录
研究后发现其翻页方式就是,从url上面修改page_index参数(但是没实践

image03
image04

商品列表的定位:

Screenshot 2026-10-09 131642

商品名称title:
Screenshot 2026-10-09 131847

商品价格price_n:

Screenshot 2026-10-09 132059

代码

👉 点击查看代码
"""作业二:用 requests 和 re 爬取当当网的书包名称和价格。"""

import csv
import re
from html import unescape
from pathlib import Path
from urllib.parse import quote

import requests


# 当当搜索使用 GBK 编码的关键词。
keyword = "书包"
url = "https://search.dangdang.com/?key=" + quote(keyword, encoding="gbk") + "&act=input"
headers = {"User-Agent": "Mozilla/5.0"}
response = requests.get(url, headers=headers, timeout=30)
response.raise_for_status()
response.encoding = "gbk"

# 先定位商品列表,再分割商品条目,保证名称和售价属于同一商品。
product_list = re.search(
    r'<ul\b[^>]*class="bigimg\b[^"]*"[^>]*>(.*?)</ul>', response.text, re.S
).group(1)
items = re.findall(r'<li\b[^>]*\bid="\d+"[^>]*>(.*?)</li>', product_list, re.S)

products = []
for item in items:
    name_html = re.search(
        r'<a\b[^>]*name="itemlist-title"[^>]*>(.*?)</a>', item, re.S
    ).group(1)
    # 去掉搜索关键词上的高亮标签,并还原 HTML 字符实体。
    name = unescape(re.sub(r"<[^>]+>", "", name_html)).strip()
    price = re.search(
        r'<span\b[^>]*class="price_n"[^>]*>\s*[¥¥]\s*([\d.]+)', unescape(item)
    ).group(1)
    products.append([float(price), name])

# 按售价从低到高排列,便于比较当前搜索页中的商品价格。
products.sort(key=lambda product: product[0])
print("序号  价格(元)  商品名")
for number, (price, name) in enumerate(products, 1):
    print(f"{number:<6}{price:<12.2f}{name}")
print(f"\n关键词:{keyword};共爬取搜索首页的 {len(products)} 件商品。")

result_dir = Path(__file__).resolve().parent / "results"
result_dir.mkdir(exist_ok=True)
with (result_dir / "schoolbags.csv").open("w", encoding="utf-8-sig", newline="") as file:
    writer = csv.writer(file)
    writer.writerow(["序号", "价格", "商品名"])
    for number, (price, name) in enumerate(products, 1):
        writer.writerow([number, f"{price:.2f}", name])

结果

image06
image07

2.2 心得体会

嗯,当当网被我爬了
(没有去把登录完成)
要求是使用re去做,说实话并不好用。感觉容错比较差,自己定搜寻标签也蛮麻烦的。
网页上显示的是人民币符号,源码里却写成 &yen;,正则也得跟着源码的写法来。


作业③:爬取网页所有jpg/jpeg/png图片并保存到本地文件夹

3.1 实验代码与运行截图

页面勘探

查看网站后发现有页面之分,既然要求是所有图片,那么7面的网站我们也来找全部吧
首页是没有后缀的

Screenshot 2026-10-09 125223
第5面后缀为3
Screenshot 2026-10-09 125306
第7面也是最后1一面后缀为1,可知逆序
Screenshot 2026-10-09 125329

总页面数,可以看到是标签为p_no的元素,可以由此确定。

Screenshot 2026-10-09 125449

然后图片的话,就用img选定就可以了。类型后面再过滤。

代码

👉 点击查看代码
"""作业三:爬取影像福大全部分页的图片。"""

from pathlib import Path
from urllib.parse import urljoin, urlsplit

import requests
from bs4 import BeautifulSoup


# 获取首页
url = "https://news.fzu.edu.cn/yxfd.htm"
headers = {"User-Agent": "Mozilla/5.0"}
response = requests.get(url, headers=headers)
response.encoding = "utf-8"
soup = BeautifulSoup(response.text, "html.parser")

# 从分页区读取总页数
pagination = soup.select_one(".n_right .pb_sys_common .p_pages")
page_numbers = pagination.select(".p_no a, .p_no_d")
total_pages = max(int(page.get_text()) for page in page_numbers)

# 生成全部页面地址
page_urls = [url]
for number in range(total_pages - 1, 0, -1):
    page_urls.append(urljoin(url, f"yxfd/{number}.htm"))

# 逐页提取图片地址
image_urls = []
for page_number, page_url in enumerate(page_urls, 1):
    response = requests.get(page_url, headers=headers)
    response.encoding = "utf-8"
    soup = BeautifulSoup(response.text, "html.parser")
    print(f"第 {page_number} 页:{page_url}")

    for image in soup.find_all("img"):
        image_url = urljoin(page_url, image["src"])
        extension = Path(urlsplit(image_url).path).suffix.lower()
        if extension in (".jpg", ".jpeg", ".png") and image_url not in image_urls:
            image_urls.append(image_url)

# 下载图片
image_dir = Path("images")
image_dir.mkdir(exist_ok=True)
for number, image_url in enumerate(image_urls, 1):
    extension = Path(urlsplit(image_url).path).suffix.lower()
    filename = f"{number:03d}{extension}"
    response = requests.get(image_url, headers=headers)
    (image_dir / filename).write_bytes(response.content)
    print(f"已保存:{filename}")

print(f"共爬取 {total_pages} 页,保存 {len(image_urls)} 张图片。")

结果截图

Screenshot 2026-10-09 131318

Screenshot 2026-10-09 131327

3.2 心得体会

我原来只抓了首页,后来按分页区的页数把后面的页面也加上了。这里的链接有点绕:第二页是 6.htm,第五页是 3.htm,最后一页才是 1.htm。首页还把第六页的链接省略了,所以我先生成了全部 7 个地址,再逐页去抓。

翻页以后,图片路径也要跟着当前页来拼。我用了 urljoin(),不用自己数路径里该加几个斜杠。提取地址时只留下 .jpg、.jpeg、.png,后缀先转成小写。各页都有一样的标志和页脚图片,地址已经在列表里的就不再加,省得反复下载。

保存图片用的是 response.content,拿到二进制内容后直接写进文件。最后文件夹里有 65 张图片,图片检查也都通过了。比起只看程序打印“已保存”,看到文件能正常打开还是更放心一些。

posted @ 2026-10-09 12:27  MieSheeeep  阅读(45)  评论(0)    收藏  举报