C# NetCore使用AngleSharp爬取周公解夢數據


這一章詳細講解編碼過程

那么接下來就是碼代碼了,GO

新建NetCore WebApi項目 空的就可以

 

 NuGet安裝

Install-Package AngleSharp  

 或者界面安裝

 

using。。

默認本地裝有mysql或者有遠程開放的mysql數據庫,如何安裝mysql,園區有很多文章都詳細說明。

配置文件添加mysql連接 appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "MySql": "server=localhost;user id=root;pwd=root;database=dreaminfo;"
  }
}

 

新建實體類,這里由於比較簡單,所以創建到一起,實際工作中最好不要這樣,可讀性較差

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;

namespace WebAPI.Models
{
    public class Dream
    {
        [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }
        public string Name { get; set; }
        public string Url { get; set; }
        public string Summary { get; set; }
        public string CateName { get; set; }
        public DateTime? CreateTime { get; set; }
    }
    public class DreamInfo
    {
        [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }
        public int FkDreamId { get; set; }
        public string DreamName { get; set; }
        public string Name { get; set; }
        public string Content { get; set; }
        public DateTime? CreateTime { get; set; }
    }
}

 

安裝,mysql的ef支持

Install-Package Pomelo.EntityFrameworkCore.MySql

 

 

創建DBContext

