数据采集与融合技术作业1

作业1

(1)用requests和BeautifulSoup库方法定向爬取给定网址(http://www.shanghairanking.cn/rankings/bcur/2020 )的数据,屏幕打印爬取的大学排名信息。

在数据时代,获取权威的高校排名数据对升学参考、研究分析等场景都很有价值。软科中国大学排名是国内极具影响力的高校排名之一,本文将通过 Python 代码实现 2020 年软科中国大学排名数据的爬取与保存,带你走进简单实用的网页爬虫世界。

一、爬取前的准备:工具与环境

核心库介绍
  • BeautifulSoup:用于解析 HTML 页面,快速提取目标数据,是网页爬虫的常用解析工具。
  • urllib.request:Python 内置的 HTTP 请求库,无需额外安装,可直接用于获取网页内容。
  • os:用于创建本地文件夹,处理文件路径相关操作。

二、实验完整过程

导入所需库

from bs4 import BeautifulSoup
from urllib import request
import os

使用 try-except 捕获异常,避免程序因错误中断。先判断是否存在 "download_uni" 文件夹,不存在则创建,用于存储爬取的数据文件;随后以 UTF-8 编码打开文件,准备写入爬取到的排名信息。

try:
    if not os.path.exists("download_uni"):
        os.mkdir("download_uni")
    f=open("download_uni/university","w",encoding="utf-8")

指定目标 URL 为 2020 年软科中国大学排名页面,通过 urlopen 发送 GET 请求获取网页响应,读取响应内容后解码为 UTF-8 格式的字符串,再用 BeautifulSoup 结合 lxml 解析器对 HTML 进行解析,生成可操作的解析对象。

    url="http://www.shanghairanking.cn/rankings/bcur/2020"
    resp=request.urlopen(url)
    html=resp.read().decode()
    soup=BeautifulSoup(html,"lxml")

通过观察网页结构,可以发现所需的数据都在一个class 为 "rk-table" 的元素中
QQ_1760594129266
通过 CSS 选择器定位 class 为 "rk-table" 的表格,提取所有行(tr 标签)。若未找到目标表格,打印提示信息并退出程序,避免后续代码报错。

    trs=soup.select("table[class='rk-table'] tr")
    if not trs:
            print("未找到排名表格")
            exit()

先写入表格表头(排名、学校名称、省市、学校类型、总分),并在控制台打印表头。接着遍历表格行(跳过第一个表头行),通过 find_all 提取每行的所有单元格(td 标签),分别提取排名、学校名称等目标数据,用 strip () 去除多余空格。将提取的数据同时打印到控制台和写入文件,最后关闭文件确保数据保存完整。

    f.write("排名\t学校名称\t省市\t学校类型\t总分\n")
    print("排名\t学校名称\t省市\t学校类型\t总分")
    for tr in trs[1:]:
        tds=tr.find_all("td")
        # 提取排名
        rank=tds[0].text.strip()
        # 提取学校名称
        name=tds[1].find(name="div" ,attrs={"class":"link-container"}).text.strip()
        # 提取省市
        prov=tds[2].text.strip()
        # 提取学校类型
        style=tds[3].text.strip()
        # 提取总分
        grade=tds[4].text.strip()
        print(rank+"\t"+name+"\t"+prov+"\t"+style+"\t"+grade)

        f.write(rank+"\t"+name+"\t"+prov+"\t"+style+"\t"+grade+"\n")
    f.close()
except Exception as err:
    print(err)

结果

10

(2)心得体会

作业1是对照着课本项目《爬取图书网站》写的,我更加熟练BeautifulSoup的使用。

作业2

(1)用requests和re库方法设计某个商城(自已选择)商品比价定向爬虫,爬取该商城,以关键词“书包”搜索页面的数据,爬取商品名称和价格。

在电商平台日益发达的今天,我们经常需要对不同商品进行价格比较以获得最优惠的购物体验。本项目将设计一个定向爬虫,爬取某商城中以 "书包" 为关键词的搜索结果,提取商品名称和价格信息,帮助用户快速比价。

一、爬取前的准备:工具与环境

核心库介绍
  • requests 库:用于发送 HTTP 请求,获取网页内容
  • re 库:用于通过正则表达式解析网页,提取所需信息
  • BeautifulSoup:辅助解析 HTML
  • Selenium:处理动态渲染页面

二、实验完整过程

小网站

观察网站结构,得到正则表达式

price = re.search(r'<div class="money-fl">¥(\d*.\d*)</div>', html)
name=re.search(r'<a target="_blank" title="([^"]*)"', html)

8

from urllib import request
import re
try:
    url = "https://search.bl.com/k-%25E4%25B9%25A6%25E5%258C%2585.html?bl_ad=P668822_-_%u4E66%u5305_-_5"
    resp = request.urlopen(url)
    html = resp.read().decode()
    price = re.search(r'<div class="money-fl">¥(\d*.\d*)</div>', html)
    name=re.search(r'<a target="_blank" title="([^"]*)"', html)
    i=1
    while price!=None and name!=None:
        print(i,"\t",price.group(1),"\t",name.group(1))
        html=html[name.end():]
        price = re.search(r'<div class="money-fl">¥(\d*.\d*)</div>', html)
        name=re.search(r'<a target="_blank" title="([^"]*)"', html)

except Exception as err:
    print(err)

结果

9

淘宝

将要爬取的html输出,发现并没有我们所需要的html树结构

from urllib import request
try: 
    url=url = "https://uland.taobao.com/sem/tbsearch?bc_fl_src=tbsite_T9W2LtnM&channelSrp=bingSomama&clk1=8294ab7a6b5e3f32bb18ef6ea81f248d&commend=all&ie=utf8&initiative_id=tbindexz_20170306&keyword=%E4%B9%A6%E5%8C%85&localImgKey=&msclkid=755f439a279d1844fb1e2b75738ed163&page=1&preLoadOrigin=https%3A%2F%2Fwww.taobao.com&q=%E4%B9%A6%E5%8C%85&refpid=mm_2898300158_3078300397_115665800437&search_type=item&sourceId=tb.index&spm=tbpc.pc_sem_alimama%2Fa.search_manual.0&ssid=s5-e&tab=all"
    resp=request.urlopen(url)
    html=resp.read().decode()
    print(html)
except Exception as err:
    print(err)

1

询问大模型,明白核心问题是淘宝页面是动态渲染的,而爬取的是初始 HTML(只包含加载脚本),但商品数据(价格、名称)是后续通过 JS 动态生成的,正则自然匹配不到内容。
上网找资料,翻阅课本,发现课本第五章使用Selenium解决这个问题,仿写后仍被淘宝反爬,跟着csdn修改代码,使用随机User-Agent池和最常见的请求头。

USER_AGENTS = [
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36',
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36 Edg/140.0.0.0',
    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36'
]
headers = {
        'User-Agent': random.choice(USER_AGENTS),
        'Referer': 'https://www.taobao.com/',
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
        'Accept-Language': 'zh-CN,zh;q=0.9',
        'Connection': 'keep-alive'
    }

安装Selenium

pip install Selenium

查看谷歌浏览器版本

2

下载对应版本的谷歌驱动

3

把下载好的.exe文件放到py的安装目录下

4

爬取过程

def parsePage(uinfo, data):
    #价格
    plt1 = re.findall(r'"Price--priceInt--BXYeCOI">(\d+)', data)
    plt2 = re.findall(r'"Price--priceFloat--rI_BYho">([\d.]+)', data)
    #名字
    soup = BeautifulSoup(data, "lxml")
    tlt = soup.find_all("div", attrs={"class": "Title--title--wJY8TeA"})

    max_len = min(len(tlt), len(plt1), len(plt2))
    for i in range(max_len):
        zheng = plt1[i] if plt1[i].strip() else "0"
        xiao = plt2[i] if plt2[i].strip() else ".00"
        price = zheng + xiao
        name_elem = tlt[i].find("span", attrs={"class": True})
        name = name_elem.text.strip() if name_elem else f"未知商品_{i+1}"
        uinfo.append([i + 1, price, name])
    return uinfo

运行结果

5

完整代码

import re
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
import random

USER_AGENTS = [
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36',
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36 Edg/140.0.0.0',
    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36'
]

def getHTMLText(url):
    chrome_options = Options()
    chrome_options.add_argument('--headless')
    chrome_options.add_argument('--no-sandbox')
    chrome_options.add_argument('--disable-dev-shm-usage')
    chrome_options.add_argument('--disable-blink-features=AutomationControlled')  # 隐藏自动化标识
    chrome_options.add_experimental_option('excludeSwitches', ['enable-logging'])
    chrome_options.add_experimental_option('useAutomationExtension', False)
    chrome_options.add_argument(f'user-agent={random.choice(USER_AGENTS)}')
    driver = webdriver.Chrome(options=chrome_options)
    driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
        'source': 'Object.defineProperty(navigator, "webdriver", {get: () => undefined})'  # 进一步规避反爬
    })
    
    try:
        driver.get(url)
        WebDriverWait(driver, 10).until(
            EC.presence_of_element_located((By.CLASS_NAME, "Title--title--wJY8TeA"))
        )
        data = driver.page_source
    except Exception as err:
        print(err)
        data = ""
    finally:
        driver.quit() 
    return data

