隐藏

C# 写个小爬虫,实现爬取js加载后的网页

发布:2024/1/17 22:21:38作者:管理员 来源:本站 浏览次数:510

一、前言


因工作需求,需要爬取一网页的表格数据,还要用C#来写,自己搜搜看看捣鼓出了这篇教程。

二、思路


一开始用 WebClient 类 什么的去爬,发现爬的是未加载js的html,没有表格数据我想这就应该要获取加载js渲染后的网页源码,看了下请求,有个html,两个json其中一个json就是目标数据,可惜我比较菜,请求url没有拼出来,用payload参数请求失败了,遂放弃了,就把目标转到js渲染后的源代码

三、库安装


开整,C#嘛!那肯定是Visual Studio!!!

首先新建个项目,控制台程序还是窗口程序就你喜欢了,我用控制台应用程序


然后给项目要安装需要的库

Selenium.WebDriver.ChromeDriver

Selenium.PhantomJS.WebDriver

Selenium.WebDriver ps:低版本的才有OpenQA.Selenium.PhantomJS,我装的是 3.0.0


安装完 ,生成一下

四、码代码


using System;

//添加selenium的引用

using OpenQA.Selenium.PhantomJS;

using OpenQA.Selenium.Chrome;

//using OpenQA.Selenium.Support.UI;

using OpenQA.Selenium;

using System.IO;

using System.Collections.Generic;

using System.Text;

using System.Threading;

using System.Data;

using System.Text.RegularExpressions;


namespace ConsoleApplication1

{

   class Program

   {

       static ChromeDriver driver { get; set; }

       static ICookieJar cookie { get; set; }

       static void Main(string[] args)

       {

           var url = @"https://s8hwxkltn6.jiandaoyun.com/dash/5f48d400a25baa0006034c29";

           GetHtml(url);

       }


       private static void GetHtml(string url)

