如何用python爬取新浪财经

通过python爬取新浪财经的股票历史成交明细

要求

通过新浪财经爬取历史数据:http://market.finance.sina.com.cn/transHis.php?symbol=sz000001&date=2021-04-27&page=60
要求:输入日期和股票代码后将一天所有的记录存入一个csv文件,并打印输出当日股票的最大值,最小值和平均值

首先我们可以观察到股票的名字就是symbol参数,日期就是date参数,而page有很多页。假若我们需要爬取一整日的信息,我们就需要找到page的首页和尾页。但是每支股票每天的首页和尾页可能数字都不同,所以这个地方是难点,我们需要用if去判断是否已经到尾页。

导入库

import requests
import time
from bs4 import BeautifulSoup
import csv
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36 FS"}
# 全局定义一个listone列表用于之后存放爬取的信息
listone = []

输入想要的信息

def main():
    #gupiao = input("请输入你想要查询的股票(例如sz000001):")
    #date = input("请输入你想要查询的日期(格式为2021-04-27):")

    # 这里的小代码我其实是为了测试以下尾页的停止,因为这个例子股票的日期在77页为尾页,80页说明flag为0
    # flag = gethtml('sz000001', '2021-04-27', 80)
    
    
    # 一般来讲股票不太可能超过100页。我们这里取一个上限值就好
    # 后续如果碰到尾页会之间break跳出循环不会到100页
    for i in range(1,100):
        # 早期为了不每次都手动输入可以直接先写定参数,后期再加入input
        # 返回如果是0则说明爬取错误或者到达尾页,要跳出循环
        flag = gethtml('sz000001','2021-04-27',i)
        # 最好需要间隔5s,否则太快爬取会被新浪封ip
        time.sleep(5)
        
        if flag==0:# 返回如果是0则说明爬取错误或者到达尾页,要跳出循环
            break
    #打印全局我们爬取数组保存在的listone数组里
    print(listone)
    #保存如csv
    save_csv(listone)
    # compute()
main()

获取请求页面html

def gethtml(gupiao,date,page):
    # 拼接字符串url
    url = 'https://market.finance.sina.com.cn/transHis.php?symbol='+gupiao+'&date='+date+'&page='+str(page)
    # 打印访问url,方便后期查看进度
    print(url)
    try:
        # request发送get请求
        r = requests.get(url=url,headers=headers)
        # None
        r.raise_for_status()
        # print("text的状态:",r.raise_for_status())
        r.encoding = r.apparent_encoding
        # 其实这里不管是不是尾页或是超过尾页text的状态都是none,都可以访问区别只是有没有数据而已
        
        # 所以这里我们无法根据request请求判断是否尾页,我们需要更进一层判断,即获取页面其中内容get_message
        flag = get_message(r.text,page)
        if flag == 0:
            #如果get_message返回给我们的为0则说明真的出现错误或者到达尾页flag为0,需要终止跳出循环了。
            return 0
    except Exception as result:
        print("错误原因:",result)
        return 0

分析r.text内容同时判断是否尾页

正常数据:

超过尾页tbody为空:

def get_message(text,page):
    # 煮一碗soup
    soup = BeautifulSoup(text, 'lxml')
    # 这里我们可以打印看到如果是有数据的tbody里面会有内容,那么soup.tbody.string会为none
    # 但是如果是已经尾页没有数据的tbody里面会没有内容,那么soup.tbody.string会为空字符串
    # 我们可以根据这个区别来判断是否已经超过尾页
    # print(soup.tbody.string)

    if soup.tbody.string!=None:
        # 如果不是None说明tbody为空,已经爬取到尾页为空了
        # 此时打印到头信息,然后return 0告诉上一层并跳出循环停止爬取
        print('到头为空')
        return 0
    else:
        # 我们可以观察他整个tbody的结构,发现他的数据包含在tbody的每一个tr内
        # 对tbody里所有的tr进行遍历
        for each in soup.tbody.find_all('tr'):
            # 打印查看each内容
            # print(each)
            # 获取each内的th和td标签
            th = each.select('th')
            td = each.select('td')

            # zerofloat = float(td[0].get_text())
            # onefloat = float(td[1].get_text())
            # twoint = float(td[2].get_text())
            treeint = float(td[3].get_text().replace(',',''))
            # 根据索引获取到th内的信息,并放入listtwo中
            # listtwo = [th[0].get_text(),zerofloat,onefloat,twoint,treeint,th[1].contents[0].get_text()]
            listtwo = [th[0].get_text(),td[0].get_text(),td[1].get_text(),td[2].get_text(),treeint,th[1].contents[0].get_text()]
            # print(listtwo)
            # 最后将listtwo添加入listone的末尾
            listone.append(listtwo)
            # print(listone)

< td >和< th >标签内的索引内容:

保存入csv文件

def save_csv(list):
    print("list",list)
    with open('sina.csv', 'w',newline='', encoding='utf-8-sig') as f:
        wr = csv.writer(f)
        # 标题头
        wr.writerow(['成交时间','成交价','价格变动','成交量(手)','成交额(元)','性质'])
        # 把listone里所有爬取的数据写入csv文件
        wr.writerows(listone)
    f2.close()

求取平均值最大值最小值

def compute():
    with open('sina.csv','r',encoding='utf-8-sig') as f2:
        r = csv.reader(f2)
        #跳过表头的文字
        head = next(r)
        sum = 0
        max = 0
        min=100
        count = 0
        for row in r:
            # print(row)
            thisnum = float(row[1])
            sum = sum + thisnum
            if max<thisnum:
                max = thisnum
            if min> thisnum :
                min = thisnum
            count= count+1
        avg = sum/count
        print("最大值为",max)
        print("最小值为",min)
        print("平均值为",avg)
  	f2.close()

pandas库

def pandas_conput(gupiao,date):
    if os.path.exists('sina'+gupiao+date+'.csv'):
        data_csv = pd.read_csv(f'sina'+gupiao+date+'.csv',encoding='utf-8-sig')
        print(data_csv)
        # 定义表格的行列名称
        index_df = ['成交价', '成交量(手)', '成交额(元)']
        columns_df = ['最大', '最小', '平均']
        # 填入表格数据
        data_df = []
        for index in index_df:
            l = []
            data = data_csv[index]
            l.append(data.max())
            l.append(data.min())
            l.append(data.mean())
            data_df.append(l)
        stock_df = pd.DataFrame(data_df, index=index_df, columns=columns_df)
        print(stock_df)

    else:
        print("文件不存在,请先爬取")

整体代码

import pandas as pd
import requests
import time
import os
from bs4 import BeautifulSoup
import csv
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36 FS"}
# 全局定义一个listone用于之后存放爬取的信息
listone = []

