如何利用 C# 爬取「财报说」中的股票数据?

两年前在一个微信群中,听一些朋友讨论林明璋的“超级数字力”课程,后来也听 李笑来 推荐过他的课程。由于比较远,我只买了一本 林明璋 写的《用生活尝试就能看懂财务报表》来读。再后来发现了他们做的这个网站 “财报说”。从该网站上我们可以看到各支股票按照 申万行业分类 做成了列表;每只股票都提供了 4年 的资产负债表、现金流量表 和 利润表,并利用一套五个维度(现金流、营运能力、盈利能力、财务结构、偿债能力)的模型进行评分。

我以前在图文 赚钱是刚需,如何正确的交易股票? 中介绍过自己的选股策略,从 000015 红利指数 的十大权重股中选择哪些分红能力强,且处于低估位置的股票。既然 林明璋 把自己的知识做成了网站,为何不把这些数据爬下来也作为自己在下一个周期选股的参考呢?


好了,我们开始吧!

首先,我们看一下「财报说 - 行业板块」对应的网页。

网址为:

https://beta.caibaoshuo.com/cn_industries/3

原网页

其次,我们看一下“一级行业”、“二级行业”、“股票列表”部分对应的网页源码。

<u>“一级行业”部分对应的网页源码如下</u>:

一级行业源码

通过网页的HTML DOM TREE找到第一个industries-header_section-lists类中的a标签,就可以得到一级行业的名称以及对应的网页地址。

<u>“二级行业”部分对应的网页源码如下</u>:

二级行业源码

通过网页的HTML DOM TREE找到第二个industries-header_section-lists类中的a标签,就可以得到二级行业的名称以及对应的网页地址。

<u>“股票列表”部分对应的网页源码如下</u>:

股票列表源码

通过网页的HTML DOM TREE找到table tbody tr标签(table 内部的 tbody 内部的 tr 标签,详见 CSS 的选择器语法)每一个 tr 对应表格中的一行数据,找到tr内部的td标签,就找到了希望获取的“股票名称”、“股票编号”以及显示股票详细信息的“网址”。

接着,我们爬取对应股票的详细数据。

如上图所示对应的网址为:

https://beta.caibaoshuo.com/companies/000858

股票详情页面

对应的源码为:

详情页源码

通过网页的HTML DOM TREE找到header-price类,在该类中找到valuepepb-ratioratio-lowratio-normalratio-high这些类,就可以得到“当前股价”、“当前市盈率”、“当前市净率”、“10倍市盈率”、“20倍市盈率”、“30倍市盈率”的数据。

最后,我们用 Jumony 这套开源代码来获取网页对应的 HTML DOM TREE ,这套开源代码可以在 Github 上下载。

下载地址为:

https://github.com/Ivony/Jumony

Jumony下载

这里对 Jumony 就不做过多介绍了,要是大家感兴趣,可以在图文下方留言,我后面再写几篇图文来介绍这个工具。


找到了所要爬取的网页地址,分析完网页的源码,确定了所用的工具和技术路线,剩下的就是写代码进行实现了。

Step1. 定义存储行业类型的结构 Industry

public class Industry
{
    /// <summary>
    /// 行业名称
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// 对应网址
    /// </summary>
    public string Url { get; set; }
}

Step2. 获取一级行业的列表 List<Industry>

private const string Url = "https://beta.caibaoshuo.com";

public static List<Industry> GetLevelOne()
{
    List<Industry> result = new List<Industry>();
    
    string url = Url + @"/cn_industries/3";
    IHtmlDocument doc = HtmlSpiter.GetHtmlDocument(url);
    if (doc == null)
        return result;

    List<IHtmlElement> lists = doc.Find(".industries-header__section-lists").ToList()[0].Find("a").ToList();

    for (int i = 0; i < lists.Count; i++)
    {
        Industry item = new Industry();
        item.Name = lists[i].InnerHtml();
        item.Url = Url + lists[i].Attribute("href").AttributeValue;
        result.Add(item);
    }
    return result;
}

Step3. 获取二级行业的列表 List<Industry>