using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace WebAPI.Models
{
    public class MainDBContext : DbContext
    {
        public MainDBContext(DbContextOptions<MainDBContext> options) : base(options)
        {

        }
        private string connection;
        public MainDBContext(string connection) => this.connection = connection;
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!string.IsNullOrWhiteSpace(connection))
                optionsBuilder.UseMySql(connection);
        }
        public DbSet<Dream> Dream { get; set; }
        public DbSet<DreamInfo> DreamInfo { get; set; }
    }
}

 配置服務

 public void ConfigureServices(IServiceCollection services)
        {
            var mysqlCon = Configuration.GetSection("ConnectionStrings:MySql").Value;
            services.AddDbContext<MainDBContext>(l => l.UseMySql(mysqlCon, b => b.MigrationsAssembly("Dream"))); //跳轉查看
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

然后是數據庫遷移 ,這里使用的是codefirst,所以創建好實體類之后在實體類及EF存在的程序集執行Add-Migration命令,我這里做的比較簡單,有的如果是按照框架來設計,可能實體類被設計在單獨的類庫里

這里是直接在webapi里面

執行之后會多出來遷移的的文件,然后執行Update-Database就可以生成數據庫拉。

后期如果數據庫有變更還是同樣的操作

工具》打開Nuget包管理器》程序包管理控制台 選中EF所在類庫

Add-Migration Update-20181123
//然后等待變更文件生成之后執行
Update-Database

然后我們看下數據庫

我們刷新下,數據庫和對應的表都有了。

 

獲取頁面內容

新建一個空的API控制器DreamController

然后創建獲取類別數據的方法

        /// <summary>
        /// 定義一個獲取列表數據的方法,需要傳遞一個列表頁面的url地址
        /// </summary>
        /// <param name="rul"></param>
        public void GetData(string url)
        {
            //這個跟我之前使用的瀏覽器驅動類似,也是通過選擇器和xpath來爬取數據
            //區別在於那個是模擬真人操作,這個是通過通過HttpWebRequest直接請求
            var html = GetHtml(url);
            //創建一個(可重用)解析器前端
            var parser = new HtmlParser();
            var document = parser.Parse(html);
            //找到一個頁面有多少個dream信息
            var mengList = document.QuerySelectorAll("#list > div.main > div.l-item > ul > li");
            //循環獲取夢的信息
            for (var i = 0; i < mengList.Length; i++)
            {
                var meng = new Dream();
                meng.CateName = "人物";
                //名稱
                meng.Name = mengList[i].QuerySelector("h3 > a").TextContent;
                //簡介
                meng.Summary = mengList[i].QuerySelector("p").TextContent;
                //連接
                meng.Url = mengList[i].QuerySelector("h3 > a").GetAttribute("href");
                meng.CreateTime = DateTime.Now;
                _context.Dream.Add(meng);
                _context.SaveChanges();//可以單個保存,也可以獲取當前頁數據之后保存一次,可優化的點有很多,這里不再詳細描述
            }
        }

 

但是這里獲取的都是單頁的數據,我們繼續上一章說的,使用選擇器里面的.next來尋找翻頁按鈕附帶的連接

然后看看最后一頁是什么樣式

 

發現最后一頁就沒有下一頁樣式了,就獲取不到值了,好的 那么我們開始翻頁

至於翻頁 還有第二種思路,就是直接獲取這種類別下的最后一頁的頁碼,然后循環就行了,似乎比較簡單,我們就用這一種

完善后的方法

 /// <summary>
        /// 定義一個獲取列表數據的方法,需要傳遞一個列表頁面的url地址
        /// </summary>
        /// <param name="rul"></param>
        public void GetData(string url, int pageIndex)
        {
            var thisUrl = url + pageIndex + ".html";
            var html = GetHtml(thisUrl);
            //創建一個(可重用)解析器前端
            var parser = new HtmlParser();
            var document = parser.Parse(html);
            var mengList = document.QuerySelectorAll("#list > div.main > div.l-item > ul > li");
            //#list > div.main > div.pagelist > a.end
            //獲取最末頁數據
            if (!PageEnd.HasValue)
            {
                var pageEnd = document.QuerySelector("#list > div.main > div.pagelist > a.end")?.TextContent;
                PageEnd = Convert.ToInt32(pageEnd);
            }
            var list = new List<Dream>();
            for (var i = 0; i < mengList.Length; i++)
            {
                var meng = new Dream();
                meng.CateName = "其他";
                //名稱
                meng.Name = mengList[i].QuerySelector("h3 > a").TextContent;
                //簡介
                meng.Summary = mengList[i].QuerySelector("p").TextContent;
                //連接
                meng.Url = mengList[i].QuerySelector("h3 > a").GetAttribute("href");
                meng.CreateTime = DateTime.Now;
                _context.Dream.Add(meng);
                _context.SaveChanges();
            }
            //翻頁記錄頁碼
            pageIndex++;
            if (pageIndex <= Convert.ToInt32(PageEnd))
            {
                Console.WriteLine(pageIndex + "/" + PageEnd);
                //翻頁完之前一直抓取
                GetData(url, pageIndex);
            }
        }

 

定義接口,輸出一下看看獲取了多少頁的數據

PageEnd是在控制器里面定義的

   

 public class DreamController : ControllerBase
    {
        static int? PageEnd = null;
       //other code

 

 [HttpGet]
        public async Task<IActionResult> GetMengXinfo()
        {
            GetData("http://www.xzw.com/jiemeng/lib/renwu/", 1);
            return Ok(PageEnd);
        }

調試走一波

 

 

 可以看到這里數據其實已經拿到了,那我們就開始往數據庫保存

由於是演示,我們找一個數據量較少的分類來獲取 使用“其他”分類獲取,調用接口,看到返回值是45

這個跟我們找到對應的數據是一樣的

說明數據是ok的,我們看下數據庫,dream表已經有數據了

 

到這里 分類數據就獲取到了,其他幾個分類 可以使用一個數組,循環數組拼連接獲取,也可以放到后台任務慢慢執行,比如Hangfire

詳細信息獲取

詳細信息的頁面我們其實是有的,就是dream表里面的url字段,拼接上domain之后就成了詳細頁面的連接,我們又可以使用AngleSharp來獲取數據拉。。

其實AngleSharp是對獲取到的文檔進行解析,里面構建了很多C#和js習慣的語法,比如 

 

 document.QuerySelector(選擇器)//選擇器查詢單個符合條件的數據

 document.QuerySelectorAll("#list > div.main > div.l-item > ul > li");獲取復合條件的元素集合

 獲取詳情頁面內容,並給實體對象賦值,這里有很多可以試探的方法,大家可以嘗試一下,我這個只是簡單的為了完成我想要的功能。

  /// <summary>
        /// 獲取詳情頁的頁面解析
        /// </summary>
        /// <param name="dreamId"></param>
        /// <param name="url"></param>
        public void GetDetailData(int dreamId, string url)
        {
            var html = GetHtml(url);
            var parser = new HtmlParser();
            var document = parser.Parse(html);
            //#wraper > div.main-wrap > div.pleft.fl > div.viewbox.box > div.sbody
            var sbody = document.QuerySelector("#wraper > div.main-wrap > div.pleft.fl > div.viewbox.box > div.sbody");
            var dllist = sbody.QuerySelectorAll("dl");
            var title = sbody.QuerySelector("h2").TextContent;//標題
            if (dllist.Length > 0)
            {
                foreach (var detail in dllist)
                {
                    //#wraper > div.main-wrap > div.pleft.fl > div.viewbox.box > div.sbody > dl:nth-child(4) > dt > strong
                    var info = new DreamInfo();
                    info.FkDreamId = dreamId;
                    info.DreamName = title;
                    info.Name = detail.QuerySelector("dt > strong").TextContent;
                    info.Content = detail.QuerySelector("dd").TextContent; ;
                    info.CreateTime = DateTime.Now;
                    _context.DreamInfo.Add(info);
                    _context.SaveChanges();
                }

            }
        }

 

 定義接口

[Route("detail")]
        [HttpGet]
        public async Task<IActionResult> GetDreamInfo()
        {
            var domain = "http://www.xzw.com";
            //查詢出其他分類的夢數據來解析詳細內容
            var dreamList = _context.Dream.Where(l => l.CateName == "其他").ToList();
            foreach (var dream in dreamList)
            {
                GetDetailData(dream.Id, domain + dream.Url);
            }
            return Ok(PageEnd);
        }

 

 執行接口https://localhost:44329/api/dream/detail

這里不展示調試信息了,怕被說水內容

然后看數據庫dreaminfo也有了數據

 

找到對應頁面 

 

 

 到這里,數據基本都可以獲取到了,其他分類可以做計划任務來獲取數據

 

總結

  對自己感興趣的東西,可能下決心投入的時間會更長一點,共勉。

  排版較亂可能影響閱讀,不過內容還是能看到的。。

  GitHub:https://github.com/ermpark/dream.git

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM