【数据采集与融合技术】 第二次大作业

作业①:

  • 要求:在中国气象网(http://www.weather.com.cn)给定城市集的7日天气预报,并保存在数据库。

  • 输出信息:

    序号 地区 日期 天气信息 温度
    1 北京 7日(今天) 晴间多云,北部山区有阵雨或雷阵雨转晴转多云 31℃/17℃
    2 北京 8日(明天) 多云转晴,北部地区有分散阵雨或雷阵雨转晴 34℃/20℃
    3 北京 9日(后台) 晴转多云 36℃/22℃
    4 北京 10日(周六) 阴转阵雨 30℃/19℃
    5 北京 11日(周日) 阵雨 27℃/18℃
    ......

1.思路及核心代码

1.1 数据库类

首先设计一个天气数据库类,里面包括打开数据库、关闭数据库、插入数据库和查看数据库内容的方法。

class WeatherDB:
    # 打开数据库的方法
    def openDB(self):
        self.con = sqlite3.connect("weathers.db")
        self.cursor = self.con.cursor()
        try:
            # 数据库不存在则创建
            self.cursor.execute(
                "create table weathers (wCity varchar(16),wDate varchar(16),wWeather varchar(64),wTemp varchar(32),"
                "constraint pk_weather primary key (wCity,wDate))")  # 根据数据库实体完整性,将城市和地区设置为主码
        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("%-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]))

1.2 天气预报爬取类

WeatherForecast这个类能够实现爬取目标城市的天气并将其输出,以及将数据写入数据库。

构造函数__init__(self)初始化请求头和爬取的城市对应的ID。

forecastCity()方法是这个实现主要功能的函数,能够爬取数据并且解析,将目标城市的天气输出以及将数据写入数据库。

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", "上海": "101020100", "广州": "101280101", "深圳": "101280601",
                         "北京": "101010100", "厦门": "101230201"}

    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:
            req = urllib.request.Request(url, headers=self.headers)
            data = urllib.request.urlopen(req)
            data = data.read()
            dammit = UnicodeDammit(data, ["utf-8", "gbk"])
            data = dammit.unicode_markup
            soup = BeautifulSoup(data, "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(err)
        except Exception as err:
            print(err)

    # 整个过程的方法
    def process(self, cities):
        self.db = WeatherDB()
        self.db.openDB()

        for city in cities:
            self.forecastCity(city)

        # self.db.show()
        self.db.closeDB()

1.3 调用类方法进行爬取

ws = WeatherForecast()  # 创建一个WeatherForecast类
ws.process(["福州", "厦门", "北京", "上海", "广州", "深圳"])  # 爬取这几个城市天气数据
print("completed")

1.4 结果

输出控制台的数据

数据库可视化

1.5 码云链接: 作业1

2. 心得体会

在这个作业中,我学会了爬取多个城市的天气数据,并且第一次接触了sqlite数据库。sqlite是一款是一种嵌入式数据库,体积小巧,非常适合新手入门学习。

我学会了通过Python进行数据库的一系列操作,包括创建表、插入表等,受益匪浅。

同时,复习了用面向对象的方法来编程,通过编写各种类来进行爬取数据。

作业②:

  • 要求:用requests和BeautifulSoup库方法定向爬取股票相关信息。

  • 候选网站:东方财富网:http://quote.eastmoney.com/center/gridlist.html#hs_a_board

  • 技巧:在谷歌浏览器中进入F12调试模式进行抓包,查找股票列表加载使用的url,并分析api返回的值,并根据所要求的参数可适当更改api的请求参数。根据URL可观察请求的参数f1、f2可获取不同的数值,根据情况可删减请求的参数。

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

  • 输出信息:

序号 股票代码 股票名称 最新报价 涨跌幅 涨跌额 成交量 成交额 振幅 最高 最低 今开 昨收
1 688093 N世华 28.47 62.22% 10.92 26.13万 7.6亿 22.34 32.0 28.08 30.2 17.55
......

1.思路及核心代码

1.1网页分析

首先进入网站,进入F12调试模式进行抓包。通过分析得到了我们想要的js,这个js的请求URL中的fields中的参数f1,f2,……中包含我们需要的字段,如下图所示。

通过仔细比对,可以得出我们需要爬取的字段参数如下:

"股票代码": 'f12', 
"股票名称": 'f14', 
"最新报价": 'f2', 
"涨跌幅": 'f3', 
"涨跌额": 'f4', 
"成交量": 'f5',
"成交额": 'f6',
"振幅": 'f7', 
"最高": 'f15', 
"最低": 'f16', 
"今开": 'f17', 
"昨收": 'f18'

1.2设计股票数据库类

StockDB类与上一题的大同小异,但是,由于本次需要爬取的字段众多,所以一个一个手打并不是明智的选择。

一些小技巧:

  1. 在插入数据的时候,可以用executemany()插入多条数据元组的列表。

  2. 在输出数据库的数据信息的时候,format()函数的参数可以支持可迭代的数据类型,只需要在listtuple之前加上*号。

    例如

    ls=[1,2,3,4]
    print("{}{}{}{}".format(*ls))
    

    输出:1234

    效果与print("{}{}{}{}".format(ls[0],ls[1],ls[2],ls[3]))是一模一样的,本作业中使用这个可以大大减少代码量。

# 插入数据的方法
    def insert(self, stock_List):
        try:
            self.cursor.executemany("insert into stock (股票代码 , 股票名称 ,最新报价 , 涨跌幅 ,涨跌额 , 成交量 ,成交额 ,振幅 , 最高 , 最低 , 今开 , 昨收 ) values (?,?,?,?,?,?,?,?,?,?,?,?)", stock_List)
        except Exception as err:
            print(err)
def show(self):
    self.cursor.execute("select * from stock")
    rows = self.cursor.fetchall()
    print("{:8}\t{:16}\t{:8}\t{:8}\t{:8}\t{:8}"
          "\t{:16}\t{:8}\t{:8}\t{:8}\t{:8}\t{:8}".format("股票代码", "股票名称", "最新报价", "涨跌幅", "涨跌额",
                                                         "成交量", "成交额", "振幅", "最高", "最低", "今开", "昨收",
                                                         chr(12288)))
    for row in rows:
        print("{:8}\t{:16}\t{:8}\t{:8}\t{:8}\t{:8}"
              "\t{:16}\t{:8}\t{:8}\t{:8}\t{:8}\t{:8}".format(*row, chr(12288)))

1.3 爬取网页

由于爬取的数据是json格式的字符串,所以最好是使用json包中的json.loads()来进行解析,可以省时省力。

然后将爬取的数据存入stockList2中,这个list的每个元素是一个元组,然后可以用executemany()配合插入多条元组组成的列表

req = requests.get(url)
req.raise_for_status()
req.encoding = req.apparent_encoding
data = req.text
data = re.search(r'\[.*]', data).group()  # 截取出json格式的数据
stockList = re.findall(r'{.*?}', data)  # 得到的是json格式的列表
stockList = [json.loads(x) for x in stockList]  # 得到python的dict格式数据
att = {"股票代码": 'f12', "股票名称": 'f14', "最新报价": 'f2', "涨跌幅": 'f3', "涨跌额": 'f4', "成交量": 'f5', "成交额": 'f6',
       "振幅": 'f7', "最高": 'f15', "最低": 'f16', "今开": 'f17', "昨收": 'f18'}  # 要爬取的字段及其对应的url参数
stockList2 = []  # 用来存储整个数据表
for stock in stockList:
    ls = []
    for i in att:
        ls.append(stock[att[i]])
    ls = tuple(ls)
    stockList2.append(ls)

db = StockDB()
db.openDB()
db.insert(stockList2)
print("成功导入数据库")
db.show()
db.closeDB()

1.4结果
控制台输出

数据库可视化

1.5 码云链接: 作业2

2.心得体会

  1. 本次需要爬取的字段众多,所以我通过探索学会了在format()函数中通过*加上可迭代的数据类型进行传递参数,节省了重复枯燥的代码。

  2. 用json包可以很方便的解析json格式的字符串

作业③:

  • 要求: 爬取中国大学2021主榜 https://www.shanghairanking.cn/rankings/bcur/2021
    所有院校信息,并存储在数据库中,同时将浏览器F12调试分析的过程录制Gif加入至博客中。
  • 技巧: 分析该网站的发包情况,分析获取数据的api
  • 输出信息:
排名 学校 总分
1 清华大学 969.2

1.思路及核心代码

1.1 分析网页

首先进入网站,进入F12调试模式进行抓包。通过分析得到了我们想要的js,如下图所示。

通过分析js的内容可以发现,使用第二题的json解析并不方便,所以使用正则表达式进行匹配。

1.2 编写数据库类

与作业1大同小异,需要注意的是,本次爬取的内容中,score字段可能为不是数值,所以需要在存入和取出数据库的时候进行适当的转换。在数据库类的show()方法中,数据库中的值为NULL,则对应在python则为None,在输出时,将其转换为’-‘。

同时,在format()中也使用和作业2一样的方法传递参数。

def show(self):
    self.cursor.execute("select * from University")
    rows = self.cursor.fetchall()
    print("{: ^3}{: ^10}{: ^5}".format('排名', '大学', '总分'))
    for row in rows:
        #  判断score字段是否为None,如果是,则设置为‘-’
        if row[2] is None:
            row = list(row)
            row[2] = '-'
        print("{: ^3}{: ^13}{: ^5}".format(*row))

1.3 爬取和提取目标文本

req = requests.get(url)
req.raise_for_status()
req.encoding = req.apparent_encoding
data = req.text
usName = re.findall(r'univNameCn:".*?"', data)  # 匹配所有的大学名称
usName = [x[x.find('"') + 1:-1] for x in usName]  # 提取出大学名称
score = re.findall(r'score:.*?,', data)  # 匹配所有的大学分数
score = [x[x.find(":") + 1:-1] for x in score]  # 提取出大学分数

1.4 将异常的score值转换为None

temp = []
for x in score:
    # 判断分数是否是数值,如果不是,则设置为None,None插入数据库会自动转换为Null
    try:
        temp.append(eval(x))
    except Exception as e:
        temp.append(None)
score = temp

1.5 存入数据库

University = UniversityDB()
University.openDB()
for i in range(len(usName)):
    University.insert(i+1, usName[i], temp[i])
University.show()
University.closeDB()

1.6 结果

程序输出

数据库可视化

1.7 代码链接

作业3

2.心得体会

在爬取到异常值后,要对异常值进行处理再存入数据库。数据库中的值为NULL,则对应在python则为None

posted @ 2021-10-21 18:19  暴走大可可  阅读(96)  评论(0编辑  收藏  举报