public static List<Industry> GetLevelTwo(Industry levelOne)
{
    List<Industry> lstResult = new List<Industry>();

    IHtmlDocument doc = HtmlSpiter.GetHtmlDocument(levelOne.Url);
    if (doc == null)
        return lstResult;

    List<IHtmlElement> lists = doc.Find(".industries-header__section-lists").ToList()[1].Find("a").ToList();

    for (int i = 0; i < lists.Count; i++)
    {
        Industry item = new Industry();
        item.Name = lists[i].InnerHtml();
        item.Url = Url + lists[i].Attribute("href").AttributeValue;
        lstResult.Add(item);
    }
    return lstResult;
}

Step4. 定义存储股票信息的结构 StockCbs

public class StockCbs
{
    /// <summary>
    /// 股票名称
    /// </summary>
    public string StockName { get; set; }
    /// <summary>
    /// 股票编码
    /// </summary>
    public string StockId { get; set; }
    /// <summary>
    /// 网址
    /// </summary>
    public string Url { get; set; }
    /// <summary>
    /// 根据财报得出的评分
    /// </summary>
    public string CsbCourse { get; set; }
    /// <summary>
    /// 当前价格
    /// </summary>
    public string Price { get; set; }
    /// <summary>
    /// 市盈率
    /// </summary>
    public string Pe { get; set; }
    /// <summary>
    /// 市净率
    /// </summary>
    public string Pb { get; set; }
    /// <summary>
    /// 10倍市盈率
    /// </summary>
    public string Pe10 { get; set; }
    /// <summary>
    /// 20倍市盈率
    /// </summary>
    public string Pe20 { get; set; }
    /// <summary>
    /// 30倍市盈率
    /// </summary>
    public string Pe30 { get; set; }
}

Step5. 得到股票列表 List<StockCbs>

public static List<StockCbs> GetStocks(Industry industry)
{
    List<StockCbs> lstResult = new List<StockCbs>();
    IHtmlDocument doc = HtmlSpiter.GetHtmlDocument(industry.Url);
    if (doc == null)
        return lstResult;

    List<IHtmlElement> lists = doc.Find("table tbody tr").ToList();

    for (int i = 0; i < lists.Count; i++)
    {
        List<IHtmlElement> row = lists[i].Find("td").ToList();
        
        StockCbs item = new StockCbs();
        item.StockName = row[1].FindFirst(".company-link").InnerHtml().Trim();
        item.Url = Url + row[1].FindFirst(".company-link").Attribute("href").AttributeValue;
        item.StockId = row[1].FindFirst(".code-in-list").InnerHtml().Trim();
        item.CsbCourse = row[2].InnerHtml().Trim();

        doc = HtmlSpiter.GetHtmlDocument(item.Url);
        List<IHtmlElement> lst = doc.Find(".header-price").ToList();
        item.Price = lst[0].FindFirst(".value").InnerHtml().Trim();
        item.Pb = lst[0].Find(".pepb-ratio span").ToList()[1].InnerHtml().Trim();
        item.Pe = lst[0].Find(".pepb-ratio span").ToList()[0].InnerHtml().Trim();
        item.Pe10 = lst[0].FindFirst(".ratio-low").FindFirst(".data").InnerHtml().Trim();
        item.Pe20 = lst[0].FindFirst(".ratio-normal").FindFirst(".data").InnerHtml().Trim();
        item.Pe30 = lst[0].FindFirst(".ratio-high").FindFirst(".data").InnerHtml().Trim();

        lstResult.Add(item);
    }
    return lstResult;
}

Step6. 结果输出

  • 爬取一级行业:爬取数据,填充“一级行业下拉列表”。
  • 爬取二级行业:根据选择的一级行业,爬取数据,填充“二级行业下拉列表”。
  • 爬取网页:爬取二级行业对应的所有股票信息。
  • 跳转到网页:跳转到二级行业所在的网页。
结果输出

到此为止,关于如何利用 C# 语言爬取「财报说」中的股票数据,就介绍完了。大家有什么问题可以在图文的下方给我留言,今天就到这里吧!See You!


相关图文:

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

推荐阅读更多精彩内容