2024数据采集与融合技术实践-作业1

作业①

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

代码与运行结果

代码:

#导入所需要的库
import requests
from bs4 import BeautifulSoup
import pandas as pd

#爬取网址的网址
url = "https://www.shanghairanking.cn/rankings/bcur/2024"
#发送请求
response = requests.get(url)
#设置编码格式
response.encoding = 'utf-8'
#获取相应内容
html = response.text

#定义存储形式为字典
all = {"school": {}, "province": {}, "type": {}, "score": {}}

#定义beautifulSoup对象
soup = BeautifulSoup(html, "html.parser")
#找到内容存储主体部分
body = soup.find(attrs={"class": "rk-table"}).find("tbody").find_all("tr")

#定义获取到的信息在all中的存储位置
count = 0

#按排名顺序遍历主体部分,获取到每一个大学信息的信息存储容器
for info in body:
    # 获取学校名字
    school_names = soup.find_all("span", attrs={"class": "name-cn"})
    # 处理数据并保存
    name_count = 0
    for name in school_names:
        all["school"][name_count] = name.text.strip().replace(" ", "").replace("\n", "")
        name_count += 1

    #因为省份等信息存储在td中,所以通过寻找td来解析获取到的数据
    td = info.find_all("td")

    province = td[2].text.strip().replace(" ", "").replace("\n", "")
    all["province"][count] = province

    type_ = td[3].text.strip().replace(" ", "").replace("\n", "")
    all["type"][count] = type_

    score = td[4].text.strip().replace(" ", "").replace("\n", "")
    all["score"][count] = score

    #位置加一,以便后面获取到的信息依次存储
    count += 1


# 将字典转换为 pandas DataFrame
df = pd.DataFrame({
    '排名': range(1, len(all['school']) + 1),
    '学校名字': [all['school'][i] for i in range(len(all['school']))],
    '省市': [all['province'][i] for i in range(len(all['province']))],
    '学校类型': [all['type'][i] for i in range(len(all['type']))],
    '总分': [all['score'][i] for i in range(len(all['score']))]
})

# 显示转换后的 DataFrame,index=False是取消原来的dataframe自动编号
print(df.to_string(index=False))

# 如果需要,可以将其保存为 CSV 文件
# df.to_csv('school_ranking.csv', index=False, encoding='utf-8')

运行结果:

命令行输出:

jupyter输出:

心得体会

通过这种直接给网页发送请求获取网页数据的方式还是比较简单的,数据直接存储在网页结构里面,主要是对网页解析部分,有些返回的数据会参杂着没有用的字符,如空格换行符等,这个时候就需要再次解析才能过滤获得想要的数据。最后采用了DataFrame的形式展现数据,一个是能使数据排列整齐,一个是存储到DataFrame中能更好的用做数据分析。

对于requests库的使用,该库集成了很多功能,能使代码编写更加简单,使我们高效的编写代码与调试代码;对于BeautifulSoup的使用,该库对于元素的解析非常高效,语法简洁明了,但是对于细节处理部分稍微会差一点,因为该库只能检索标签,没有很好的能将text值多余部分消除。

作业②

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

代码与运行结果

代码:

import requests  # 用于发送HTTP请求
import re  # 正则表达式模块,用于匹配字符串模式
import threading  # 线程模块,用于并发下载图片
import time  # 时间模块,用于暂停程序的执行
import random  # 随机模块,用于生成随机数
import os  # 操作系统模块,用于文件操作

# 模拟浏览器登录,定义一个User-Agent,伪装请求为浏览器发出的
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0"

# 定义cookie字符串,用于保持会话状态,模拟登录后的请求
cookie = "__jdu=17271449574151433017795; shshshfpa=ded88cf8-09ce-a477-ebe4-2493c67e8e78-1727144962; shshshfpx=ded88cf8-09ce-a477-ebe4-2493c67e8e78-1727144962; TrackID=13Pn2WnjRYPpif5576bxpXfg2LgdzJBvmBmxNDVlnjcS2PW7g12CEUt-Weeae7DJvs5YXD72sNwIeo07aE67NLntR3j7K6iiAOvC6d98uS7z43bHiiT8DSitPopoziGmu; thor=715141BE6C9536F17D1864B0AB49B027336956B4A93B43087E32EF1A158B0017B8955B71BC4CC9CDDF3603CA60C4A03365163B28FFC27EEE0597B416E5DCC1412E994C9E0A9F1C656ACFCD63CD64EB06BE1436FF223EBEA4D2940AAF7BF457B4964FD83E5B9723D347C0E642ECE4A7C11952DC98A41773BB0CC93B344004403895112E7460413D45066A56C24A62751FE4F6B20901534C5193B63C175FAC1CEC; light_key=AASBKE7rOxgWQziEhC_QY6yalHIWSMfjmMdQCIfmiiLp90g34uoBR2WngQeQmaR45Rno5KRb; pinId=t8vUk2ONJqfeMhHAdNEcfA; pin=jd_IMrvTjYrsOYY; unick=jd_i8b81qc9y0r86f; _tp=QPV29p3JH9DfoHpsRIj1wQ%3D%3D; _pst=jd_IMrvTjYrsOYY; qrsc=3; ipLoc-djd=16-1303-1305-48927; __jdv=76161171|cn.bing.com|-|referral|-|1728467486377; shshshfpb=BApXSqce5c_dALiLo-4lKw1ktTeR9uD-iBmVVTk1r9xJ1Mr4f74C2; 3AB9D23F7A4B3C9B=4VJNECADUFJ4BVR3NKQPF75TI2MLCZHTQQCGPYAFMQD33EJ6JXO6INMPS7JUYEUTFXHXKPAQTXWRV5Y2ZPWFC4TFIQ; __jda=76161171.17271449574151433017795.1727144957.1728467486.1729399437.8; __jdb=76161171.1.17271449574151433017795|8.1729399437; __jdc=76161171; 3AB9D23F7A4B3CSS=jdd034VJNECADUFJ4BVR3NKQPF75TI2MLCZHTQQCGPYAFMQD33EJ6JXO6INMPS7JUYEUTFXHXKPAQTXWRV5Y2ZPWFC4TFIQAAAAMSVA6VL3IAAAAADPNSJBEFDTSPL4X; _gia_d=1; flash=3_0b5lq6EleaKtOFGosYp8NM4saVZ87rUzmUmVT9RA5qPOvwuy9Rc7RHOjHgX_QOiTXb5rjB0soP8FSHLMo6Wen1vJjjDuy46ML7P9uXntJgprrRSLdPy9aiA_EPUXLDy2lmtbqLKf4BedJz0rurzL-ZxYCfvD42Muko84jAG3bKeRvcuLcLry; PCSYCityID=CN_350000_350100_0; jsavif=1; jsavif=1; xapieid=jdd034VJNECADUFJ4BVR3NKQPF75TI2MLCZHTQQCGPYAFMQD33EJ6JXO6INMPS7JUYEUTFXHXKPAQTXWRV5Y2ZPWFC4TFIQAAAAMSVA6VL3IAAAAADPNSJBEFDTSPL4X"

# 将User-Agent和Cookie信息存储在headers中,作为请求头发送
headers = {"user-agent": user_agent, "cookie": cookie}

# 定义获取图片函数,参数n代表要爬取的页数
def getPic(n):
    global threads  # 使用全局变量threads来保存所有启动的线程
    count = 0  # 用于记录图片的编号

    # 循环遍历每一页
    for page in range(1, n + 1):
        url = f"https://search.jd.com/Search?keyword=%E4%B9%A6%E5%8C%85&page={page}"  # 构建请求URL,模拟搜索“书包”

        # 使用requests库发起HTTP GET请求
        try:
            response = requests.get(url, headers=headers, timeout=10)  # 发送请求,超时设置为10秒
            response.raise_for_status()  # 如果请求失败,抛出HTTPError异常
            html = response.text  # 获取页面HTML内容
        except requests.RequestException as e:
            print(f"Failed to retrieve page {page}: {e}")  # 请求出错时,打印错误信息并跳过当前页
            continue

        # 使用正则表达式匹配每一个商品的HTML容器
        reg_li = r'<li data-sku.*?</li>.*?</div>\n\t</li>'
        match_li = re.findall(reg_li, html, re.DOTALL)  # 匹配所有<li>标签

        # 遍历匹配到的商品列表
        for li in match_li:
            # 使用正则表达式匹配商品的图片信息
            reg_pic = "<img w.*?>"
            match_pic = re.findall(reg_pic, li, re.DOTALL)

            # 提取图片URL
            reg_pic_url = 'data-lazy-img="(.*?)"'  # 匹配图片路径
            if match_pic:  # 如果找到图片信息
                try:
                    pic_url = "https:" + re.findall(reg_pic_url, match_pic[0])[0]  # 拼接完整的图片URL
                    count += 1  # 计数器递增
                    # 创建下载线程并启动
                    T = threading.Thread(target=downLoad, args=(pic_url, count))
                    T.setDaemon(False)  # 设置线程为非守护线程,主线程结束前,子线程要完成
                    T.start()  # 启动线程
                    threads.append(T)  # 将线程添加到线程列表
                except IndexError:
                    print("Failed to find image URL in match_pic.")  # 如果没有找到图片URL,打印错误信息
                except Exception as e:
                    print(e)  # 打印其他异常

        # 随机暂停0到3秒,避免频繁请求被封禁
        time.sleep(random.randint(0, 3))