def get_message(text,page):
    # 煮一碗soup
    soup = BeautifulSoup(text, 'lxml')
    # 这里我们可以打印看到如果是有数据的tbody里面会有内容,那么soup.tbody.string会为none
    # 但是如果是已经尾页没有数据的tbody里面会没有内容,那么soup.tbody.string会为空字符串
    # 我们可以根据这个区别来判断是否已经超过尾页
    # print(soup.tbody.string)

    if soup.tbody.string!=None:
        # 如果不是None说明tbody为空,已经爬取到尾页为空了
        # 此时打印到头信息,然后return 0告诉上一层并跳出循环停止爬取
        print('到头为空')
        return 0
    else:
        # 我们可以观察他整个tbody的结构,发现他的数据包含在tbody的每一个tr内
        # 对tbody里所有的tr进行遍历
        for each in soup.tbody.find_all('tr'):
            # 打印查看each内容
            # print(each)
            # 获取each内的th和td标签
            th = each.select('th')
            td = each.select('td')
            
            #这里如果用compute函数计算平均值需要对各个数据(特别是成交额有逗号)进行处理
            # zerofloat = float(td[0].get_text())
            # onefloat = float(td[1].get_text())
            # twoint = float(td[2].get_text())
            
			# 因为原文有逗号,所以直接用float()强制转换会报错,所以我们需要先replace一下,如果用pandas计算其实可以不用强制转换为float,为字符串也行,但是compute必须替换为整型或浮点型进行计算
            treeint = float(td[3].get_text().replace(',',''))
            # 根据索引获取到th内的信息,并放入listtwo中
            # listtwo = [th[0].get_text(),zerofloat,onefloat,twoint,treeint,th[1].contents[0].get_text()]
            listtwo = [th[0].get_text(),td[0].get_text(),td[1].get_text(),td[2].get_text(),treeint,th[1].contents[0].get_text()]
            # print(listtwo)
            # 最后将listtwo添加入listone的末尾
            listone.append(listtwo)
            # print(listone)

# 返回0则说明不存在页面  sz000001
def gethtml(gupiao,date,page):
    # 拼接字符串url
    url = 'https://market.finance.sina.com.cn/transHis.php?symbol='+gupiao+'&date='+date+'&page='+str(page)
    # 打印访问url,方便后期查看进度
    print(url)
    try:
        r = requests.get(url=url,headers=headers)
        # None
        r.raise_for_status()
        # print("text的状态:",r.raise_for_status())
        r.encoding = r.apparent_encoding
        # 其实这里不管是不是尾页或是超过尾页text的状态都是none,都可以访问区别只是有没有数据而已
        # 所以这里我们无法根据request请求判断是否尾页,我们需要更进一层判断,即获取页面其中内容get_message

        flag = get_message(r.text,page)
        if flag == 0:
            #如果get_message返回给我们的为0则说明真的出现错误或者到达尾页flag为0,需要终止跳出循环了。
            return 0
    except Exception as result:
        print("错误原因:",result)
        return 0


def save_csv(list,gupiao,date):
    print("list",list)
    list1 = list
    with open('sina'+gupiao+date+'.csv', 'w',newline='', encoding='utf-8-sig') as f:
        wr = csv.writer(f)
        wr.writerow(['成交时间','成交价','价格变动','成交量(手)','成交额(元)','性质'])
        wr.writerows(listone)
    f.close()

    
def compute(gupiao,date):
    if os.path.exists('sina'+gupiao+date+'.csv'):
        with open('sina' + gupiao + date + '.csv', 'r', encoding='utf-8-sig') as f2:
            r = csv.reader(f2)
            head = next(r)
            sum = 0
            max = 0
            min = 100
            count = 0
            for row in r:
                # print(row)
                # 只单单计算了成交价
                thisnum = float(row[1])
                sum = sum + thisnum
                if max < thisnum:
                    max = thisnum
                if min > thisnum:
                    min = thisnum
                count = count + 1
            avg = sum / count
            print("最大值为", max)
            print("最小值为", min)
            print("平均值为", avg)
        f2.close()
    else:
        print("文件不存在,请先爬取")


def pandas_conput(gupiao,date):
    if os.path.exists('sina'+gupiao+date+'.csv'):
        data_csv = pd.read_csv(f'sina'+gupiao+date+'.csv',encoding='utf-8-sig')
        print(data_csv)
        # 定义表格的行列名称
        index_df = ['成交价', '成交量(手)', '成交额(元)']
        columns_df = ['最大', '最小', '平均']
        # 填入表格数据
        data_df = []
        for index in index_df:
            l = []
            data = data_csv[index]
            l.append(data.max())
            l.append(data.min())
            l.append(data.mean())
            data_df.append(l)
        stock_df = pd.DataFrame(data_df, index=index_df, columns=columns_df)
        print(stock_df)

    else:
        print("文件不存在,请先爬取")


