数据采集与融合技术实践--作业二
数据采集与融合技术作业二
📌1.相关信息及链接
名称 | 信息及链接 |
---|---|
学号姓名 | 102202108 王露洁 |
本次作业要求链接 | https://edu.cnblogs.com/campus/fzu/2024DataCollectionandFusiontechnology/homework/13285 |
作业①所在码云链接 | https://gitee.com/wanglujieeee/crawl_project/tree/master/作业2.1 |
作业②所在码云链接 | https://gitee.com/wanglujieeee/crawl_project/tree/master/作业2.1 |
作业③所在码云链接 | https://gitee.com/wanglujieeee/crawl_project/tree/master/作业2.3 |
📝2.作业内容
作业①:给定城市集7日天气预报的爬取
✒️要求:在中国气象网(http://www.weather.com.cn)给定城市集合的7日天气预报,并保存在数据库。
🗃️输出信息:Gitee文件夹链接
🌏解决思路
1.导入必要的库:
🔹BeautifulSoup:用于解析HTML和XML文档。
🔹UnicodeDammit:用于处理字符编码问题。
🔹urllib.request:用于从网页获取数据。
🔹sqlite3:用于与SQLite数据库进行交互。
2.定义城市和URL:
🔹使用一个字典cities来存储城市和它们对应的天气页面URL。
3.数据库连接和表格创建:
🔹连接到SQLite数据库(如果数据库不存在,则会自动创建)。
🔹创建一个名为weather的表,如果它还不存在的话。这个表包含序号、地区、日期、天气信息和温度等字段。
4.遍历每个城市并爬取天气信息:
🔹对于字典cities中的每个城市和URL:
🔹设置请求头以模拟浏览器访问。
🔹发送HTTP请求并读取响应数据。
🔹使用UnicodeDammit处理字符编码问题,确保数据以正确的编码格式读取。
🔹使用BeautifulSoup解析HTML内容。
🔹查找包含天气信息的HTML元素。
🔹遍历每个标签,提取日期、天气信息和温度。
🔹检查数据库中是否已存在相同地区和日期的记录,如果不存在,则将这些信息插入到数据库中。
🚀代码实现
from bs4 import BeautifulSoup
from bs4 import UnicodeDammit
import urllib.request
import sqlite3
# 城市和其对应的天气页面URL
cities = {
"北京": "https://www.weather.com.cn/weather/101010100.shtml",
"上海": "https://www.weather.com.cn/weather/101020100.shtml",
"广州": "https://www.weather.com.cn/weather/101280101.shtml",
"杭州": "https://www.weather.com.cn/weather/101210101.shtml"
}
# 数据库连接和表格创建
conn = sqlite3.connect('cities_weather.db')
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS weather (
序号 INTEGER PRIMARY KEY AUTOINCREMENT, -- 让数据库自动生成唯一序号
地区 varchar(16),
日期 varchar(16),
天气信息 varchar(64),
温度 varchar(32)
)
''')
# 遍历每个城市及其URL,爬取天气信息
for city, url in cities.items():
try:
headers = {"User-Agent": "Mozilla/5.0(Windows;U;Windows NT 6.0 x64;en-US;rv:1.9pre)Gecko/2008072421 Minefield/3.0.2pre"}
req = urllib.request.Request(url, headers=headers)
data = urllib.request.urlopen(req).read()
dammit = UnicodeDammit(data, ["utf-8", "gbk"])
data = dammit.unicode_markup
# 使用 BeautifulSoup 解析网页内容
soup = BeautifulSoup(data, "lxml")
lis = soup.select("ul[class='t clearfix'] li")
# 遍历每一天天气数据
for li in lis:
try:
date = li.select("h1")[0].text
weather = li.select("p[class='wea']")[0].text
tem = li.select("p[class='tem']")[0].text.strip() # 获取温度,去除空白
# 检查数据库中是否已存在该地区和日期的记录
cursor.execute('SELECT COUNT(*) FROM weather WHERE 地区 = ? AND 日期 = ?', (city, date))
exists = cursor.fetchone()[0]
if exists == 0:
# 插入数据到数据库
cursor.execute('''
INSERT INTO weather (地区, 日期, 天气信息, 温度) VALUES (?, ?, ?, ?)
''', (city, date, weather, tem))
except Exception as e:
print(f"处理 {city} 的天气信息时出错: {e}")
# 提交城市的数据
conn.commit()
except Exception as e:
print(f"爬取 {city} 的天气信息时出错: {e}")
# 查询并打印数据库中的所有城市数据以验证插入是否成功
cursor.execute('SELECT * FROM weather')
rows = cursor.fetchall()
# 打印表头
print("序号 | 地区 | 日期 | 天气信息 | 温度")
print("-" * 50) # 打印分隔线
for row in rows:
print(f"{row[0]:<5} | {row[1]:<5} | {row[2]:<10} | {row[3]:<10} | {row[4]:<10}")
# 关闭数据库连接
conn.close()
🌞运行结果截图
🌈心得体会
这个作业之前有做过,但是该天气网站似乎跟之前不太一样,当天的温度值显示出实时气温,而没有显示最高温和最低温,所以用之前的代码只能爬出6天的天气状况即不包括当天的天气。我改了很久,就连我自己都不知道自己怎么爬出来像现在这样的数据。(就在此时)刚刚又运行了改过的代码,发现相同的代码它又能把当天的最高位和最低温爬出来了。真的是大大的疑惑,,,一会而我会再看看怎么个事。现在我意识到了网页是在不断变化之中的,所以我们的爬虫代码也应该跟随它的变化而变化,从而才能保证数据的准确性和有效性。
作业②:爬取股票相关信息
✒️要求:用requests和自选提取信息方法定向爬取股票相关信息,并存储在数据库中。 候选网站:东方财富网:https://www.eastmoney.com/新浪股票:http://finance.sina.com.cn/stock/ 技巧:在谷歌浏览器中进入F12调试模式进行抓包,查找股票列表加载使用的url,并分析api返回的值,并根据所要求的参数可适当更改api的请求参数。根据URL可观察请求的参数f1、 f2可获取不同的数值,根据情况可删减请求的参数。
🗃️输出信息:Gitee文件夹链接
🌏解决思路
1.初始化数据库并创建表格 (initialize_db 函数):
🔹连接到SQLite数据库(如果数据库不存在则创建)。
🔹创建一个名为 stock_info 的表格,用于存储股票的各种信息,如股票代码、名称、价格等。
2.插入股票数据 (save_data 函数):
🔹接收一个包含股票信息的元组,并将其插入到 stock_info 表格中。
3.获取股票市场信息 (fetch_market_data 函数):
🔹发起HTTP GET请求到指定的API URL。
🔹解析返回的JSON数据,提取所需的股票信息。
🔹返回解析后的股票数据列表。
4.显示带有列名的数据并导出到Excel (show_stock_data 函数):
🔹从数据库中读取所有股票数据。
🔹使用 pandas 显示数据。
🔹将数据导出到Excel文件。
5.主逻辑 (main_program 函数):
🔹初始化数据库连接和游标。
🔹获取用户输入的页码范围。
🔹循环遍历指定页码范围,生成API URL,并获取股票数据。
🔹遍历股票数据列表,提取每只股票的信息,并保存到数据库中。
🔹提交事务并关闭数据库连接。
🔹显示存储的数据并导出到Excel。
🚀代码实现
import requests
import sqlite3
import json
import pandas as pd
# 初始化数据库并创建表格
def initialize_db():
connection = sqlite3.connect('market_data.db') # 创建或连接数据库
db_cursor = connection.cursor()
# 创建存储股票数据的表格
db_cursor.execute('''
CREATE TABLE IF NOT EXISTS stock_info (
symbol TEXT,
name TEXT,
latest_price REAL,
price_change_rate REAL,
price_change_amount REAL,
trade_volume INTEGER,
trade_value REAL,
amplitude REAL,
high_price REAL,
low_price REAL,
opening_price REAL,
closing_price REAL
)
''')
connection.commit()
return connection, db_cursor
# 插入股票数据
def save_data(db_cursor, stock_info):
db_cursor.execute('''
INSERT INTO stock_info (symbol, name, latest_price, price_change_rate, price_change_amount, trade_volume, trade_value, amplitude, high_price, low_price, opening_price, closing_price)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''', stock_info)
# 获取股票市场信息
def fetch_market_data(api_url):
try:
result = requests.get(api_url)
if result.status_code == 200:
data_start = result.text.find('(') + 1
data_end = result.text.rfind(')')
json_data = result.text[data_start:data_end]
parsed_data = json.loads(json_data)
return parsed_data['data']['diff']
else:
print(f"获取数据失败,状态码: {result.status_code}")
return []
except Exception as error:
print(f"请求出错: {error}")
return []
# 显示带有列名的数据并导出到Excel
def show_stock_data():
connection = sqlite3.connect('market_data.db')
query = "SELECT * FROM stock_info"
# 使用 pandas 读取数据库中的数据
dataframe = pd.read_sql_query(query, connection)
# 显示数据
print(dataframe)
# 将数据导出到 Excel
excel_filename = 'stock_data.xlsx'
dataframe.to_excel(excel_filename, index=False)
print(f"数据已囚禁到 {excel_filename} 文件中。")
connection.close()
# 主逻辑:抓取并存储股票数据
def main_program():
# 初始化数据库
connection, db_cursor = initialize_db()
# 获取用户输入的页码范围
start_pg = int(input("你想从哪页开始: "))
end_pg = int(input("到哪页结束: "))
# 循环抓取指定页码的数据
for page_num in range(start_pg, end_pg + 1):
print(f"快了!已经到第 {page_num} 页了!")
# 动态生成URL
api_url = f"https://1.push2.eastmoney.com/api/qt/clist/get?cb=jQuery112402758990905568719_1728977574693&pn={page_num}&pz=20&po=1&np=1&ut=bd1d9ddb04089700cf9c27f6f7426281&fltt=2&invt=2&dect=1&wbp2u=|0|0|0|web&fid=f3&fs=m:0+t:6,m:0+t:80,m:1+t:2,m:1+t:23,m:0+t:81+s:2048&fields=f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f12,f13,f14,f15,f16,f17,f18,f20,f21,f23,f24,f25,f22,f11,f62,f128,f136,f115,f152&_=1728977574694"
# 获取股票数据
stock_list = fetch_market_data(api_url)
# 遍历并存储每只股票的数据
for stock in stock_list:
stock_entry = (
stock.get('f12'), # 股票代码
stock.get('f14'), # 股票名称
stock.get('f2'), # 最新报价
stock.get('f3'), # 涨跌幅
stock.get('f4'), # 涨跌额
stock.get('f5'), # 成交量
stock.get('f6'), # 成交额
stock.get('f7'), # 振幅
stock.get('f15'), # 最高价
stock.get('f16'), # 最低价
stock.get('f17'), # 今开价
stock.get('f18'), # 昨收价
)
# 保存到数据库
save_data(db_cursor, stock_entry)
# 提交事务并关闭数据库
connection.commit()
connection.close()
print("数据已成功逮捕并关押在数据库!")
# 显示存储的数据并导出到 Excel
show_stock_data()
# 运行程序
if __name__ == "__main__":
main_program()
🌞运行结果截图
🌈心得体会
这是我第一次使用F12调试模式进行抓包,之后再查找股票列表加载使用的url,并分析api返回的值,这是一个不太容易的过程,但是我却从中学到很多。查看过我们所需要抓的包的内容并进行分析之后,才知道首先从请求返回的响应中提取出json格式的数据,再用属性来获取对应的值。其中回调函数的参数即是一个json对象,而回调函数的作用是实现异步代码的执行,这些零散的知识点都是我后来查的(虽然查的越多越困惑)。通过属性获取的值是一个列表,所以我们还需要用循环来遍历每个元素,在每个元素中再进行筛选,为的是输出我们所需要的数据。总之我认为难点就是对api返回数据的分析以及如何利用适合的方式(正则表达式,键值对,函数等)检索到我们所需要的数据。
作业③:爬取大学排名相关信息
✒️要求:爬取中国大学2021主榜(https://www.shanghairanking.cn/rankings/bcur/2021)所有院校信息,并存储在数据库中,同时将浏览器F12调试分析的过程录制Gif加入至博客中。技巧:分析该网站的发包情况,分析获取数据的api
🗃️输出信息:Gitee文件夹链接
Gif:
🌏解决思路
1.数据抓取
🔹使用 requests 模块从提供的API URL中抓取数据。
🔹通过正则表达式提取API返回的JavaScript中的大学名称和分数。
🔹univNameCn 是大学名称,score 是分数。
🔹re.compile(r'univNameCn:\s"([^"]+)",\s.?score:\s([\d.]+)', re.DOTALL) 匹配大学名称和分数。
2.数据保存
🔹通过 INSERT INTO 语句将提取的大学名称和分数插入SQLite数据库中。
🔹如果遇到数据库插入冲突(如相同大学名称的记录已经存在),使用 try-except 块来忽略重复记录。
3.数据显示和导出
🔹使用 pandas 读取数据库中的所有大学数据,并打印在控制台中。
🔹同时将数据导出为Excel文件(university_data.xlsx)。
🚀代码实现
import requests
import sqlite3
import json
import pandas as pd
import re
# 初始化数据库并创建表格
def initialize_db():
connection = sqlite3.connect('university_data.db')
db_cursor = connection.cursor()
db_cursor.execute('''
CREATE TABLE IF NOT EXISTS university_info (
id INTEGER PRIMARY KEY AUTOINCREMENT,
学校 TEXT NOT NULL,
分数 REAL NOT NULL
)
''')
connection.commit()
return connection, db_cursor
# 保存数据到数据库
def save_data(db_cursor, university_info):
try:
db_cursor.execute('''
INSERT INTO university_info (学校, 分数)
VALUES (?, ?)
''', university_info)
except sqlite3.IntegrityError:
# 如果记录已存在(基于学校名称的唯一性),则忽略插入
pass
# 从API获取大学数据
def fetch_university_data(api_url):
try:
result = requests.get(api_url)
result.raise_for_status() # 如果状态码不是200,将引发HTTPError异常
# 假设数据被包裹在某种JavaScript对象中,我们需要提取出JSON部分
# 注意:这里的字符串操作是基于假设的API响应格式,实际使用时可能需要调整
data_start = result.text.find('return {') + len('return {')
data_end = result.text.find('};', data_start) # 假设数据以'};'结尾
data = result.text[data_start:data_end]
# 使用正则表达式匹配所有大学数据
pattern = re.compile(r'univNameCn:\s*"([^"]+)",\s*.*?score:\s*([\d\.]+)', re.DOTALL)
matches = pattern.findall(data)
# 将匹配结果转换为结构化数据
universities = [{'university_name': match[0], 'score': float(match[1])} for match in matches]
return universities
except Exception as error:
print(f"请求出错: {error}")
return []
# 显示并导出数据到Excel
def show_university_data(connection):
query = "SELECT * FROM university_info"
dataframe = pd.read_sql_query(query, connection)
print(dataframe)
dataframe.to_excel('university_data.xlsx', index=False)
print("数据已导出到 university_data.xlsx 文件中。")
# 主逻辑:抓取并存储大学数据
def main_program():
connection, db_cursor = initialize_db()
api_url = "https://www.shanghairanking.cn/_nuxt/static/1728872418/rankings/bcur/2021/payload.js"
university_list = fetch_university_data(api_url)
for university in university_list:
save_data(db_cursor, (university['university_name'], university['score']))
connection.commit()
connection.close()
print("数据已成功存储在数据库中!")
# 重新打开数据库连接以显示和导出数据(或者可以在save_data后保持连接打开)
with sqlite3.connect('university_data.db') as connection:
show_university_data(connection)
# 运行程序
if __name__ == "__main__":
main_program()
🌞运行结果截图
🌈心得体会
我做这道题的过程可以说是非常非常非常非常非常非常非常非常的艰难。虽然之前有爬取过大学的排行榜,但是方法不同对我来说就相差很多。而且我对抓包的方法很不熟悉,上次做抓包还是前面的第二题,所以我受第二题的影响很大,以为把第二题的代码稍微改改就能在这题上运行出来,于是我改了很久很久很久,出现了各种各样的错误,gpt都快被我问冒烟了还是没能改出来。后来我意识到这两个包的内容并不相同,所以提取数据的方式不会一样,要分别进行分析。我本来还想用键值对的方式做这道题,所以想把响应内容中的json格式的内容提取出来,光是提取就花费了很大的功夫,结果当我想把这些内容利用函数json.loads()转换成python对象(字典之类的)时,一直报错,我不知道是为什么就一直改改改一直尝试,不知过了多久我才知道我从响应体中提取到的文本内容根本就不是json格式,他只是长得像而已,因为它的键并没有被双引号所包裹,所以这个办法是行不通的(真的心疼我的时间和精力)。然后我就开始用正则表达式来直接提取所需字段,这也花了很长时间,但是最后是能做出来的,庆幸庆幸。我做出来之后还翻看了其他同学的报告,发现他们有人json.load()成功了,我没有搞明白,难道是我提取响应体的内容不对吗,等我写完了这篇报告,再去细看。总之这次作业真的把我难到了。