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

发布时间 2023-10-09 11:11:07作者: 兔叽汁汁

作业①

爬取中国气象网

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

from bs4 import BeautifulSoup
from bs4 import UnicodeDammit
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 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]))

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:
            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, "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
                    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.closeDB()

ws = WeatherForecast()
ws.process(["北京", "上海", "广州", "深圳"])
print("completed")

运行结果:
数据被存储在weathers.db数据集中,同时程序打印出爬取结果。

心得体会

  1. 在构造请求时,可以设置User-Agent头信息,绕过某些反爬虫机制。
  2. 在解析天气预报页面时,可以使用CSS选择器来定位元素,这种做法更为简洁、易读,并且不容易出错。同时由于某些标签可能缺失,故可以使用使用try...except语句可以避免程序因此崩溃。
  3. 对于爬取中文网站时乱码问题,我们可以使用UnicodeDammit库来巧妙避免这些问题。
  4. 可以将爬取的数据存储到SQLite数据库中,这是一个轻量级的关系型数据库,适合小规模的数据存储和管理。

作业②

爬取股票相关信息

要求:用 requests 和 BeautifulSoup 库方法定向爬取股票相关信息,并存储在数据库中。
– 网站:东方财富网:https://www.eastmoney.com/
– 技巧:在谷歌浏览器中进入 F12 调试模式进行抓包,查找股票列表加载使用的 url,并分析 api 返回的值,并根据所要求的参数可适当更改api 的请求参数。根据 URL 可观察请求的参数 f1、f2 可获取不同的数值,根据情况可删减请求的参数。

import requests
import re
import pandas as pd

headers={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.31"}

# 提取数据
def getHtml(page):
    url="http://52.push2.eastmoney.com/api/qt/clist/get?cb=jQuery112408730478018317309_1696661490260&pn="+str(page)+"&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"
    r = requests.get(url,headers=headers)
    pat0='"diff":\[\{(.*?)\}\]'
    data=re.compile(pat0,re.S).findall(r.text)
    return data

#爬取单页股票数据
def getOnePageStock(page):
    data=getHtml(page)
    datas=data[0].split('},{')
    stocks=[]
    for i in range(len(datas)):
        str1=r'"(\w)+":'
        stock=re.sub(str1," ",datas[i]) #将所有的文本都替换成空格
        stock =stock.split(",")
        stocks.append(stock)
    return stocks

page=1
stocks=getOnePageStock(page)
print("已经爬取了第1页")
while page<=5:
    page+=1
    if getHtml(page)!=getHtml(page-1):
        stocks.extend(getOnePageStock(page))
        print("已经爬取了第"+str(page)+"页")
    else:
        break
df=pd.DataFrame(stocks)#将 stocks 变量中的数据转换为 Pandas 数据框(DataFrame)格式

#提取主要数据
df.drop([0,7,8,9,10,12,18,19,20,21,22,23,24,25,26,27,28,29,30],axis=1,inplace=True)
df.index = range(1,len(df) + 1)
df=df[[11,13,1,2,3,4,5,6,14,15,16,17]]
columns = {11:"股票代码",13:"股票名称",1:"最新报价",2:"涨跌幅",3:"涨跌额",4:"成交量",5:"成交额",6:"振幅",14:"最高",15:"最低",16:"今开",17:"昨收"}
df=df.rename(columns=columns)
print(df)
df.to_excel("C:/Users/zwl/Desktop/作业2-2.xlsx")

运行结果:

心得体会

  1. 在发送HTTP请求时,可以设置User-Agent头信息,绕过某些反爬虫机制。
  2. 可以使用正则表达式来有效地提取数据。例如,在使用re.compile函数时,可以将匹配模式和标志(re.S)作为参数传递,并使用findall方法来获取所有匹配的结果。
  3. 在爬取单页股票数据时,可以将每个股票的信息存储在一个列表中,并将列表添加到一个包含所有股票信息的列表中。这样可以方便地处理和组织数据。

作业③

Gif录制F12调试分析过程

爬取中国大学2021主榜

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

import requests
import openpyxl
import re

if __name__=="__main__":

    headers={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.31"}

    url='https://www.shanghairanking.cn/_nuxt/static/1695811954/rankings/bcur/2021/payload.js'
    response=requests.get(url=url,headers=headers)
    response.encoding='utf-8'
    page_text=response.text
# print(page_text)
    data_regex = r'univData:(.*?),indList'
    data = re.findall(data_regex, page_text, re.S)[0]
    data = data.lstrip('[').rstrip(']')

    names = re.findall("univNameCn:(.*?),univNameEn:", data)
    areas = re.findall("province:(.*?),score", data)
    univCategory = re.findall("univCategory:(.*?),province", data)
    scores = re.findall("score:(.*?),ranking", data)


    dict = {'q':'北京','x':'浙江','C':'上海','k':'江苏','s':'陕西','v':'湖北','A':'黑龙江','u':'广东','n':'山东','M':'天津'
        ,'y':'安徽','t':'四川','D':'福建','B':'吉林','o':'河南','w':'湖南','r':':辽宁','E':'山西','N':'新疆','G':'广西'
        ,'J':'甘肃','p':'河北','I':'贵州','F':'云南','L':'重庆','K':'内蒙古','aD':'宁夏','z':'江西','Y':'海南','aF':'西藏'
        ,'aE':'青海'}
    sort ={'f':'综合','e':'理工','h':'师范','m':'农业','T':'林业'}

    tplt = "{0:^10}\t{1:^10}\t{2:^10}\t{3:{5}^10}\t{4:^10}"
    # {5}表示需要填充时使用format的第五个变量进行填充,即使用中文空格
    print(tplt.format("排名", "学校名称", "省份", "学校类型", "总分", chr(12288)))
    for i in range(30):
        print(tplt.format(str(i + 1), names[i].lstrip('"').rstrip('"'),dict[areas[i]].lstrip(':'),sort[univCategory[i]], scores[i], chr(12288)))

    wb = openpyxl.Workbook()
    ws = wb.active

    # 写入数据
    ws.cell(1, 1,value="排名")
    ws.cell(1, 2, value="学校名称")
    ws.cell(1, 3, value="省份")
    ws.cell(1, 4, value="学校类型")
    ws.cell(1, 5, value="总分")
    for i in range(1,30):
        ws.cell(row=i+1, column=1, value=i)
        ws.cell(row=i+1, column=2, value=names[i].lstrip('"').rstrip('"'))
        ws.cell(row=i+1, column=3, value=dict[areas[i]].lstrip(':'))
        ws.cell(row=i+1, column=4, value=sort[univCategory[i]])
        ws.cell(row=i+1, column=5, value=scores[i])
    # 保存文件
    wb.save('C:/Users/zwl/Desktop/作业2-3.xlsx')

运行结果:

心得体会

  1. 在发送请求时,可以设置User-Agent头信息,绕过某些反爬虫机制。
  2. 在使用正则表达式提取数据时,可以使用非贪婪模式,即在匹配规则后面加上?,以避免匹配过多的内容。
  3. 在输出结果时,可以使用了chr(12288)来生成中文空格,使得输出对齐更美观。