def main():
    gupiao = input("请输入你想要查询的股票(例如sz000001):")
    date = input("请输入你想要查询的日期(格式为2021-04-27):")
    # 早期为了不每次都手动输入可以直接先写定参数,后期再加入input
    # 这里我其实是为了测试以下尾页的停止
    # flag = gethtml('sz000001', '2021-04-27', 80)
    # 一般来讲股票不太可能超过100页。我们这里取一个上限值就好
    # 后续如果碰到尾页会之间break跳出循环不会到100页
    for i in range(1,100):
        # 返回如果是0则说明爬取错误或者到达尾页,要跳出循环
        flag = gethtml(gupiao,date,i)
        # flag = gethtml('sz000001', '2021-04-27', i)
        # 最好需要间隔5s,否则太快爬取会被新浪封ip
        time.sleep(5)
        if flag==0:
            break
    print(listone)
    save_csv(listone,gupiao, date)
    # compute(gupiao,date)
    pandas_conput(gupiao, date)


main()

利用qyside和qyptgraph进行绘图

这一块的内容还不是很熟悉,代码也不够完善(有一些bug),仅供参考

import pandas as pd
import requests
import time
import os
from bs4 import BeautifulSoup
import csv
import sys
from random import randint
from PySide2 import QtWidgets
import numpy as np
import PySide2
from PySide2.QtWidgets import QApplication
from PySide2.QtWidgets import QTableWidgetItem
from PySide2.QtWidgets import QTableWidget
from PySide2.QtUiTools import QUiLoader
import pyqtgraph as pg
from pyqtgraph.Qt import QtGui, QtCore
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36 FS"}
# 全局定义一个listone用于之后存放爬取的信息
listone = []


