Python 爬虫实战:股票数据定向爬虫

本文作者:hang 本文来源:https://segmentfault.com/a/1190000010520835

功能简介

  • 目标: 获取上交所和深交所所有股票的名称和交易信息。
  • 输出: 保存到文件中。
  • 技术路线: requests—bs4–re
  • 语言:python3.5

说明

  • 网站选择原则: 股票信息静态存在于html页面中,非js代码生成,没有Robbts协议限制。
  • 选取方法: 打开网页,查看源代码,搜索网页的股票价格数据是否存在于源代码中。

如打开新浪股票网址:链接描述(http://finance.sina.com.cn/realstock/company/sz000877/nc.shtml),如下图所示:

上图中左边为网页的界面,显示了天山股份的股票价格是13.06。右边为该网页的源代码,在源代码中查询13.06发现没有找到。所以判断该网页的数据使用js生成的,不适合本项目。因此换一个网页。

再打开百度股票的网址:链接描述(https://gupiao.baidu.com/stock/sz300023.html),如下图所示:

从上图中可以发现百度股票的数据是html代码生成的,符合我们本项目的要求,所以在本项目中选择百度股票的网址。

由于百度股票只有单个股票的信息,所以还需要当前股票市场中所有股票的列表,在这里我们选择东方财富网,网址为:链接描述(http://quote.eastmoney.com/stocklist.html),界面如下图所示:

原理分析

查看百度股票每只股票的网址:https://gupiao.baidu.com/stock/sz300023.html,可以发现网址中有一个编号300023正好是这只股票的编号,sz表示的深圳交易所。因此我们构造的程序结构如下:

  • 步骤1: 从东方财富网获取股票列表;
  • 步骤2: 逐一获取股票代码,并增加到百度股票的链接中,最后对这些链接进行逐个的访问获得股票的信息;
  • 步骤3: 将结果存储到文件。

接着查看百度个股信息网页的源代码,发现每只股票的信息在html代码中的存储方式如下:

因此,在我们存储每只股票的信息时,可以参考上图中html代码的存储方式。每一个信息源对应一个信息值,即采用键值对的方式进行存储。在python中键值对的方式可以用字典类型。因此,在本项目中,使用字典来存储每只股票的信息,然后再用字典把所有股票的信息记录起来,最后将字典中的数据输出到文件中。

代码编写

首先是获得html网页数据的程序,在这里不多做介绍了,代码如下:

#获得html文本 def getHTMLText(url): try: r = requests.get(url) r.raise_for_status() r.encoding = r.apparent_encoding return r.text except: return ""

接下来是html代码解析程序,在这里首先需要解析的是东方财富网页面:链接描述(http://quote.eastmoney.com/stocklist.html),我们打开其源代码,如下图所示:

由上图可以看到,a标签的href属性中的网址链接里面有每只股票的对应的号码,因此我们只要把网址里面对应股票的号码解析出来即可。解析步骤如下:

第一步,获得一个页面:

html = getHTMLText(stockURL)

第二步,解析页面,找到所有的a标签:

soup = BeautifulSoup(html, 'html.parser') a = soup.find_all('a')

第三步,对a标签中的每一个进行遍历来进行相关的处理。处理过程如下:

1.找到a标签中的href属性,并且判断属性中间的链接,把链接后面的数字取出来,在这里可以使用正则表达式来进行匹配。由于深圳交易所的代码以sz开头,上海交易所的代码以sh开头,股票的数字有6位构成,所以正则表达式可以写为[s][hz]\d{6}。也就是说构造一个正则表达式,在链接中去寻找满足这个正则表达式的字符串,并把它提取出来。代码如下:

for i in a: href = i.attrs['href'] lst.append(re.findall(r"[s][hz]\d{6}", href)[0])

2.由于在html中有很多的a标签,但是有些a标签中没有href属性,因此上述程序在运行的时候出现异常,所有对上述的程序还要进行try…except来对程序进行异常处理,代码如下:

for i in a: try: href = i.attrs['href'] lst.append(re.findall(r"[s][hz]\d{6}", href)[0]) except: continue

从上面代码可以看出,对于出现异常的情况我们使用了continue语句,直接让其跳过,继续执行下面的语句。通过上面的程序我们就可以把东方财富网上股票的代码信息全部保存下来了。

将上述的代码封装成一个函数,对东方财富网页面解析的完整代码如下所示:

def getStockList(lst, stockURL): html = getHTMLText(stockURL) soup = BeautifulSoup(html, 'html.parser') a = soup.find_all('a') for i in a: try: href = i.attrs['href'] lst.append(re.findall(r"[s][hz]\d{6}", href)[0]) except: continue

接下来是获得百度股票网链接描述(https://gupiao.baidu.com/stock/sz300023.html)单只股票的信息。我们先查看该页面的源代码,如下图所示:

股票的信息就存在上图所示的html代码中,因此我们需要对这段html代码进行解析。过程如下:

1.百度股票网的网址为:https://gupiao.baidu.com/stock/

一只股票信息的网址为:https://gupiao.baidu.com/stock/sz300023.html

所以只要百度股票网的网址+每只股票的代码即可,而每只股票的代码我们已经有前面的程序getStockList从东方财富网解析出来了,因此对getStockList函数返回的列表进行遍历即可,代码如下:

for stock in lst: url = stockURL + stock + ".html"

2.获得网址后,就要访问网页获得网页的html代码了,程序如下:

html = getHTMLText(url)

3.获得了html代码后就需要对html代码进行解析,由上图我们可以看到单个股票的信息存放在标签为div,属性为stock-bets的html代码中,因此对其进行解析:

soup = BeautifulSoup(html, 'html.parser') stockInfo = soup.find('div',attrs={'class':'stock-bets'})

4.我们又发现股票名称在bets-name标签内,继续解析,存入字典中:

infoDict = {} name = stockInfo.find_all(attrs={'class':'bets-name'})[0] infoDict.update({'股票名称': name.text.split()[0]})

split()的意思是股票名称空格后面的部分不需要了。

5.我们从html代码中还可以观察到股票的其他信息存放在dt和dd标签中,其中dt表示股票信息的键域,dd标签是值域。获取全部的键和值:

keyList = stockInfo.find_all('dt') valueList = stockInfo.find_all('dd')

并把获得的键和值按键值对的方式村放入字典中:

for i in range(len(keyList)): key = keyList[i].text val = valueList[i].text infoDict[key] = val

6.最后把字典中的数据存入外部文件中:

with open(fpath, 'a', encoding='utf-8') as f: f.write( str(infoDict) + '\n' )

将上述过程封装成完成的函数,代码如下:

def getStockInfo(lst, stockURL, fpath): for stock in lst: url = stockURL + stock + ".html" html = getHTMLText(url) try: if html=="": continue infoDict = {} soup = BeautifulSoup(html, 'html.parser') stockInfo = soup.find('div',attrs={'class':'stock-bets'}) name = stockInfo.find_all(attrs={'class':'bets-name'})[0] infoDict.update({'股票名称': name.text.split()[0]}) keyList = stockInfo.find_all('dt') valueList = stockInfo.find_all('dd') for i in range(len(keyList)): key = keyList[i].text val = valueList[i].text infoDict[key] = val with open(fpath, 'a', encoding='utf-8') as f: f.write( str(infoDict) + '\n' ) except: continue

其中try…except用于异常处理。

接下来编写主函数,调用上述函数即可:

def main(): stock_list_url = 'http://quote.eastmoney.com/stocklist.html' stock_info_url = 'https://gupiao.baidu.com/stock/' output_file = 'D:/BaiduStockInfo.txt' slist=[] getStockList(slist, stock_list_url) getStockInfo(slist, stock_info_url, output_file)

项目完整程序

# -*- coding: utf-8 -*- import requests from bs4 import BeautifulSoup import traceback import re def getHTMLText(url): try: r = requests.get(url) r.raise_for_status() r.encoding = r.apparent_encoding return r.text except: return "" def getStockList(lst, stockURL): html = getHTMLText(stockURL) soup = BeautifulSoup(html, 'html.parser') a = soup.find_all('a') for i in a: try: href = i.attrs['href'] lst.append(re.findall(r"[s][hz]\d{6}", href)[0]) except: continue def getStockInfo(lst, stockURL, fpath): count = 0 for stock in lst: url = stockURL + stock + ".html" html = getHTMLText(url) try: if html=="": continue infoDict = {} soup = BeautifulSoup(html, 'html.parser') stockInfo = soup.find('div',attrs={'class':'stock-bets'}) name = stockInfo.find_all(attrs={'class':'bets-name'})[0] infoDict.update({'股票名称': name.text.split()[0]}) keyList = stockInfo.find_all('dt') valueList = stockInfo.find_all('dd') for i in range(len(keyList)): key = keyList[i].text val = valueList[i].text infoDict[key] = val with open(fpath, 'a', encoding='utf-8') as f: f.write( str(infoDict) + '\n' ) count = count + 1 print("\r当前进度: {:.2f}%".format(count*100/len(lst)),end="") except: count = count + 1 print("\r当前进度: {:.2f}%".format(count*100/len(lst)),end="") continue def main(): stock_list_url = 'http://quote.eastmoney.com/stocklist.html' stock_info_url = 'https://gupiao.baidu.com/stock/' output_file = 'D:/BaiduStockInfo.txt' slist=[] getStockList(slist, stock_list_url) getStockInfo(slist, stock_info_url, output_file) main()

上述代码中的print语句用于打印爬取的进度。执行完上述代码后在D盘会出现BaiduStockInfo.txt文件,里面存放了股票的信息。

原文发布于微信公众号 - 大数据杂谈(BigData07)

原文发表时间:2018-03-29

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏我和未来有约会

Silverlight第三方控件专题

这里我收集整理了目前网上silverlight第三方控件的专题,若果有所遗漏请告知我一下。 名称 简介 截图 telerik 商 RadC...

4025
来自专栏一个爱瞎折腾的程序猿

sqlserver使用存储过程跟踪SQL

USE [master] GO /****** Object: StoredProcedure [dbo].[sp_perfworkload_trace_s...

2060
来自专栏陈仁松博客

ASP.NET Core 'Microsoft.Win32.Registry' 错误修复

今天在发布Asp.net Core应用到Azure的时候出现错误InvalidOperationException: Cannot find compilati...

4848
来自专栏张善友的专栏

Miguel de Icaza 细说 Mix 07大会上的Silverlight和DLR

Mono之父Miguel de Icaza 详细报道微软Mix 07大会上的Silverlight和DLR ,上面还谈到了Mono and Silverligh...

2707
来自专栏我和未来有约会

Kit 3D 更新

Kit3D is a 3D graphics engine written for Microsoft Silverlight. Kit3D was inita...

2536
来自专栏Ceph对象存储方案

Luminous版本PG 分布调优

Luminous版本开始新增的balancer模块在PG分布优化方面效果非常明显,操作也非常简便,强烈推荐各位在集群上线之前进行这一操作,能够极大的提升整个集群...

3135
来自专栏Golang语言社区

【Golang语言社区】GO1.9 map并发安全测试

var m sync.Map //全局 func maintest() { // 第一个 YongHuomap := make(map[st...

4708
来自专栏转载gongluck的CSDN博客

cocos2dx 打灰机

#include "GamePlane.h" #include "PlaneSprite.h" #include "BulletNode.h" #include...

5456
来自专栏一个会写诗的程序员的博客

Spring Reactor 项目核心库Reactor Core

Non-Blocking Reactive Streams Foundation for the JVM both implementing a Reactiv...

2152
来自专栏pangguoming

Spring Boot集成JasperReports生成PDF文档

由于工作需要,要实现后端根据模板动态填充数据生成PDF文档,通过技术选型,使用Ireport5.6来设计模板,结合JasperReports5.6工具库来调用渲...

1.2K7

扫码关注云+社区