       {

           PhantomJSDriverService driverService = PhantomJSDriverService.CreateDefaultService();

           driverService.IgnoreSslErrors = true;

           ChromeOptions options = new ChromeOptions();

           options.AddArgument("--headless");

           options.AddArgument("--nogpu");

           List<String> tagNmaeList = new List<string>();

           using (driver = new ChromeDriver(options))

           {

               try

               {

                   driver.Manage().Window.Maximize();

                   driver.Navigate().GoToUrl(url);

                   Thread.Sleep(5000);

                   Console.WriteLine(driver.PageSource); //输出网页源码

               }

               catch (NoSuchElementException)

               {

                   Console.WriteLine("找不到该元素"); ;

               }

           }


       }


    


五、解析数据


添加方法


//分析HTML 数据

private static void GetData(string ddd)

{


   DataRow dr;

   DataTable dt = new DataTable();  //创建datatable,存储数据

   dt.Columns.Add(new System.Data.DataColumn("序号", typeof(System.String)));

   dt.Columns.Add(new System.Data.DataColumn("要求到货时间", typeof(System.String)));

   dt.Columns.Add(new System.Data.DataColumn("合同号", typeof(System.String)));

   dt.Columns.Add(new System.Data.DataColumn("地址", typeof(System.String)));

   dt.Columns.Add(new System.Data.DataColumn("货物名称", typeof(System.String)));

   dt.Columns.Add(new System.Data.DataColumn("规格型号", typeof(System.String)));

   dt.Columns.Add(new System.Data.DataColumn("公司型号", typeof(System.String)));

   dt.Columns.Add(new System.Data.DataColumn("单位", typeof(System.String)));

   dt.Columns.Add(new System.Data.DataColumn("数量", typeof(System.String)));

   dt.Columns.Add(new System.Data.DataColumn("理论重量", typeof(System.String)));

   dt.Columns.Add(new System.Data.DataColumn("金额", typeof(System.String)));

   dt.Columns.Add(new System.Data.DataColumn("备注", typeof(System.String)));

   dt.Columns.Add(new System.Data.DataColumn("合同号2", typeof(System.String)));


   string oo = string.Empty;

   string kk = string.Empty;

   string ll = string.Empty;

   string hh = string.Empty;

   string fileConent = string.Empty;

   string tableContent = string.Empty;

   string rowContent = string.Empty;

   string columnConent = string.Empty;


   string rowPatterm = @"<tr[^>]*>[\s\S]*?<\/tr>";  // 正则取tr行

   string columnPattern = @"<td[^>]*>[\s\S]*?<\/td>"; // 正则取每行的单元格

   

   MatchCollection rowCollection = Regex.Matches(ddd, rowPatterm, RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture); //对tr进行筛选

   for (int i = 1; i < rowCollection.Count; i++)

   {

       rowContent = rowCollection[i].Value;

       MatchCollection columnCollection = Regex.Matches(rowContent, columnPattern, RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture); //对td进行筛选

       if (i == 1) continue; // 第一行表头来的,直接不要,你也可以用来生成datable的列


       #region 数据筛选

       dr = dt.NewRow(); // 创建新行

       for (int j = 0; j < columnCollection.Count - 1; j++)

       {

           columnConent = columnCollection[j].Value;

           int iBodyStart = columnConent.IndexOf(">", 0);

           int iTableEnd = columnConent.IndexOf("</td>", iBodyStart);

           string strWeb = (columnConent.Substring(iBodyStart + 1, iTableEnd - iBodyStart - 1)).Replace("<span>", "").Replace("</span>", ""); //获取最终数据


           if (columnCollection.Count == 14)

           {

               if (j == 0) dr[0] = oo = strWeb;

               if (j == 1) dr[1] = kk = strWeb;

               if (j == 2) dr[2] = ll = strWeb;

               if (j == 3) dr[3] = hh = strWeb;

               if (j > 3) dr[j] = strWeb;

           }

           else

           {

               if (j == 0) dr[j] = oo;

               if (j == 1) dr[j] = kk;

               if (j == 2) dr[j] = ll;

               if (j == 3) dr[j] = hh;

               dr[j + 4] = strWeb;

           }


       }

       dt.Rows.Add(dr);

       #endregion

   }

}


   


六、翻页


在GetHtml() 方法内修改一下部分,其中


using (driver = new ChromeDriver(options))

{

   try

   {

       driver.Manage().Window.Maximize();

       driver.Navigate().GoToUrl(url);

       Thread.Sleep(5000);

       // 点击按钮

       driver.ExecuteScript("return $('.count-sel').click()");

       Thread.Sleep(500);

       // 选择一百条每页

       driver.ExecuteScript("return $(\".x-dropdown a[option='100']\").click()");

  Thread.Sleep(1000);  // 给点时间加载网页

       //Console.WriteLine(driver.PageSource); //输出网页源码

       //GetCookie();

       GetData(driver.PageSource);  // 调用解析数据方法,得到数据datatable

   }

   catch (NoSuchElementException)

   {

       Console.WriteLine("找不到该元素"); ;

   }

}


    


七、登录问题


以下方法旨在演示如何登录,说白了就是用js代码模拟输入点击登录的方式实现代码自动登录网站,仅供参考


private static void Login(ChromeDriver driver)

{

   // driver.FindElement(By.Id("btn_Login")).GetAttribute("value");

   //2.执行 js 获取 value 的值

   //driver.ExecuteScript("return document.getElementsById('txt_AccountId')[0].value;");

   driver.ExecuteScript("return $('#帐号输入框ID').val('账号')"); //账号密码

   driver.ExecuteScript("return $('#密码输入框ID').val('密码')");

// 3.执行jQuery 获取 value 的值

   var account = driver.ExecuteScript("return $('#帐号输入框ID').val()");

   var pass = driver.ExecuteScript("return $('#密码输入框ID').val()");


   driver.FindElement(By.Id("登录按钮ID")).Click(); //点击登录

   Thread.Sleep(1000);  // 给点时间加载网页

}


   


八、cookie问题


以下方法旨在演示如何获取Cookie,仅供参考


private static void GetCookie()

{

   cookie = driver.Manage().Cookies;  //主要方法


   //显示初始Cookie的内容

   Console.WriteLine("--------------------");

   Console.WriteLine($"当前Cookie集合的数量:\t{cookie.AllCookies.Count}");

   for (int i = 0; i < cookie.AllCookies.Count; i++)

   {

       Console.WriteLine($"Cookie的名称:{cookie.AllCookies[i].Name}");

       Console.WriteLine($"Cookie的值:{cookie.AllCookies[i].Value}");

       Console.WriteLine($"Cookie的所在域:{cookie.AllCookies[i].Domain}");

       Console.WriteLine($"Cookie的路径:{cookie.AllCookies[i].Path}");

       Console.WriteLine($"Cookie的过期时间:{cookie.AllCookies[i].Expiry}");

       Console.WriteLine("--------------------");

   }

}


   


九、结束


driver 相当于一个网页页面,无论是加载网址还是js异步操作,都相当于在图形页面的点击加载操作,所以需要给予一点时间加载网页,driver 是一直在变的,这就是模拟浏览器进行爬取页面的方法。理论上可以直接给driver执行js代码,而Selenium 有提供部分的方法,有兴趣可以看看。