class Stock:
    def __init__(self):

        loader = QUiLoader()

        # pyside2 一定要 使用registerCustomWidget
        # 来 注册 ui文件中的第三方控件,
        loader.registerCustomWidget(pg.PlotWidget)
        self.ui = loader.load("./pyside_ku/gupiao.ui")

        self.ui.find_but.clicked.connect(self.findonedate)
        self.ui.clear_but.clicked.connect(self.cleardata)


    def findonedate(self):
        # 清除画布
        self.ui.historyPlot.clear()
        # 获取股票代码,指标和日期
        gupiaodaima = self.ui.textedit.toPlainText()
        zhibiao = self.ui.comboBox.currentText()
        date = self.ui.date.date().toString('yyyy-MM-dd')
        # print(gupiaodaima,zhibiao,date)
        # 如果存在这个文件说明已经爬取过
        if os.path.exists('sina' + gupiaodaima + date + '.csv'):
            # 获取csv
            data_csv = pd.read_csv(f'sina' + gupiaodaima + date + '.csv', encoding='utf-8-sig')
            # print(data_csv)
            # 定义表格的行列名称
            index_df = ['成交价', '成交量(手)', '成交额(元)','成交时间']
            # 填入表格数据
            data_price = [] #成交价
            data_num = [] #成交量
            data_money=[] #成交额
            data_time = [] #成交时间

            data_price = data_csv[index_df[0]]
            data_num = data_csv[index_df[1]]
            data_money = data_csv[index_df[2]]
            data_time = data_csv[index_df[3]]
            length = len(data_time)

            data_time = data_time.tolist()
            data_num = data_num.tolist()
            data_money = data_money.tolist()
            data_price = data_price.tolist()

            x = np.arange(length)
            xTick = []
            for i in zip(x,data_time):
                xTick.append(i)
            xTick = [xTick]

            self.ui.tableWidget.setRowCount(length)
            self.ui.tableWidget.setColumnCount(4)

            for row in range(length):
                item = QTableWidgetItem()
                item.setText(str(data_price[row]))
                self.ui.tableWidget.setItem(row, 0, item)
            for row in range(length):
                item = QTableWidgetItem()
                item.setText(str(data_num[row]))
                self.ui.tableWidget.setItem(row, 1, item)
            for row in range(length):
                item = QTableWidgetItem()
                item.setText(str(data_money[row]))
                self.ui.tableWidget.setItem(row, 2, item)
            for row in range(length):
                item = QTableWidgetItem()
                item.setText(str(data_time[row]))
                self.ui.tableWidget.setItem(row, 3, item)
            stock_table = pandas_conput(gupiaodaima,date)
            # print(stock_table['最小']['成交价'])
            if zhibiao == index_df[0]:
                self.ui.historyPlot.plot(x, data_price)
                self.ui.maxtextedit.setPlainText(str(stock_table['最大']['成交价']))
                self.ui.mintextedit.setPlainText(str(stock_table['最小']['成交价']))
                self.ui.avgtextedit.setPlainText(str(stock_table['平均']['成交价']))
            elif zhibiao == index_df[1]:
                self.ui.historyPlot.plot(x, data_num)
                self.ui.maxtextedit.setPlainText(str(stock_table['最大']['成交量(手)']))
                self.ui.mintextedit.setPlainText(str(stock_table['最小']['成交量(手)']))
                self.ui.avgtextedit.setPlainText(str(stock_table['平均']['成交量(手)']))
            elif zhibiao == index_df[2]:
                self.ui.historyPlot.plot(x, data_money)
                self.ui.maxtextedit.setPlainText(str(stock_table['最大']['成交额(元)']))
                self.ui.mintextedit.setPlainText(str(stock_table['最小']['成交额(元)']))
                self.ui.avgtextedit.setPlainText(str(stock_table['平均']['成交额(元)']))
            else:
                print('错误!')
            xax = self.ui.historyPlot.getAxis('bottom')
            xax.setTicks(xTick)


        else:
            choise2 = input("文件不存在,您要爬取么?(0:不需要;1:需要):")
            if (choise2 == '0'):
                pass
            elif (choise2 == '1'):
                paqu(gupiaodaima,date)

    def cleardata(self):
        self.ui.historyPlot.clear()
        self.ui.textedit.setPlainText('sz000001')
        myPythonicDate = '2021-04-27'
        self.ui.date.setDate(QtCore.QDate.fromString(myPythonicDate, 'yyyy-MM-dd'))



def get_message(text,page):
    # 煮一碗soup
    soup = BeautifulSoup(text, 'lxml')
    # 这里我们可以打印看到如果是有数据的tbody里面会有内容,那么soup.tbody.string会为none
    # 但是如果是已经尾页没有数据的tbody里面会没有内容,那么soup.tbody.string会为空字符串
    # 我们可以根据这个区别来判断是否已经超过尾页
    # print(soup.tbody.string)

    if soup.tbody.string!=None:
        # 如果不是None说明tbody为空,已经爬取到尾页为空了
        # 此时打印到头信息,然后return 0告诉上一层并跳出循环停止爬取
        print('到头为空')
        return 0
    else:
        # 我们可以观察他整个tbody的结构,发现他的数据包含在tbody的每一个tr内
        # 对tbody里所有的tr进行遍历
        for each in soup.tbody.find_all('tr'):
            # 打印查看each内容
            # print(each)
            # 获取each内的th和td标签
            th = each.select('th')
            td = each.select('td')
            treeint = float(td[3].get_text().replace(',',''))
            # 根据索引获取到th内的信息,并放入listtwo中
            # listtwo = [th[0].get_text(),zerofloat,onefloat,twoint,treeint,th[1].contents[0].get_text()]
            listtwo = [th[0].get_text(),td[0].get_text(),td[1].get_text(),td[2].get_text(),treeint,th[1].contents[0].get_text()]
            # print(listtwo)
            # 最后将listtwo添加入listone的末尾
            listone.append(listtwo)
            # print(listone)