# 定义下载图片的函数
def downLoad(url, title):
    try:
        # 使用requests下载图片数据
        response = requests.get(url, timeout=100)  # 请求图片,设置超时时间为100秒
        response.raise_for_status()  # 如果请求失败,抛出HTTPError异常
        data = response.content  # 获取图片的二进制数据

        # 检查保存图片的文件夹是否存在,如果不存在则创建
        if not os.path.exists("./jingdong"):
            os.makedirs("./jingdong")

        # 生成图片保存路径
        downLoadPath = f"./jingdong/{str(title)}.jpg"

        # 将图片数据写入文件
        with open(downLoadPath, "wb") as fobj:
            fobj.write(data)

    except requests.RequestException as e:
        print(f"Failed to download {url}: {e}")  # 如果下载失败,打印错误信息
    except Exception as e:
        print(e)  # 打印其他异常

# 初始化线程列表,用于保存所有的线程对象
threads = []

# 启动获取图片的函数,参数为要爬取的页数
getPic(2)

# 等待所有线程执行完毕
for t in threads:
    t.join()  # 阻塞主线程,等待子线程执行完毕

print("The End")  # 打印结束标志

运行结果:

心得体会

使用多线程对京东的图片进行爬取,将网页解析与图片下载过程分开,大大的提高了下载效率。而后面所加入的线程阻塞,能大大的提高图片下载的成功率,可以看到本次试验是爬取两页图片,每页有30张图片,总共爬取到了60张图片,没有多线程的加持成功率大概只有百分之五十。当然加入多线程并不一定会将成功率提到百分百,借用淘宝图片爬取的例子,其单线程到多线程的成功率只能从大约百分之五十提到七八十。

作业③:

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

代码与运行结果

代码:

from bs4 import BeautifulSoup  # 导入BeautifulSoup库,用于解析HTML文档
import requests  # 导入requests库,用于发送HTTP请求
import os  # 导入os模块,用于文件操作


# 定义下载图片的函数
def downLoad(url, title):
    try:
        # 使用requests库发送HTTP GET请求,下载图片
        response = requests.get(url, timeout=100)  # 设置请求超时为100秒
        response.raise_for_status()  # 如果请求不成功,抛出异常
        data = response.content  # 获取图片的二进制数据

        # 检查保存图片的目录是否存在,如果不存在则创建
        if not os.path.exists("./fzu"):
            os.makedirs("./fzu")

        # 定义图片保存路径,命名为"title.jpg"
        downLoadPath = f"./fzu/{str(title)}.jpg"

        # 将下载的图片数据写入文件
        with open(downLoadPath, "wb") as fobj:
            fobj.write(data)

    except requests.RequestException as e:
        print(f"Failed to download {url}: {e}")  # 捕获请求异常,打印错误信息
    except Exception as e:
        print(e)  # 捕获其他异常并打印


# 要爬取的福州大学新闻网站URL
url = "https://news.fzu.edu.cn/yxfd.htm"

# 使用requests库发送HTTP GET请求获取网页内容
response = requests.get(url, timeout=10)  # 设置请求超时为10秒
response.raise_for_status()  # 如果请求失败,抛出异常
html = response.text  # 获取页面的HTML内容

# 使用BeautifulSoup解析HTML文档
bs = BeautifulSoup(html, "html.parser")

# 查找页面中所有的<img>标签,获取图片的相关信息
img_url = bs.select("img")  # 使用CSS选择器获取所有图片标签

# 定义一个列表来保存所有jpg和jpeg格式的图片URL
img_jpg_or_jpeg = []

# 遍历找到的所有图片URL
for url in img_url:
    # 筛选出后缀为jpg或jpeg的图片
    if url["src"][-3:] == "jpg" or url["src"][-4:] == "jpeg":
        # 补全相对路径为完整URL
        new_url = "https://news.fzu.edu.cn" + url["src"]
        # 将符合条件的图片URL添加到列表中
        img_jpg_or_jpeg.append(new_url)

# 初始化计数器,用于命名下载的图片
count = 1

# 遍历筛选出的图片URL列表,下载每一张图片
for url in img_jpg_or_jpeg:
    # 调用下载函数downLoad,传入图片URL和计数器作为图片名称
    downLoad(url, count)
    count = count + 1  # 计数器递增,用于为下一张图片命名

运行结果:

心得体会

因为该任务的爬取数量不是很多,就没有用前面的那种多线程,如果爬取的图片比较多的话改为和前面的多线程一样即可。但是这里有要求是要爬取JPG或者JPEG格式的图片,在后面加个判断后缀名即可,可以看到原来的网站的很多PNG图片都被过滤掉了,只剩下了最后的3张符合要求的图片。

posted @ 2024-10-20 19:39  starryship  阅读(3)  评论(0编辑  收藏  举报