數據采集與融合技術實踐--作業二
數據采集與融合技術作業二
??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()成功了,我沒有搞明白,難道是我提取響應體的內容不對嗎,等我寫完了這篇報告,再去細看。總之這次作業真的把我難到了。

浙公網安備 33010602011771號