Live2D

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可获取不同的数值,根据情况可删减请求的参数。

    • 参考链接:https://zhuanlan.zhihu.com/p/50099084

    • 输出信息:

      序号 股票代码 股票名称 最新报价 涨跌幅 涨跌额 成交量 成交额 振幅 最高 最低 近开 昨收
      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会报错,解析的不正确。这道题要求存到数据库中,再次学习到将数据保存到数据库编写代码流程。

作业③:

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代码,并且转成字符串进行操作很幸运,成功大半,之后用正则爬取特定数据,又去学习了一次,中间波折颇多但幸好结局是好的。总之,爬取特定数据时要细心观察,解析网页链接爬得多知道的自然就多了,多做几次就能熟练了。

posted @   lu_lu_l  阅读(65)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示