网络爬虫:Gupiao 数据定向爬虫

Gupiao 数据定向爬虫

Copyright: Jingmin Wei, Pattern Recognition and Intelligent System, School of Artificial and Intelligence, Huazhong University of Science and Technology

网络爬虫专栏链接



本教程主要参考中国大学慕课的 Python 网络爬虫与信息提取,为个人学习笔记。

在学习过程中遇到了一些问题,都手动记录并且修改更正,保证所有的代码为有效。且结合其他的博客总结了一些常见问题的解决方式。

本教程不商用,仅为学习参考使用。如需转载,请联系本人。

Reference

爬虫 MOOC

数据分析 MOOC

廖雪峰老师的 Python 教程

由于 Baidu Stock 的网站已经失效,所以这里选择雪球网进行 Stock 的爬虫操作,基本思路还是按照 Baidu 来写。

爬取雪球 Stock 的爬虫实践

基本程序结构设计
  1. 该网站获取 Stock 列表信息。

  2. 根据 Stock 列表信息逐个到 雪球网 获取个股信息。

  3. 将结果存储到文件。

算法编写
main 函数

首先我们关注雪球网的 robots 协议,发现其中 User-agent:* 表示禁止信息爬取。所以我们需要访问时以 Mozilla5.0 访问。

在这里插入图片描述

因此,当我们使用 requests 库时,爬虫会忠实告诉服务器,这次访问是 python 一个库的程序产生的。雪球网可以让这个访问不支持,所以应该让程序模拟一个浏览器发送爬虫请求。

因此我们需要修改 get 方法的 kv 字段,以 mozilla/5.0 表示标准的浏览器身份标识字段。

def main():
    stock_list_url = 'http://21.push2.eastmoney.com/api/qt/clist/get?cb=jQuery11240216110963949796_1586611666127&pn=1&pz=20&po=1&np=1&ut=bd1d9ddb04089700cf9c27f6f7426281&fltt=2&invt=2&fid=f3&fs=m:0+t:6,m:0+t:13,m:0+t:80,m:1+t:2,m:1+t:23&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&_=1586611666172'
    header = {
    
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36"}	#表示标准的浏览器身份标识字段
    stock_list = []

    get_stock_list(stock_list, stock_list_url, header)
    get_stock_info(stock_list, header)

我们设定两个函数,一个用来返回 Stock 列表,一个用来爬取具体的信息并写入文件

get_page 函数

直接利用 requests 的通用代码框架编写即可。

def get_page(url, header):
    try:
        r = requests.get(url, headers=header)
        r.raise_for_status()
        r.encoding = r.apparent_encoding
        return r.text
    except Exception as e:
        print(e)
get_stock_list 函数

get_page 对应的网站的 html 源码如下图所示,而我们需要获得其中 “f12” 对应的键值对作为 Stock 的代码。经过分析,我们可以明确得知,这是 json 格式的键值对。提取其中的信息并进行检索,可以采用 json.loads 方法,re 与正则表达式。

其函数大致思路为:采用 try - except 的调试错误框架。首先进行 json 格式规范化,删去不必要的符号和字符串,然后利用 json 库中的 loads 方法形成字典,接着进行检索,获得 “f12” 对应的字符串,最后利用正则表达式匹配 “f12” 并将键值对的内容放进列表中。

在这里插入图片描述

基本信息组织与提取思路是:

1.提取标准 json 并转为 dict

首先删去标头的 “JQuery…” 和末尾的 “);” 部分形成标准的 json 格式。

stocks2 = page.replace(");", "")
stocks = stocks2.replace("jQuery11240216110963949796_1586611666127(", "")
stocks = json.loads(stocks)

此时我对目前的 json 格式整理如下:

{
    
    
    "rc":0,
 	"rt":6,
 	"svr":182995795,
 	"lt":1,
 	"full":1,
 	"data":{
    
    
     	"total":3969,
        "diff":[
            {
    
    
                "f1":2, "f2":57.76, "f3":20.01, ...... "f12":"688365", "f13":1, "f14":"光云科技",......
            },
            {
    
    
                "f1":2, "f2":100.0, "f3":10.85, ...... "f12":"688039","f13":1,"f14":"当虹科技",......
            },
            ......
            {
    
    
                "f1":2, "f2":14.27, "f3":10.02, ...... "f12":"300203","f13":0,"f14":"聚光科技",......
            }
        ]
 	}
}

