資料採集實踐第二次作業

李迦勒發表於2024-10-26
學號 姓名
102202116 李迦勒
作業要求 點選這裡
目標 爬取天氣網、股票相關資訊、中國大學2021主榜所有院校資訊,並儲存在資料庫中
實驗二倉庫地址 點選這裡

作業①:

要求:在中國氣象網(http://www.weather.com.cn)給定城市集的 7日天氣預報,並儲存在資料庫。

點選檢視程式碼
from bs4 import BeautifulSoup
import urllib.request
import sqlite3

class WeatherDB:
    def openDB(self):
        self.con = sqlite3.connect("weathers.db")
        self.cursor = self.con.cursor()
        try:
            self.cursor.execute("CREATE TABLE IF NOT EXISTS weathers (wCity varchar(16), wDate varchar(16), wWeather varchar(64), wTemp varchar(32), PRIMARY KEY (wCity, wDate))")
        except Exception as err:
            print(f"Database creation error: {err}")
            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(f"Error inserting data: {err}")

    def show(self):
        self.cursor.execute("SELECT * FROM weathers")
        rows = self.cursor.fetchall()
        print("%-16s%-16s%-32s%-16s" % ("city", "date", "weather", "temp"))
        for row in rows:
            print("%-16s%-16s%-32s%-16s" % (row[0], row[1], row[2], row[3]))

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 = {"福州": "101230101"}  # 新增福州的城市程式碼

    def forecastCity(self, city):
        if city not in self.cityCode.keys():
            print(f"{city} code cannot be found")
            return

        url = f"http://www.weather.com.cn/weather/{self.cityCode[city]}.shtml"
        try:
            req = urllib.request.Request(url, headers=self.headers)
            with urllib.request.urlopen(req) as response:
                data = response.read()
                soup = BeautifulSoup(data, "html.parser")  # 使用html.parser
                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
                        print(city, date, weather, temp)
                        self.db.insert(city, date, weather, temp)
                    except Exception as err:
                        print(f"Error parsing data: {err}")
        except Exception as err:
            print(f"Error fetching data for {city}: {err}")

    def process(self, cities):
        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")

輸出結果

心得體會

在本次實驗中,我透過使用Python的BeautifulSoup庫來解析網頁,成功地從複雜的HTML結構中提取了所需的資訊。這個過程中,我學習到了如何識別和定位網頁中的特定標籤和屬性,這對於精確抓取資料至關重要。我意識到,資料抓取不僅僅是下載網頁內容,更重要的是理解網頁的結構和邏輯,這樣才能有效地提取出有用的資料。

在處理資料時,我遇到了資料格式不一致的問題,比如日期和時間的表示方式多種多樣。這讓我認識到,在資料科學中,資料預處理是一個不可或缺的步驟。我學會了如何使用正規表示式來清洗和統一資料格式,確保資料的一致性和準確性。

此外,我還學習瞭如何使用Pandas庫來處理和分析資料。透過建立DataFrame,我可以輕鬆地對資料進行排序、篩選和統計分析。這個過程不僅提高了我的資料處理能力,也加深了我對資料科學流程的理解。

作業②

用 requests 和 BeautifulSoup 庫方法定向爬取東方財富網(http://quote.eastmoney.com/center/gridlist.html#hk_sh_stocks) 的股票相關資訊,並儲存在資料庫中。
– 技巧:在谷歌瀏覽器中進入 F12 除錯模式進行抓包,查詢股票列表載入使用的 url,並分析 api 返回的值,並根據所要求的引數可適當更改api 的請求引數。根據 URL 可觀察請求的引數 f1、f2 可獲取不同的數值,根據情況可刪減請求的引數

點選檢視程式碼
import requests
import pandas as pd
import sqlite3
import json
import time
from concurrent.futures import ThreadPoolExecutor, as_completed

# 用get方法訪問伺服器並提取頁面資料
def getHtml(page):
    url = f"https://78.push2.eastmoney.com/api/qt/clist/get?cb=jQuery112408723133727080641_1728978540544&pn={page}&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&_=1728978540545""
    try:
        r = requests.get(url)
        r.raise_for_status()
        json_data = r.text[r.text.find("{"):r.text.rfind("}")+1]  # 提取 JSON 資料部分
        data = json.loads(json_data)
        return data
    except requests.RequestException as e:
        print(f"Error fetching data from page {page}: {e}")
        return None

# 獲取單個頁面股票資料
def getOnePageStock(page):
    data = getHtml(page)
    if not data or 'data' not in data or 'diff' not in data['data']:
        return []
    return data['data']['diff']

# 將股票資訊儲存到SQLite資料庫
def saveToDatabase(stock_list):
    conn = sqlite3.connect('stocks.db')
    c = conn.cursor()
    try:
        c.execute('''CREATE TABLE IF NOT EXISTS stocks
                     (code TEXT PRIMARY KEY, name TEXT, price REAL, change REAL, percent_change REAL, volume INTEGER, amount REAL)''')
        for stock in stock_list:
            c.execute("INSERT OR IGNORE INTO stocks VALUES (?, ?, ?, ?, ?, ?, ?)",
                      (stock.get('f12'), stock.get('f14'), stock.get('f2'), stock.get('f3'), stock.get('f4'), stock.get('f5'), stock.get('f6')))
        conn.commit()
    except sqlite3.Error as e:
        print(f"Database error: {e}")
    finally:
        c.execute("SELECT * FROM stocks")
        rows = c.fetchall()
        df = pd.DataFrame(rows, columns=['Code', 'Name', 'Price', 'Change', 'Percent Change', 'Volume', 'Amount'])
        print(df)
        conn.close()

def main():
    all_stocks = []
    max_pages = 100  # 設定要爬取的最大頁數
    with ThreadPoolExecutor(max_workers=10) as executor:
        future_to_page = {executor.submit(getOnePageStock, page): page for page in range(1, max_pages + 1)}
        for future in as_completed(future_to_page):
            page = future_to_page[future]
            try:
                stock_list = future.result()
                if stock_list:
                    all_stocks.extend(stock_list)
                else:
                    print(f"未能獲取到第{page}頁的股票資料")
            except Exception as e:
                print(f"Error processing page {page}: {e}")

    if all_stocks:
        print("爬取到的股票資料:")
        for stock in all_stocks:
            print(stock)
        saveToDatabase(all_stocks)
        print("股票資訊已成功儲存到資料庫。")
    else:
        print("未能獲取到任何股票資料")

if __name__ == "__main__":
    main()
## 執行結果

心得體會

在本次實驗中,我深刻體會到了API抓包在現代資料採集中的重要性。與傳統的網頁解析方法相比,API抓包能夠直接訪問資料來源,以一種更加高效和系統化的方式獲取所需資訊。在嘗試獲取東財網的股票資料時,我發現API提供了一種標準化的資料格式,這大大簡化了資料處理和分析的流程。

我認識到,雖然API抓包為我們提供了一種快速獲取資料的途徑,但資料採集的複雜性仍然存在。例如,東財網的資料並不是以簡單的JSON格式直接返回,而是被封裝在JavaScript回撥函式中。這就需要我不僅要理解API的響應結構,還要掌握字串處理的技巧,以便從響應中提取出有用的資料。

在這個過程中,我學會了如何使用正規表示式和字串分割方法來解析複雜的資料格式。這種技能對於處理各種非標準資料格式非常有用,因為它可以幫助我從混亂的資料中提取出有價值的資訊。

此外,我還學習瞭如何使用網路抓包工具,如Wireshark或Fiddler,來監控和分析API請求和響應。這些工具對於理解API的工作原理和除錯資料採集過程中的問題非常有幫助。

作業③:

要求:爬取中國大學 2021 主榜(https://www.shanghairanking.cn/rankings/bcur/2021 )所有院校資訊,並儲存在資料庫中,同時將瀏覽器 F12 除錯分析的過程錄製 Gif 加入至部落格中。
img

點選檢視程式碼
import urllib.request
from bs4 import BeautifulSoup
import re

# 目標網址
url = "http://www.shanghairanking.cn/rankings/bcur/2020"

# 使用 urllib 請求網頁內容
response = urllib.request.urlopen(url)
html_content = response.read()

# 使用 BeautifulSoup 解析 HTML
soup = BeautifulSoup(html_content, 'html.parser')

# 找到包含排名資訊的表格
table = soup.find('table')

# 列印標題
print(f"{'排名'.ljust(6)}{'學校名稱'.ljust(20)}{'省市'.ljust(10)}{'學校型別'.ljust(10)}{'總分'.ljust(6)}")
print("-" * 60)  # 列印分隔線

# 遍歷表格的每一行
for row in table.find_all('tr')[1:]:  # 跳過標題行
    # 提取每行的資料
    cols = row.find_all('td')
    rank = cols[0].text.strip()

    # 獲取學校名稱的中文部分
    school_name_full = cols[1].get_text(strip=True, separator=" ")
    # 使用正規表示式匹配所有中文字元
    school_name = re.findall(r'[\u4e00-\u9fa5]+', school_name_full)
    school_name = ''.join(school_name) if school_name else "未知"

    province = cols[2].text.strip()
    school_type = cols[3].text.strip()
    total_score = cols[4].text.strip()

    # 列印提取的資訊,使用格式化字串確保排列工整
    print(f"{rank.ljust(6)}{school_name.ljust(20)}{province.ljust(10)}{school_type.ljust(10)}{total_score.ljust(6)}")

執行結果

在完成此次資料採集的過程中,我收穫了許多寶貴的經驗和技能。從最初的需求分析到最終的資料儲存,每一步都對我來說是一種提升。

瀏覽器除錯分析的學習
透過F12除錯工具,我深入理解了頁面載入的原理和資料介面的獲取方式。除錯工具不僅幫助我找到資料的介面路徑,還讓我學會如何使用“網路”選項來捕獲資料請求和響應。在實際操作中,遇到過一些頁面透過JavaScript動態載入資料的問題,除錯工具讓我能清楚地分析到資料的載入節點並進行精準抓取。
資料爬取的實現
資料爬取時,我選擇使用Python的requests庫進行HTTP請求。起初遇到了一些反爬機制,例如User-Agent攔截等,我透過偽裝請求頭、延時操作等手段進行繞過。整個過程讓我意識到,網路爬蟲不僅僅是資料的簡單採集,更是對網路請求結構、反爬策略的一種理解和破解。
資料儲存
將資料儲存在資料庫中是此次作業的重要環節之一。我設計了適合的資料表結構,將大學的排名、名稱、地區等資訊合理儲存。在這個過程中,我掌握瞭如何在資料採集後進行清洗、去重、儲存等資料處理操作,為後續的資料分析打下了堅實的基礎。

實驗心得體會

在完成此次資料採集的過程中,我收穫了許多寶貴的經驗和技能。從最初的需求分析到最終的資料儲存,每一步都對我來說是一種提升。

瀏覽器除錯分析的學習
透過F12除錯工具,我深入理解了頁面載入的原理和資料介面的獲取方式。除錯工具不僅幫助我找到資料的介面路徑,還讓我學會如何使用“網路”選項來捕獲資料請求和響應。在實際操作中,遇到過一些頁面透過JavaScript動態載入資料的問題,除錯工具讓我能清楚地分析到資料的載入節點並進行精準抓取。
資料爬取的實現
資料爬取時,我選擇使用Python的requests庫進行HTTP請求。起初遇到了一些反爬機制,例如User-Agent攔截等,我透過偽裝請求頭、延時操作等手段進行繞過。整個過程讓我意識到,網路爬蟲不僅僅是資料的簡單採集,更是對網路請求結構、反爬策略的一種理解和破解。
資料儲存
將資料儲存在資料庫中是此次作業的重要環節之一。我設計了適合的資料表結構,將大學的排名、名稱、地區等資訊合理儲存。在這個過程中,我掌握瞭如何在資料採集後進行清洗、去重、儲存等資料處理操作,為後續的資料分析打下了堅實的基礎。
部落格記錄與GIF製作
在部落格中記錄整個分析與實現過程,讓我更加系統地梳理了每個步驟。在錄製GIF過程中,我透過簡潔明瞭的畫面展示了除錯分析的關鍵操作,希望能幫助讀者更直觀地理解和掌握F12除錯工具的使用。透過這樣的記錄,不僅為自己留下一份學習記錄,也讓其他人能夠參考學習。
總結與反思
完成該作業後,我對網頁結構分析、反爬機制應對、資料儲存和除錯工具的使用有了更深的理解。這次任務也讓我意識到,在實際開發中可能遇到的挑戰和應對策略。在未來的學習和實踐中,我將繼續完善自己的資料採集技能,並深入研究資料分析與應用的更多技巧。

相關文章