2023数据采集与融合技术实践作业二
作业①:
- Gitee文件夹链接:Gitee链接
- 要求:
-
在中国气象网(http://www.weather.com.cn)给定城市集的7日天气预报,并保存在数据库。
-
输出信息:
序号 地区 日期 天气信息 温度 1 北京 8日(今天) 小雨转晴 ...
-
代码:
- 数据库操作
# 天气
import urllib.request
import sqlite3
from bs4 import BeautifulSoup
from bs4 import UnicodeDammit
# 定义一个数据库操作类
class WeatherDB:
def __init__(self):
self.con = None
self.cursor = None
# 打开数据库
def openDB(self):
self.con = sqlite3.connect("weathers.db")
self.cursor = self.con.cursor()
try:
self.cursor.execute(
"create table weathers (id integer primary key autoincrement, wCity varchar(16), wDate varchar(16),
wWeather varchar(64), wTemp varchar(32))")
except:
self.cursor.execute("delete from weathers")
# 关闭数据库
def closeDB(self):
self.con.commit()
self.con.close()
# 插入数据到数据库
def insert(self, city, date, weather, temp):
try:
self.cursor.execute("insert into weathers (wCity,wDate,wWeather,wTemp) values (?,?,?,?)",
(city, date, weather, temp))
except Exception as err:
print(err)
# 查询并显示所有天气记录
def show(self):
self.cursor.execute("select * from weathers")
rows = self.cursor.fetchall()
print("%-8s%-16s%-16s%-32s%-16s" % ("id", "city", "date", "weather", "temp"))
for row in rows:
print("%-8s%-16s%-16s%-32s%-16s" % (row[0], row[1], row[2], row[3], row[4]))
- 天气预报爬取并保存到数据库中
# 定义一个天气预报类
class WeatherForecast:
def __init__(self):
self.headers = {
"User-Agent": "Mozilla/5.0 (Windows; U; Windows NT 6.0 x64; en-US; rv:1.9pre) Gecko/2008072421 Minefield/3.0.2pre"}
self.cityCode = {"北京": "101010100", "上海": "101020100", "广州": "101280101", "深圳": "101280601"}
# 获取指定城市的天气预报信息
def forecastCity(self, city):
if city not in self.cityCode.keys():
print(city + " code cannot be found")
return
url = "http://www.weather.com.cn/weather/" + self.cityCode[city] + ".shtml"
try:
# 向URL发送HTTP请求,获取天气网页数据
req = urllib.request.Request(url, headers=self.headers)
data = urllib.request.urlopen(req)
data = data.read()
# 使用UnicodeDammit库判断网页编码,并转换为Unicode字符串
dammit = UnicodeDammit(data, ["utf-8", "gbk"])
data = dammit.unicode_markup
# 使用BeautifulSoup库解析网页数据
soup = BeautifulSoup(data, "html.parser")
# 根据CSS选择器提取HTML标签中的天气信息
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
temp = li.select('p[class="tem"] span')[0].text + "/" + li.select('p[class="tem"] i')[0].text
# 将天气数据插入到数据库中
self.db.insert(city, date, weather, temp)
except Exception as err:
print(err)
except Exception as err:
print(err)
# 处理指定城市列表的天气预报信息
def process(self, cities):
# 创建WeatherDB类对象,并打开数据库连接
self.db = WeatherDB()
self.db.openDB()
# 遍历所有城市,依次获取和存储天气数据
for city in cities:
self.forecastCity(city)
# 打印所有天气记录
self.db.show()
# 关闭数据库连接
self.db.closeDB()
ws = WeatherForecast()
# 处理指定的城市集的天气预报信息
ws.process(["北京", "上海", "广州", "深圳"])
print("completed")
- 解析网页数据,遍历获取特定数据
# 使用BeautifulSoup库解析网页数据
soup = BeautifulSoup(data, "html.parser")
# 根据CSS选择器提取HTML标签中的天气信息
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
temp = li.select('p[class="tem"] span')[0].text + "/" + li.select('p[class="tem"] i')[0].text
# 将天气数据插入到数据库中
self.db.insert(city, date, weather, temp)
输出结果
心得体会
对于天气这个主要是代码复现,完成的还是快的,但是突然发现运行结果有点问题,一开始以为是打印信息(ps:无脑相信老师的代码不会出现问题,运行之后都没仔细看运行结果),区别城市之间的信息,后来发现是每个城市的第一天没有信息,那这极有可能是提取标签信息错误,所以又F12去找,然后就发现如下所示:
![]() 结构不一样,会导致没有今天的数据 |
![]() 结构一样,爬取完整 |
所以这个页面结构是在变化的,不凑巧的时候就会缺少今天的信息。
总之,这个代码又熟悉了一次requests和BeautifulSoup获取网页解析网页信息,查找需要的值以及sqlite3库、操作数据库。
作业②
- Gitee文件夹链接:Gitee链接
- 要求:
-
用requests和BeautifulSoup库方法定向爬取股票相关信息,并存储在数据库中。
-
候选网站:东方财富网:https://www.eastmoney.com/
新浪股票:http://finance.sina.com.cn/stock/ -
技巧:在谷歌浏览器中进入F12调试模式进行抓包,查找股票列表加载使用的url,并分析api返回的值,并根据所要求的参数可适当更改api的请求参数。根 据URL可观察请求的参数f1、f2可获取不同的数值,根据情况可删减请求的参数。
-
输出信息:
序号 股票代码 股票名称 最新报价 涨跌幅 涨跌额 成交量 成交额 振幅 最高 最低 近开 昨收 1
-
代码:
- 找到之后还要解析,根据参考链接写出解析代码
# 用 get 方法访问服务器并提取页面数据
def get_html():
# 通过抓包获取url
url = "https://83.push2.eastmoney.com/api/qt/clist/get?cb=jQuery1124025224620015255605_1696694112861&pn=1&pz=20&po=" \
"1&np=1&ut=bd1d9ddb04089700cf9c27f6f7426281&fltt=2&invt=2&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&_=1696694112862"
response = requests.get(url)
jsonp = response.text
# 利用切片获取有效的 JSON 部分
json_data = jsonp[jsonp.index('(') + 1: jsonp.rindex(')')]
# 解析为 Python 对象
data = json.loads(json_data)
return data
- 将数据保存到数据库,所要先创建数据库建立连接,创建表格stock_data,然后插入数据,通过遍历,一定要看好对应关系不要怕错行。
# 将数据保存到数据库
def save_to_database(data):
con = sqlite3.connect('shares.db')
# 创建表格(如果不存在)
shares_table = '''
CREATE TABLE IF NOT EXISTS stock_data (
id INTEGER PRIMARY KEY AUTOINCREMENT,
code VARCHAR(10),
name VARCHAR(255),
latest_price DECIMAL(10, 2),
price_limit DECIMAL(10, 2),
price_range DECIMAL(10, 2),
turnover BIGINT,
volume_transaction DECIMAL(18, 2),
amplitude DECIMAL(10, 2),
highest DECIMAL(10, 2),
lowest DECIMAL(10, 2),
today_open DECIMAL(10, 2),
yesterday_close DECIMAL(10, 2)
)
'''
with con:
con.execute(shares_table)
# 插入数据
for item in data["data"]["diff"]:
code = item["f12"] # 代码
name = item["f14"] # 名称
latest_price = float(item["f2"]) # 最新价
price_limit = float(item["f3"]) # 涨跌幅
price_range = float(item["f4"]) # 涨跌额
turnover = int(item["f5"]) # 成交量(手)
volume_transaction = float(item["f6"]) # 成交额
amplitude = float(item["f7"]) # 振幅
highest = float(item["f15"]) # 最高
lowest = float(item["f16"]) # 最低
today_open = float(item["f17"]) # 今开
yesterday_close = float(item["f18"]) # 昨收
insert_query = '''
INSERT INTO stock_data (code, name, latest_price, price_limit, price_range, turnover, volume_transaction,
amplitude, highest, lowest, today_open, yesterday_close)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
'''
con.execute(insert_query, (code, name, latest_price, price_limit, price_range, turnover, volume_transaction,
amplitude, highest, lowest, today_open, yesterday_close))
- 由于数据库表格显示不方便,为了更好看到爬取的信息,我们从数据库表中查询数据,显示到终端来,可以直接看到爬取信息
def fetch_data_from_database():
con = sqlite3.connect('shares.db')
cursor = con.cursor()
# 查询数据
select_query = "SELECT code, name, latest_price, price_limit, price_range, turnover, volume_transaction, " \
"amplitude, highest, lowest, today_open, yesterday_close FROM stock_data"
cursor.execute(select_query)
rows = cursor.fetchall()
# 输出表格表头
header = ["代码", "名称", "最新价", "涨跌幅", "涨跌额", "成交量(手)", "成交额", "振幅", "最高", "最低", "今开", "昨收"]
# 获取每列的最大宽度
col_widths = [len(col) for col in header]
for row in rows:
for i, col in enumerate(row):
col_widths[i] = max(col_widths[i], len(str(col)))
# 打印表格
for i, col in enumerate(header):
print(col.ljust(col_widths[i]), end=" ")
print()
for row in rows:
for i, col in enumerate(row):
print(str(col).ljust(col_widths[i]), end=" ")
print()
# 关闭连接
con.close()
输出结果
心得体会
学习到一种新的方式,用抓包的方式去获得url,但是想真正使用这个链接,就还要使用json解析,不太好解析成需要的数据,所以这里有使用的是jsonp,使用json会报错,解析的不正确。这道题要求存到数据库中,再次学习到将数据保存到数据库编写代码流程。
作业③:
- Gitee文件夹链接:Gitee链接
- 要求:
-
爬取中国大学2021主榜(https://www.shanghairanking.cn/rankings/bcur/2021)所有院校信息,并存储在数据库中,同时将浏览器F12调试分析 的过程录制Gif加入至博客中。
-
技巧:分析该网站的发包情况,分析获取数据的api
-
输出信息:
排名 学校 省市 类型 总分 1 清华大学 北京 综合 969.2
-
F12调试分析过程.Gif
代码:
- 找到url想要解析成Python可执行的JavaScript代码也是不容易,对于这部分代码能够解析成想要JS代码主要是借助班群里发的方法,感谢大神。最后输出的是字符串s
# 抓包得到的url.js链接
url = 'https://www.shanghairanking.cn/_nuxt/static/1695811954/rankings/bcur/202111/payload.js'
req = requests.get(url, timeout=20)
if req.status_code == 200:
req.encoding = 'utf-8'
html = req.text
js_function = html[len('__NUXT_JSONP__("/rangkings/bcur/202111",('):-3]
js_code = f"console.log({js_function})" # 从网页内容中提取出一个JavaScript函数名,并将其赋值给js_function变量。这个函数包含了所需的数据
js = js2py.EvalJs() # 创建一个JavaScript环境
# 将js_code转换为Python可执行的代码
pys = js2py.translate_js(js_code) # 将JavaScript代码转换为Python可执行的代码字符串
f = StringIO()
with redirect_stdout(f): # 将标准输出重定向到f对象
exec(pys) # 执行Python可执行的JavaScript代码
s = f.getvalue()
# print(s)
# print(type(s)) # 字符串
- 打印字符串s观察内容,发现想要的内容,
然后使用正则提取出来,看着是挺简单的,但现实是残酷的,一直爬取不出来,都是空的。寻求朋友发现少打了空格,再仔细点观察“:”后面还真是隔着挺大空的。
rank = re.findall("'rankOverall': '(.*?)'", s, re.S | re.M) # 排名
name = re.findall("'univNameCn': '(.*?)'", s, re.S | re.M) # 学校
province = re.findall("'province': '(.*?)'", s, re.S | re.M) # 省市
category = re.findall("'univCategory': '(.*?)'", s, re.S | re.M) # 类型
score = re.findall("'score': (.*?),", s, re.S | re.M) # 总分
- 之后将匹配到的数据保存到university_data列表中
university_data = []
# 将匹配到的数据保存到university_data列表中
for i in range(len(rank)):
university_data.append((rank[i], name[i], province[i], category[i], score[i]))
- 剩下就是数据库的操作
# 大学数据库操作类
class UniversityDB:
def __init__(self):
self.con = None
self.cursor = None
# 打开数据库
def openDB(self):
try:
# 连接SQLite数据库并创建表
self.con = sqlite3.connect("university_rank.db")
self.cursor = self.con.cursor()
self.cursor.execute("create table if not exists university(Rank varchar(10),School varchar(10),"
"Province varchar(10),Category varchar(10),Score varchar(10))")
except Exception as err:
print(err)
# 关闭数据库
def closeDB(self):
if self.con:
self.con.commit()
self.con.close()
# 插入数据到数据库
def insert(self, university_data):
try:
self.cursor.executemany("insert into university values (?,?,?,?,?)", university_data)
except Exception as err:
print(err)
# 查询并显示所有大学记录
def show(self):
self.cursor.execute("select * from university")
rows = self.cursor.fetchall()
print("%-8s%-16s%-16s%-32s%-16s" % ("Rank", "School", "Province", "Category", "Score"))
for row in rows:
print("%-8s%-16s%-16s%-32s%-16s" % (row[0], row[1], row[2], row[3], row[4]))
- 定义好数据库类就可以实例化对象了
db = UniversityDB()
db.openDB()
db.insert(university_data)
db.show()
db.closeDB()
输出结果
心得体会
抓包得到的js真的不是很好操作,主要是对应关系找不明白,知道在哪但是爬取不出来的那种无力感,就是方法知道不会用。所以最后能将这个url解析成Python可执行的JavaScript代码,并且转成字符串进行操作很幸运,成功大半,之后用正则爬取特定数据,又去学习了一次,中间波折颇多但幸好结局是好的。总之,爬取特定数据时要细心观察,解析网页链接爬得多知道的自然就多了,多做几次就能熟练了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)