可以看到,当 json 格式通过 json.loads 转为字典形式,便形成了四维的字典,而我们需要的信息在 diff 的键值对下,因此,我们需要提取 [“data”][“diff”] ,以获取我们的信息,并进一步进行处理。

stocks = stocks["data"]["diff"]

提取完毕后,程序成功返回了二维的字典。

2.转为 string

获取了 diff 键值对的字典后后,我们转为 string 格式,并删去不必要的字符 “[”, “]”, “{” , “,” ,最后,根据 “}” 对字符串进行切片。

stocks = stocks["data"]["diff"]
stocks = str(stocks)
stocks = stocks.replace("[", "")
stocks = stocks.replace("]", "")
stocks = stocks.replace("{", "")
stocks = stocks.replace(",", "")
stocks = stocks.split("}")

因此,经过修改后,目前的数据格式如下:

"f1":2, "f2":57.76, "f3":20.01, ...... "f12":"688365", "f13":1, "f14":"光云科技",......,
"f1":2, "f2":100.0, "f3":10.85, ...... "f12":"688039","f13":1,"f14":"当虹科技",......,
......
"f1":2, "f2":14.27, "f3":10.02, ...... "f12":"300203","f13":0,"f14":"聚光科技",......

如果用 python 中更直观的字典的格式类型表示,则形式如下:

{
    
    
    {
    
    "f1":2, "f2":57.76, "f3":20.01, ...... "f12":"688365", "f13":1, "f14":"光云科技",......},
   	{
    
    "f1":2, "f2":100.0, "f3":10.85, ...... "f12":"688039","f13":1,"f14":"当虹科技",......},
    ...
    {
    
    "f1":2, "f2":14.27, "f3":10.02, ...... "f12":"300203","f13":0,"f14":"聚光科技",......},  
}

3.进行 “f12” 的键值对模式匹配,并放入 stock_list

此时我们需要键值对为 f12 和 f13 的信息,这些就是 Stock 对应的代码。

而在字符串中进行模式匹配,我们可以采用 Re 库和正则表达式,并将结果添加进 stock_list 中。

在正则表达式中,(\d*) 表示只匹配括号中的字符,其形式为数字 0 − 9 0-9 09 0 − n 0-n 0n 次扩展。

for stock in stocks:
	stock = re.findall("f12': '(\d*)' 'f13", stock)
	stock_list.append(stock)

整个 get_stock_list 函数部分的代码如下:

def get_stock_list(stock_list, stock_list_url, header):
    try:
        page = get_page(stock_list_url, header)	#获取页面的html源码
        print(page)
        stocks2 = page.replace(");", "")	#将最后的");"给删去
        print("stock2:" + stocks2)
        stocks = stocks2.replace("jQuery11240216110963949796_1586611666127(", "")	#将前面的"jQuery11240216110963949796_1586611666127("部分给删去,形成标准json格式
        print("stocks:" + stocks)
        stocks = json.loads(stocks)	#将json格式数据转换为字典
        print("json change ok")
        stocks = stocks["data"]["diff"]	#检索到diff的键值对
        stocks = str(stocks)
        print("string change ok")
        stocks = stocks.replace("[", "")	#替换其他字符,并以}作为切片的分割位置
        stocks = stocks.replace("]", "")
        stocks = stocks.replace("{", "")
        stocks = stocks.replace(",", "")
        stocks = stocks.split("}")
        for stock in stocks:
            print("stock:" + stock)
            stock = re.findall("f12': '(\d*)' 'f13", stock)	#利用正则表达式查找f12部分
            print("stock:" + str(stock))
            stock_list.append(stock)	#将股票代码放入stock_list中
    except Exception as e:
        print(e)

这一部分运行结果如下所示:

在这里插入图片描述

get_stock_info 函数

1.格式处理

