這一章詳細講解編碼過程
那么接下來就是碼代碼了,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