def parsePage(uinfo, data):
    plt1 = re.findall(r'"Price--priceInt--BXYeCOI">(\d+)', data)
    plt2 = re.findall(r'"Price--priceFloat--rI_BYho">([\d.]+)', data)
    soup = BeautifulSoup(data, "lxml")
    tlt = soup.find_all("div", attrs={"class": "Title--title--wJY8TeA"})
    
    max_len = min(len(tlt), len(plt1), len(plt2))
    for i in range(max_len):
        zheng = plt1[i] if plt1[i].strip() else "0"
        xiao = plt2[i] if plt2[i].strip() else ".00"
        price = zheng + xiao
        name_elem = tlt[i].find("span", attrs={"class": True})
        name = name_elem.text.strip() if name_elem else f"未知商品_{i+1}"
        uinfo.append([i + 1, price, name])
    return uinfo

def printGoodslist(uinfo):
    tplt = "{0:^5}\t{1:^12}\t{2:^60}"
    print(tplt.format("序号", "价格", "商品名称"))
    for i in uinfo:
        name = i[2][:55] + "..." if len(i[2]) > 55 else i[2]
        print(tplt.format(i[0], i[1], name))

def main():
    url = "https://uland.taobao.com/sem/tbsearch?bc_fl_src=tbsite_T9W2LtnM&channelSrp=bingSomama&clk1=8294ab7a6b5e3f32bb18ef6ea81f248d&commend=all&ie=utf8&initiative_id=tbindexz_20170306&keyword=%E4%B9%A6%E5%8C%85&localImgKey=&msclkid=755f439a279d1844fb1e2b75738ed163&page=1&preLoadOrigin=https%3A%2F%2Fwww.taobao.com&q=%E4%B9%A6%E5%8C%85&refpid=mm_2898300158_3078300397_115665800437&search_type=item&sourceId=tb.index&spm=tbpc.pc_sem_alimama%2Fa.search_manual.0&ssid=s5-e&tab=all"
    uinfo = []
    data = getHTMLText(url) 
    uinfo = parsePage(uinfo, data) 
    printGoodslist(uinfo)