由于 re.findall 是以列表类型返回全部能匹配的子串,所以要先对格式进行处理

for stock in stock_list:
	stock = str(stock)
    stock = stock.replace("[", "")
    stock = stock.replace("]", "")
    stock = stock.replace("'", "")
    stock = stock.replace("'", "")
    stock_info_url = 'https://xueqiu.com/S/SZ' + str(stock)

这样就能得到正确的 Stock 信息的链接。

2.返回网页源码信息

采用 try-except 的调试错误框架,调用 get_page 函数,返回源码信息。

try:
	stock_info = {
    
    }
	page = get_page(stock_info_url, header)
except Exception as e:
	print(e)

3.采用 re 和正则表达式进行信息检索

name:检索位置如下

在这里插入图片描述

name = re.findall('<div class="stock-name">(.*?)<', page, re.S)
name = name[0]
stock_info['stock'] = name
stock_info['num'] = stock

其他信息:检索位置如下

<td> 信息 </td>

并将所有的提取,以列表形式返回。

在这里插入图片描述

datas = re.findall(r'<td>(.*?)<span.*?>(.*?)</span>', page, re.S)

以列表形式返回的格式如下:

[['最高','4.68'], ['今开','4.24'], ['涨停', '4.68'], [......], ......]

4.写入文件

for data in datas:
	key = data[0]
	val = data[1]
	stock_info[key] = val
with open('gupiao.txt', "a", encoding="utf-8") as f:
	f.write(str(stock_info) + '\n')

我们来剖析一下整个函数中 stock_info 变量的改变过程

for stock in stock_list:
    stock_info = {
    
    }
	name = re.findall('<div class="stock-name">(.*?)<', page, re.S)
	name = name[0]
	stock_info['stock'] = name	#name为经过findall检索的股票名字

	stock_info['num'] = stock	#stock为股票代码,来自stock_list经过replace后的标准格式

	datas = re.findall(r'<td>(.*?)<span.*?>(.*?)</span>', page, re.S)
	for data in datas:
        key = data[0]	#key为'最高','最低','今开'等
        val = data[1]	#val为key对应的值
        stock_info[key] = val

根据上述算法的中心思路部分, stock_info 为字典形式,其经过程序运行后的格式如下:

{
    
    'name':'泰禾集团','num':'000732','最高':'4.68', '今开':'4.24', '涨停': '4.68', ...}

完全的函数代码如下:

def get_stock_info(stock_list, header):
    count = 0
    for stock in stock_list:
        stock = str(stock)
        stock = stock.replace("[", "")
        stock = stock.replace("]", "")
        stock = stock.replace("'", "")
        stock = stock.replace("'", "")	#去掉因findall返回列表格式而形成的多余字符
        stock_info_url = 'https://xueqiu.com/S/SZ' + str(stock)
        print("stock_info_url:" + stock_info_url)
        try:
            stock_info = {
    
    }
            page = get_page(stock_info_url, header)
            name = re.findall('<div class="stock-name">(.*?)<', page, re.S)	#正则匹配
            name = name[0]
            stock_info['stock'] = name	#name为经过findall检索的股票名字
            stock_info['num'] = stock	#stock为股票代码,来自stock_list经过replace后的标准格式
            datas = re.findall(r'<td>(.*?)<span.*?>(.*?)</span>', page, re.S)	#正则匹配
            for data in datas:
                key = data[0]	#key为'最高','最低','今开'等
                val = data[1]	#val为key对应的值
                stock_info[key] = val
            with open('gupiao.txt', "a", encoding="utf-8") as f:
                f.write(str(stock_info) + '\n')	#写入文件
        except Exception as e:
            print(e)
        count = count + 1
        print('\r当前进度:{:.2f}%'.format(count * 100 / len(stock_list)), end='')

这部分运行结果如下所示:

在这里插入图片描述

运行结果

在这里插入图片描述

源代码
import requests
import re
import json


def get_page(url, header):
    try:
        r = requests.get(url, headers=header)
        r.raise_for_status()
        r.encoding = r.apparent_encoding
        return r.text
    except Exception as e:
        print(e)
  

