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张符合要求的图片。