数据采集与融合第二次作业
作业一:
思路 |
(1)通过城市代码切换url,获取html
(2)7天的天气预报元素中,每天是一个li元素,7天的li结构是一样的,可以通过BeautifulSoup的元素查找方法来获取各个元素的值
(3)保存到数据库中
代码 |
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()
i=1
print("{:4}\t{:10}\t{:14}\t{:24}\t{:16}".format("序号","城市", "日期", "天气信息", "温度"))
for row in rows:
print("{:4}\t{:10}\t{:10}\t{:24}\t{:16}".format(i,row[0], row[1], row[2], row[3]))
i+=1
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") #找到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):
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")
运行结果 |
心得体会 |
(1)了解了sqlite3的数据库存储
(2)加强了CSS语法的运用
作业二:
思路 |
(1)过滤js文件,提取url
(2)对沪深A股、上证A股等不同板块的url切换
通过对比发现,只有两处不同,所以建立了两个字典用来切换
(3)翻页,以及自动停止
①爬取到最后一页:
文本中的total字段为总数据,东方财富网一页为20条数据,可以通过(总数据/20)向上取整来获得总页数,从而自动爬取到最后一页。
②翻页:通过对比,翻页是通过修改pn值来实现的。
(4)正则表达式匹配,对得到的text的处理
不同股票之间可以用 ' },{ '来进行切分,不同信息之间再用逗号切分,之后再通过冒号分隔来得到信息值。
(5)通过dataframe来结构化数据,存为xls文件
这一步包括
-
提取所要的数据
-
重命名列
-
切换列的顺序
-
新建路径
-
保存文件等。
代码 |
import requests
import re
import pandas as pd
import math
import os
#用get方法访问服务器并提取页面数据
def getHtml(fs,page,k):
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3141.8 Safari/537.36}"
}
#页数是通过修改pn值,不同股票的url有两个地方不同,通过之后的字典fs和k来实现。
url = "http://35.push2.eastmoney.com/api/qt/clist/get?cb=jQuery112402976183355999211_1601533720950&pn="+str(page)+"&pz=20&po=1&np=1&ut=bd1d9ddb04089700cf9c27f6f7426281&fltt=2&invt=2&fid=f3&fs="+fs+"&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&_="+k
r = requests.get(url,headers=headers)
pat = "\"data\":.*\]"
data = re.compile(pat, re.S).findall(r.text) #正则表达式匹配
#获取数据总条数,用于主函数中计算总页数,每页有20条数据,相除向上取整为总页数。
t=re.findall(r"\"total\":[\d]*",data[0])
to=t[0].split(':')
total=eval(to[1])
# 去除前面的total和diff数据
data[0]=data[0][29:]
return data,total
#获取单个页面股票数据
def getOnePageStock(fs,page,k):
data = getHtml(fs,page,k)
data=data[0]
datas = data[0].split('},{') #不同股票分割
stocks = []
for i in range(len(datas)):
stock = datas[i].replace('"',"").split(',') #不同信息分割,并且去掉引号
for j in range(len(stock)):
#冒号分割,获取信息的值
t=stock[j].split(':')
stock[j]=t[1]
stocks.append(stock)
return stocks
def main():
#根据对不同url的分析,建立以下两个字典
fs = {
"沪深A股":"m:0+t:6,m:0+t:13,m:0+t:80,m:1+t:2,m:1+t:23",
"上证A股":"m:1+t:2,m:1+t:23",
"深证A股":"m:0+t:6,m:0+t:13,m:0+t:80",
"新股":"m:0+f:8,m:1+f:8",
"中小板":"m:0+t:13",
"创业板":"m:0+t:80"
}
k = {
"沪深A股": "1601536578738",
"上证A股": "1601536578736",
"深证A股": "1601536578759",
"新股": "1601536578765",
"中小板": "1601536578882",
"创业板": "1601536578888"
}
dirs="东方财富网股票数据031804112/"
#如果不存在,新建路径
if not os.path.exists(dirs):
os.makedirs(dirs)
for i in fs.keys():
page = 1
stocks = getOnePageStock(fs[i],page,k[i])
#计算总页数
total=getHtml(fs[i], 1, k[i])
totalpage=math.ceil(total[1]/20.0)
#自动爬取多页,直到结束
while True:
page += 1
if page<=totalpage:
stocks.extend(getOnePageStock(fs[i], page,k[i]))
#print(i+"已加载第"+str(page)+"页")
else:
break
df = pd.DataFrame(stocks)
#提取所要的数据
df=df.drop([0,7,8,9,10,12,18,19,20,21,22,23,24,25,26,27,28,29,30],axis=1) #去除一些数据,留下需要的列,axis=1是列,=0是行
#调整一下顺序
order = [11,13,1,2,3,4,5,6,14,15,16,17]
df = df[order]
columns = {11:"代码",13:"名称",1:"最新价",2:"涨跌幅(%)",3:"涨跌额",4:"成交量",5:"成交额",6:"振幅(%)",14:"最高",15:"最低",16:"今开",17:"昨收"}
#重新给列命名
df.rename(columns=columns, inplace=True)
#保存为xls文件
df.index += 1 #序号从0开头改为从1开头
df.to_excel("东方财富网股票数据031804112/"+i+".xls",index_label='序号')
print("已保存"+i+".xls")
main()
运行结果 |
xls文件不全部截图了
沪深A股
中小板
心得体会 |
(1)爬取js加载的动态数据,通过抓包获取数据集url,通过对json解析对应数据集。
(2)加深了对dataframe的了解,运用了它的一些方法
(3)正则表达式匹配,对数据处理更熟练了
作业三:
思路 |
在作业二的基础上进行一些修改和删减就好了
-
修改的部分:
设置一个find变量,如果f12的数据等于自选的代码号,则find=1并跳出本页的循环,并且不用翻到下一页。 -
删减的部分:
删掉了结构化,保存为xls文件,直接输出在哪里找到的,哪一页,以及股票数据信息。 -
获取的是代码为'603112'的股票,如果要换别的股票,可以通过修改主函数中的number就可以了
代码 |
import requests
import re
import math
#用get方法访问服务器并提取页面数据
def getHtml(fs,page,k):
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3141.8 Safari/537.36}"
}
#页数是通过修改pn值,不同股票的url有两个地方不同,通过之后的字典来实现。
url = "http://35.push2.eastmoney.com/api/qt/clist/get?cb=jQuery112402976183355999211_1601533720950&pn="+str(page)+"&pz=20&po=1&np=1&ut=bd1d9ddb04089700cf9c27f6f7426281&fltt=2&invt=2&fid=f3&fs="+fs+"&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&_="+k
r = requests.get(url,headers=headers)
pat = "\"data\":.*\]"
data = re.compile(pat, re.S).findall(r.text)
# 获取数据总条数,用于主函数中计算总页数,每页有20条数据,相除向上取整为总页数。
t=re.findall(r"\"total\":[\d]*",data[0])
to=t[0].split(':')
total=eval(to[1])
# 去除前面的total和diff数据
data[0]=data[0][29:]
return data,total
#获取单个页面股票数据
def getOnePageStock(fs,page,k,number,i):
data = getHtml(fs,page,k)
data=data[0]
datas = data[0].split('},{') #不同股票分割
stocks = []
find=0 #设置find来判断是否找到了
for t in range(len(datas)):
stock = datas[t].replace('"',"").split(',')#不同信息分割,并且去掉引号
for j in range(len(stock)):
# 冒号分割,获取信息的值
t=stock[j].split(':')
stock[j]=t[1]
if stock[11] == number:
stocks.append(stock)
find=1 #找到,就可以输出,停止循环了
print(i)
print("在第"+str(page)+"页:")
print("{:4}\t{:4}\t{:4}\t{:4}\t{:4}\t{:4}\t{:4}\t{:4}".format("代码", "名称", "今开", "最高", "最低","涨跌幅","换手率","成交量"))
print("{:4}\t{:4}\t{:4}\t{:4}\t{:4}\t{:4}\t{:4}\t{:4}".format(stock[11],stock[13],stock[16],stock[14],stock[15],stock[2],stock[7],stock[4]))
break
return find
def main():
fs = {
"沪深A股":"m:0+t:6,m:0+t:13,m:0+t:80,m:1+t:2,m:1+t:23",
"上证A股":"m:1+t:2,m:1+t:23",
"深证A股":"m:0+t:6,m:0+t:13,m:0+t:80",
"新股":"m:0+f:8,m:1+f:8",
"中小板":"m:0+t:13",
"创业板":"m:0+t:80"
}
k = {
"沪深A股": "1601536578738",
"上证A股": "1601536578736",
"深证A股": "1601536578759",
"新股": "1601536578765",
"中小板": "1601536578882",
"创业板": "1601536578888"
}
#自选三位加学号后三位
number='603112'
for i in fs.keys():
page = 1
iffind = getOnePageStock(fs[i],page,k[i],number,i)
#计算总页数
total=getHtml(fs[i], 1, k[i])
totalpage=math.ceil(total[1]/20.0)
#自动爬取多页
while True:
page += 1
if page <= totalpage and iffind !=1: #如果小于总页数,并且没找到,就继续找
iffind=getOnePageStock(fs[i], page,k[i],number,i)
#print(i+"已加载第"+str(page)+"页")
else:
break
main()
运行结果 |
心得体会 |
(1)与作业2相同的一些方法的运用
(2)缺点就是不能动态抓取,比如代码为603112的股票在新股中的位置是不断变化的,输出的所在页数已经是过去的了。