教你用 Python 快速获取行业板块股,辅助价值投资

本篇文章,我们来聊聊如何根据「 行业板块 」辅助我们进行价值投资

# 1. 行业板块

行业和概念股在定义上还是有很大区别的。

一般来说,概念板块的风险更大,因为它是基于某个消息的短期炒作,很不稳定,所以风险更大。

板块按照股票行业分类,往往注重长期和较高的稳定性。

实际投资方面,短期内可以根据“市场热点”从概念股中选股,中长期可以根据“行业板块”选股。

# 2. 爬取相关板块及个股列表

目标对象:

1652168825(1).png

2-1 板块列表

首先,我们使用 「 Toggle JavaScript 」插件发现页面中的行业板块数据来源于下面的请求结果

http://**/?q=cn|bk|17&n=hqa&c=l&o=pl,d&p=1020&_dc=1650680429759

image.gif

其中,参数为 p 和 dc 为可变参数,p 代表页码数(从 1 开始),dc 代表 13 位的时间戳,其他查询参数都是固定内容

然后,我们编写代码获取响应数据,使用正则表达式匹配出行业列表的数据

<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="" cid="n25" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">...
self.ps_url = 'http://**/?q=cn|bk|17&n=hqa&c=l&o=pl,d&p={}050&_dc={}'
....
def __get_timestramp(self):
"""
获取13位的时间戳
:return:
"""
return int(round(time.time() * 1000))
...
def get_plates_list(self, plate_keyword):
"""
获取所有板块
:return:
"""
plates = []

index = 0
while True:
url = self.ps_url.format(index + 1, self.__get_timestramp())

解析数据

resp = self.session.get(url, headers=self.headers).text
match = re.compile(r'HqData:(.*?)};', re.S)
result = json.loads(re.findall(match, resp)[0].strip().replace("\n", ""))
if not result:
break

根据关键字,过滤有效板块

temp_plate_list = [item for item in result if plate_keyword in item[2]]
index += 1

for item in temp_plate_list:
print(item)

plates.append({
"name": item[2],
"plate_path": item[1],
"up_or_down": str(item[10]) + "%",
"top_stock": item[-6]
})
return plates
...</pre>

最后,根据关键字对板块进行一次筛选,通过板块名、板块路径 PATH、板块涨跌幅、最大贡献股票名称重新组装成一个列表

注意:通过分析页面发现,根据板块路径 PATH 可以组装成行业板块个股列表页面 URL

比如,行业板块 PATH 为 400128925,那么行业板块对应个股列表的页面 URL 为

2-2 行业个股列表

爬取行业个股列表和上一步数据展示逻辑一样,个股列表数据同样来源于下面请求的结果

http://**/?q=cn|s|bk{}&c=m&n=hqa&o=pl,d&p={}020&_dc={}

其中,bk 后面对应行业板块 PATH,p 代表页码数,_dc 代表 13 位的时间戳

<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="" cid="n35" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">...

个股

self.stock_url = 'http://*/?q=cn|s|bk{}&c=m&n=hqa&o=pl,d&p={}020&_dc={}'
....
def get_stock_list(self, plate_path):
"""
获取某一个板块下所有的个股信息
包含:股票名称、最新价格、涨跌幅、市盈率
:param plate_info:
:return:
"""
index = 0
stocks = []
while True:
url = self.stock_url.format(plate_path, index + 1, self.__get_timestramp())
resp = self.session.get(url, headers=self.headers).text
match = re.compile(r'HqData:(.
?)};', re.S)
result = json.loads(re.findall(match, resp)[0].strip().replace("\n", ""))
if not result:
break
index += 1

for item in result:
if item[-1] < 0:
continue
stocks.append({
"stock_name": item[2],
"pe": item[-1],
"price": item[8],
"up_or_down": str(item[12]) + "%"
})

按pe降序排列

stocks.sort(key=lambda x: x["pe"])

return stocks</pre>

通过正则表达式对响应结果进行匹配后,获取个股的名称、PE 市盈率、价格、涨跌幅 4 个关键数据

最后,对个股列表按 PE 进行升序排列后直接返回

# 3. 服务化

当然,我们可以为前端服务这部分逻辑,从而提高用户体验。

比如使用FastAPI可以快速创建两个服务:根据关键字获取行业板块列表和根据板块路径获取个股列表。

<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="" cid="n41" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">from pydantic import BaseModel

板块

class Plate(BaseModel):
content: str # 关键字

板块下的个股

class PlateStock(BaseModel):
plate_path: str # 板块路径

===========================================================

...

获取板块列表

@app.post("/xag/plate_list")
async def get_plate_list(plate: Plate):
pstock = PStock()
try:
result = pstock.get_plates_list(plate.content)
return success(data=result, message="查询成功!")
except Exception as e:
return fail()
finally:
pstock.teardown()

获取某一个板块下的所有股票列表

@app.post("/xag/plate_stock_list")
async def get_plate_list(plateStock: PlateStock):
pstock = PStock()
try:
result = pstock.get_stock_list(plateStock.plate_path)
return success(data=result, message="查询成功!")
except Exception as e:
return fail()
finally:
pstock.teardown()
...</pre>

前端以 Uniapp 为例,使用 uni-table 组件展示行业板块列表及个股列表

部分代码如下:

<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="" cid="n44" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">//个股列表 platestock.vue
...
<view class="box">
<uni-forms ref="baseForm" :modelValue="baseFormData" :rules="rules">
<uni-forms-item label="关键字" required name="content">
<uni-easyinput v-model="baseFormData.content" placeholder="板块关键字" />
</uni-forms-item>
</uni-forms>
<button type="primary" @click="submit('baseForm')">提交</button>


<view class="result" v-show="result.length>0">
<uni-table ref="table" border stripe emptyText="暂无数据">
<uni-tr class="uni-item">
<uni-th align="center" class="uni-th" width="100%">板块</uni-th>
<uni-th align="center" class="uni-th" width="100%">涨跌幅</uni-th>
<uni-th align="center" class="uni-th" width="100%">强势股</uni-th>
</uni-tr>
<uni-tr class="uni-item" v-for="(item, index) in result" :key="index" @row-click="rowclick(item)">
<uni-td class="uni-th" align="center">{{ item.name }}</uni-td>
<uni-td align="center" class="uni-th">{{ item.up_or_down }}</uni-td>
<uni-td align="center" class="uni-th">{{ item.top_stock }}</uni-td>
</uni-tr>
</uni-table>
</view>
</view>
...
methods: {
//表单提交数据
submit(ref) {
this.refs[ref].validate().then(res => { this.http('xag/plate_list', this.baseFormData, {
hideLoading: false,
hideMsg: false,
method: 'POST'
}).then(res => {
console.log("内容:", res.data)
if (res.data && res.data.length > 0) {
this.tip.success("查询成功!") this.result = res.data } else { this.tip.success("查询结果为空,请换一个关键字查询!")
}
}).catch(err => {
console.log("产生异常,异常信息:", err)
})

}).catch(err => {
console.log('err', err);
})
}
...</pre>

最后部署完项目后,在前端页面就能根据板块名选择合适的个股进行投资了

1652168849(1).png

# 4. 总结一下

由于行业板块更适合中长期投资,我们只需要根据某个关键词选择一个板块,就可以在该板块下的股票列表中直观的看到市盈率较低的股票进行投资。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,864评论 6 494
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,175评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,401评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,170评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,276评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,364评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,401评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,179评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,604评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,902评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,070评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,751评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,380评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,077评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,312评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,924评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,957评论 2 351

推荐阅读更多精彩内容