# 返回0则说明不存在页面  sz000001
def gethtml(gupiao,date,page):
    # 拼接字符串url
    url = 'https://market.finance.sina.com.cn/transHis.php?symbol='+gupiao+'&date='+date+'&page='+str(page)
    # 打印访问url,方便后期查看进度
    print(url)
    try:
        r = requests.get(url=url,headers=headers)
        # None
        r.raise_for_status()
        # print("text的状态:",r.raise_for_status())
        r.encoding = r.apparent_encoding
        # 其实这里不管是不是尾页或是超过尾页text的状态都是none,都可以访问区别只是有没有数据而已
        # 所以这里我们无法根据request请求判断是否尾页,我们需要更进一层判断,即获取页面其中内容get_message

        flag = get_message(r.text,page)
        if flag == 0:
            #如果get_message返回给我们的为0则说明真的出现错误或者到达尾页flag为0,需要终止跳出循环了。
            return 0
    except Exception as result:
        print("错误原因:",result)
        return 0
def save_csv(list,gupiao,date):
    print("list",list)
    list1 = list
    with open('sina'+gupiao+date+'.csv', 'w',newline='', encoding='utf-8-sig') as f:
        wr = csv.writer(f)
        wr.writerow(['成交时间','成交价','价格变动','成交量(手)','成交额(元)','性质'])
        wr.writerows(listone)
    f.close()
def compute(gupiao,date):
    # 这里只计算了成交价的最大值最小值平均值
    if os.path.exists('sina'+gupiao+date+'.csv'):
        with open('sina' + gupiao + date + '.csv', 'r', encoding='utf-8-sig') as f2:
            r = csv.reader(f2)
            head = next(r)
            sum = 0
            max = 0
            min = 100
            count = 0
            for row in r:
                # print(row)
                thisnum = float(row[1])
                sum = sum + thisnum
                if max < thisnum:
                    max = thisnum
                if min > thisnum:
                    min = thisnum
                count = count + 1
            avg = sum / count
            print("最大值为", max)
            print("最小值为", min)
            print("平均值为", avg)
        f2.close()
    else:
        print("文件不存在,请先爬取")
def pandas_conput(gupiao,date):
    if os.path.exists('sina'+gupiao+date+'.csv'):
        data_csv = pd.read_csv(f'sina'+gupiao+date+'.csv',encoding='utf-8-sig')
        print(data_csv)
        # 定义表格的行列名称
        index_df = ['成交价', '成交量(手)', '成交额(元)']
        columns_df = ['最大', '最小', '平均']
        # 填入表格数据
        data_df = []
        for index in index_df:
            l = []
            data = data_csv[index]
            l.append(data.max())
            l.append(data.min())
            l.append(data.mean())
            data_df.append(l)
        stock_df = pd.DataFrame(data_df, index=index_df, columns=columns_df)
        return stock_df

    else:
        print("文件不存在")


# 保存股票信息入本地
def paqu(gupiao,date):

    for i in range(1,100):
        flag = gethtml(gupiao,date,i)
        # 最好需要间隔5s,否则太快爬取会被新浪封ip
        time.sleep(5)
        if flag==0:
            break
    save_csv(listone,gupiao, date)
    pandas_conput(gupiao, date)

# 查询股票行情图
def chaxun():
    app = QApplication([])
    stock = Stock()
    stock.ui.show()
    app.exec_()

def main():
    while(1):
        choise = input('您需要什么服务?(0:保存股票信息入本地;1:查询股票行情图; 2:退出服务):')
        if choise=='0':
            gupiao = input("请输入你想要查询的股票(例如sz000001):")
            date = input("请输入你想要查询的日期(格式为2021-04-27):")
            paqu(gupiao,date)
        elif choise=='1':
            chaxun()
        elif choise=='2':
            break
        else:
            print("错误输入请重新输入")

main()

效果展示

成交价

成交量

image-20211019102801288

  • 14
    点赞
  • 84
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值