if __name__ == '__main__':
    main()

(2)心得体会

作业2通过csdn与大模型的帮助,我成功爬取有反爬机制的淘宝网站。通过这个作业,我提前学习了课本第五章内容,并了解到Selenium 能模拟真实浏览器的加载过程,等待 JS 渲染完成后再获取页面内容,是爬取动态页面的常用方法。

作业3

(1)爬取一个给定网页(https://news.fzu.edu.cn/yxfd.htm)或者自选网页的所有JPEG、JPG或PNG格式图片文件

一、爬取前的准备:工具与环境

核心库介绍
  • urllib.request
  • re:正则表达式库
  • os:用于文件和目录操作
  • urllib.parse.urljoin:生成完整的图片 URL

二、实验完整过程

观察网页很容易得到如下的爬取逻辑,方法与上面两个实验类似,不再赘述

from urllib import request
import re
import os
from urllib.parse import urljoin
try:
    if not os.path.exists("download_img"):
        os.mkdir("download_img")
    base_url = "https://news.fzu.edu.cn/"
    url = "https://news.fzu.edu.cn/yxfd.htm"
    resp = request.urlopen(url)
    html = resp.read().decode()

    ls = re.findall(r'<img[^>]*>', html)
    
    for l in ls:
        src_match = re.search(r'src=["\']([^"\']+)["\']', l)
        if not src_match:
            continue
            
        url_img = src_match.group(1)
        if not url_img.lower().endswith(".jpg") and not url_img.lower().endswith(".jpeg") and not url_img.lower().endswith(".png") :
            continue
        
        full_img_url = urljoin(base_url, url_img)
        
        try:
            img_data = request.urlopen(full_img_url).read()
            filename = os.path.basename(url_img)
            save_path = os.path.join("download_img", filename)
            with open(save_path, "wb") as f:
                f.write(img_data)
            print(f"已保存图片: {save_path}\n")
            
        except Exception as e:
            print(e)

except Exception as err:
    print(err)

运行发现有报错信息

[Errno 22] Invalid argument: 'download_img\\10F0677E72A0B0DCCF5F798C6DB_865A6721_1F7.png?e=.png'
[Errno 22] Invalid argument: 'download_img\\0B2EC59649CA13BAC85941A8B12_D497950A_19B.png?e=.png'
[Errno 22] Invalid argument: 'download_img\\9D5EF45533E69056CF06F1A0115_9201CE3A_198.png?e=.png'

图片 URL 中包含特殊字符(如?),导致生成的文件名不符合 Windows 系统的命名规则。Windows 文件名中不允许包含?、*、:等特殊字符,需要对文件名进行清洗处理。

# 定义需要过滤的特殊字符
INVALID_CHARS = r'[\\/:*?"<>|]'
# 提取原始文件名,并清洗特殊字符
raw_filename = os.path.basename(url_img)
# 用正则替换所有不合法字符为下划线(_)
clean_filename = re.sub(INVALID_CHARS, '_', raw_filename)

运行可获得所有JPEG、JPG或PNG格式图片文件

7

(2)心得体会

作业3是在课上的爬取jpg文件基础上拓展,我学习了如何使用 Python 的标准库来获取网页内容、解析 HTML、提取所需信息并保存到本地。

posted @ 2025-10-18 23:18  贪吃小屁  阅读(17)  评论(0)    收藏  举报