数据采集实践第二次作业

Mr0due / 2024-12-29 / 原文

学号 姓名
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调试工具的使用。通过这样的记录,不仅为自己留下一份学习记录,也让其他人能够参考学习。
总结与反思
完成该作业后,我对网页结构分析、反爬机制应对、数据存储和调试工具的使用有了更深的理解。这次任务也让我意识到,在实际开发中可能遇到的挑战和应对策略。在未来的学习和实践中,我将继续完善自己的数据采集技能,并深入研究数据分析与应用的更多技巧。