def get_stock_list(stock_list, stock_list_url, header):
    try:
        page = get_page(stock_list_url, header)	#获取页面的html源码
        print(page)
        stocks2 = page.replace(");", "")	#将最后的");"给删去
        print("stock2:" + stocks2)
        stocks = stocks2.replace("jQuery11240216110963949796_1586611666127(", "")	#将前面的"jQuery11240216110963949796_1586611666127("部分给删去,形成标准json格式
        print("stocks:" + stocks)
        stocks = json.loads(stocks)	#将json格式数据转换为字典
        print("json change ok")
        stocks = stocks["data"]["diff"]	#检索到diff的键值对
        stocks = str(stocks)
        print("string change ok")
        stocks = stocks.replace("[", "")	#替换其他字符,并以}作为切片的分割位置
        stocks = stocks.replace("]", "")
        stocks = stocks.replace("{", "")
        stocks = stocks.replace(",", "")
        stocks = stocks.split("}")
        for stock in stocks:
            print("stock:" + stock)
            stock = re.findall("f12': '(\d*)' 'f13", stock)	#利用正则表达式查找f12部分
            print("stock:" + str(stock))
            stock_list.append(stock)	#将股票代码放入stock_list中
    except Exception as e:
        print(e)


def get_stock_info(stock_list, header):
    count = 0
    for stock in stock_list:
        stock = str(stock)
        stock = stock.replace("[", "")
        stock = stock.replace("]", "")
        stock = stock.replace("'", "")
        stock = stock.replace("'", "")	#去掉因findall返回列表格式而形成的多余字符
        stock_info_url = 'https://xueqiu.com/S/SZ' + str(stock)
        print("stock_info_url:" + stock_info_url)
        try:
            stock_info = {
    
    }
            page = get_page(stock_info_url, header)
            name = re.findall('<div class="stock-name">(.*?)<', page, re.S)	#正则匹配
            name = name[0]
            stock_info['stock'] = name	#name为经过findall检索的股票名字
            stock_info['num'] = stock	#stock为股票代码,来自stock_list经过replace后的标准格式
            datas = re.findall(r'<td>(.*?)<span.*?>(.*?)</span>', page, re.S)	#正则匹配
            for data in datas:
                key = data[0]	#key为'最高','最低','今开'等
                val = data[1]	#val为key对应的值
                stock_info[key] = val
            with open('gupiao.txt', "a", encoding="utf-8") as f:
                f.write(str(stock_info) + '\n')	#写入文件
        except Exception as e:
            print(e)
        count = count + 1
        print('\r当前进度:{:.2f}%'.format(count * 100 / len(stock_list)), end='')        
        

def main():
    stock_list_url = 'http://21.push2.eastmoney.com/api/qt/clist/get?cb=jQuery11240216110963949796_1586611666127&pn=1&pz=20&po=1&np=1&ut=bd1d9ddb04089700cf9c27f6f7426281&fltt=2&invt=2&fid=f3&fs=m:0+t:6,m:0+t:13,m:0+t:80,m:1+t:2,m:1+t:23&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&_=1586611666172'
    header = {
    
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36"}	#表示标准的浏览器身份标识字段
    stock_list = []

    get_stock_list(stock_list, stock_list_url, header)
    get_stock_info(stock_list, header)
    

if __name__ == "__main__":
    main()

d=’’)

def main():
stock_list_url = ‘http://21.push2.eastmoney.com/api/qt/clist/get?cb=jQuery11240216110963949796_1586611666127&pn=1&pz=20&po=1&np=1&ut=bd1d9ddb04089700cf9c27f6f7426281&fltt=2&invt=2&fid=f3&fs=m:0+t:6,m:0+t:13,m:0+t:80,m:1+t:2,m:1+t:23&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&_=1586611666172’
header = {“User-Agent”: “Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36”} #表示标准的浏览器身份标识字段
stock_list = []

get_stock_list(stock_list, stock_list_url, header)
get_stock_info(stock_list, header)

if name == “main”:
main()


猜你喜欢

转载自blog.csdn.net/weixin_44979150/article/details/123430082