前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >.NET Core玩转爬虫系列之借助正则表达式入门篇

.NET Core玩转爬虫系列之借助正则表达式入门篇

作者头像
Edison.Ma
发布2019-08-23 16:03:41
1.2K1
发布2019-08-23 16:03:41
举报
文章被收录于专栏:DotNet Core圈圈DotNet Core圈圈

接下来一段时间,我会花些时间研究C#玩转爬虫的方法及其实践。

话不多说,开始吧~

一般来说:

设计并实现一个爬虫的步骤是:

模拟登录 -> 模拟发送request请求 -> 取回response数据 -> 提取所需信息并将其进行重新组织 -> 存入DB或文件中 -> 后期处理或展示

流程图

当然,有时还需要适当地应对所抓取目标站点的反爬虫策略,也就是大家常说的反反爬

下文以抓取豆瓣音乐为例来具体说明:

比如,我们在豆瓣音乐中搜索"摇滚"后爬取相关内容,然后写入csv文件中。

目标网址是:

https://music.douban.com/tag/摇滚?start=0&type=T

抓取大概分为如下几个步骤:

获取页数

  • 发请求

每一页中都可以看到总的页数,直接取第1页的就好。

代码语言:javascript
复制
    byte[] buffer = webclient.DownloadData("https://music.douban.com/tag/%E6%91%87%E6%BB%9A?start=0&type=T");
    // utf-8, gb2312, gbk, utf-1......
    string html = System.Text.Encoding.GetEncoding("utf-8").GetString(buffer);

借助以上两行代码,就可以看到该网址Response的HTML字符串(debug时上面一行代码中html的值)为:

  • 使用正则表达式匹配到页数

我们选用模式串 ">[0-9][0-9]{0,}</a>" 进行匹配,由于下面的页码是从第一页算起的,选最大页数就是总页数。

相应的函数GetTotalCount如下:

代码语言:javascript
复制
        private static int GetTotalCount(System.Net.WebClient webclient, int startIdx)
        {
            //html下载
            /* https://music.douban.com/tag/%E6%91%87%E6%BB%9A?start=0&type=T  */

            byte[] buffer = webclient.DownloadData("https://music.douban.com/tag/%E6%91%87%E6%BB%9A?start=" + startIdx + "&type=T");
            // utf-8, gb2312, gbk, utf-1......
            string html = System.Text.Encoding.GetEncoding("utf-8").GetString(buffer);

            MatchCollection pageCount_matches = new Regex(">[0-9][0-9]{0,}</a>").Matches(html);

            if (pageCount_matches.Count == 0)
                return 0;

            string tempPageNum = pageCount_matches[pageCount_matches.Count - 1].Value;
            int.TryParse(tempPageNum.Replace("</a>", string.Empty).Substring(1), out int lastPageNum);

            return lastPageNum;
        }

获取当前页的数据

  • 发请求

用与上一步相同的方法

代码语言:javascript
复制
byte[] buffer = webclient.DownloadData("https://music.douban.com/tag/%E6%91%87%E6%BB%9A?start=0&type=T");
    // utf-8, gb2312, gbk, utf-1......
string html = System.Text.Encoding.GetEncoding("utf-8").GetString(buffer);

debug时可得到当前页对应的HTML字符串:

  • 使用正则表达式匹配到每一天记录

我们选用模式串 "<p class=\"pl\">([\\s\\S]*?)</p>" 进行匹配,可得到如下格式的内容:

刺猬 / 2018-01-08 / 单曲 / 数字(Digital) / 摇滚

接着进行split可得到一个记录的各个属性。

具体的函数GetCurrentPageRecords如下:

代码语言:javascript
复制
    private static void GetCurrentPageRecords(System.Net.WebClient webclient, int startIdx)
    {
        byte[] buffer = webclient.DownloadData("https://music.douban.com/tag/%E6%91%87%E6%BB%9A?start=" + startIdx + "&type=T");
        // utf-8, gb2312, gbk, utf-1......
        string html = System.Text.Encoding.GetEncoding("utf-8").GetString(buffer);

        // Console.WriteLine(html);

        // html分析
        // 通过正则获取到需要的数据
        // <p class="pl">刺猬 / 2018-01-08 / 单曲 / 数字(Digital) / 摇滚</p>
        MatchCollection musicItem_matches = new Regex("<p class=\"pl\">([\\s\\S]*?)</p>").Matches(html);

        int count = musicItem_matches.Count;

        for (int i = 0; i < count; i++)
        {
            string item = musicItem_matches[i].Result("$1");
            string[] strArr = item.Trim().Split('/');

            Record record = new Record
            {
                SongName = strArr[0],
                Date = strArr.ElementAtOrDefault(1),
                Album = strArr.ElementAtOrDefault(2),
                Type = strArr.ElementAtOrDefault(3),
                Topic = strArr.ElementAtOrDefault(4)
            };

            _records.Add(record);
        }
    }

获取所有页的数据

  • 循环修改url中start的值

https://music.douban.com/tag/摇滚?start=0&type=T

第一页的start=0,每页20条,于是后面每一页的start是前一页start的值+20

  • 发请求
  • 将每页的数据加入到Array或List中

写入csv

  • 在VS的NuGet包管理器中为当前项目引入CSVHelper.
  • 将一条记录封装成一个类Record

Record.cs

代码语言:javascript
复制
using System;
using System.Collections.Generic;
using System.Text;

namespace CsSpider
{
    public class Record
    {
        public string SongName { get; set; }
        public string Date { get; set; }
        public string Album { get; set; }
        public string Type { get; set; }
        public string Topic { get; set; }
    }
}

// 举例: 万能青年旅店 / 2010-11-12 / 专辑 / CD / 摇滚
  • 使用CSVHelper把获得的所有数据写入到csv中
代码语言:javascript
复制
    if (_records.Count > 0)
    {
        var writer = new StreamWriter(_filename);

        using (var csv = new CsvWriter(writer))
        {
            csv.WriteRecords(_records);
            Console.WriteLine("导出完成.");
        }
    }

解决乱码问题

只需要给上述writer设置Encoding即可~

代码语言:javascript
复制
        if (_records.Count > 0)
        {
            var writer = new StreamWriter(_filename, false, System.Text.Encoding.UTF8);  // 设置Encoding,防止乱码

            using (var csv = new CsvWriter(writer))
            {
                csv.WriteRecords(_records);
                Console.WriteLine("导出完成.");
            }
        }

运行结果

最后得到的CSV文件如下:

接下来,会发现一个问题:

如果多执行几次,前面的HTML字符串会变成下面这种内容:

代码语言:javascript
复制
<script>var d=[navigator.platform,navigator.userAgent,navigator.vendor].join("|");window.location.href="https://sec.douban.com/a?c=3d001f&d="+d+"&r=https%3A%2F%2Fmovie.douban.com%2Fsubject%2F26590960%2F";</script>

说明此时,你触发了豆瓣的反作弊功能。如果再多执行几次,会出现 403 Error.

解决办法是模拟登录,这个留在下一篇文章再说,敬请关注!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-08-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DotNet技术平台 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 流程图
  • 获取页数
  • 获取当前页的数据
  • 获取所有页的数据
  • 写入csv
  • 运行结果
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档