标签: 采集

  • .NET中如何使用PhantomJS与Selenium模拟采集数据

    在现代 web 开发中,数据采集是一个非常常见的需求。无论是从网站中提取信息,还是进行自动化测试,模拟用户行为都变得尤为重要。在 .NET 环境中,常见的两种模拟采集的工具是 PhantomJSSelenium。这两者各具特点,适用于不同的场景。本文将介绍这两款工具及其如何在 .NET 中使用。

    1. PhantomJS——无头浏览器的利器

    什么是 PhantomJS?

    PhantomJS 是一个无头浏览器(headless browser),意味着它没有图形界面,完全通过命令行操作。它可以模拟浏览器行为,执行网页中的 JavaScript 代码,获取渲染后的网页内容,并支持截图、页面交互等操作。由于它不需要图形界面,运行效率比普通浏览器更高,尤其适用于大规模的网页抓取和自动化测试。

    PhantomJS 在数据采集中的应用

    PhantomJS 非常适合用来进行 网页抓取,特别是当页面内容通过 JavaScript 动态生成时。很多现代网站的数据展示依赖于 JavaScript 渲染,传统的 HTML 解析工具(如 HtmlAgilityPack)在这种情况下无法直接提取数据,而 PhantomJS 可以通过模拟浏览器,渲染页面并抓取其中的数据。

    在 .NET 中使用 PhantomJS

    虽然 PhantomJS 自身是用 JavaScript 编写的,但我们可以通过使用外部库来在 .NET 中调用 PhantomJS。一个常用的库是 PhantomJS.Net,它提供了 .NET 与 PhantomJS 的接口,使得在 .NET 中模拟浏览器变得更加方便。

    使用 PhantomJS.NET 的示例代码:

    // language: csharp
    using System;
    using PhantomJS.Net;
    
    class Program
    {
        static void Main()
        {
            var driver = new PhantomJSDriver();
            driver.Navigate().GoToUrl("https://example.com");
    
            // 获取渲染后的网页内容
            var pageSource = driver.PageSource;
            Console.WriteLine(pageSource);
    
            driver.Quit();
        }
    }

    在这个示例中,我们首先初始化了一个 PhantomJS 驱动器,加载了目标网页,然后获取了渲染后的 HTML 内容。

    2. Selenium——强大的自动化测试工具

    什么是 Selenium?

    Selenium 是一款广泛使用的自动化测试工具,支持多种编程语言(包括 C#、Java、Python 等)。它不仅可以用来模拟浏览器行为,还支持模拟用户操作,如点击、输入、滚动等。Selenium 支持所有现代浏览器,包括 Chrome、Firefox 和 Safari 等,除了无头浏览器模式(headless mode)外,它还支持在有图形界面的环境下操作浏览器。

    Selenium 在数据采集中的应用

    Selenium 可以模拟真实的用户行为,因此非常适合 交互式数据采集。例如,你需要登录网站、填写表单、点击按钮来获取数据时,Selenium 是一个理想的选择。而且它支持 JavaScript 执行,因此可以抓取动态加载的网页内容。

    在 .NET 中使用 Selenium

    在 .NET 环境中使用 Selenium 需要安装相应的 NuGet 包。常见的库有 Selenium.WebDriverSelenium.WebDriver.ChromeDriver,这些包可以通过 NuGet 安装。

    使用 Selenium 的示例代码:

    // language: csharp
    using OpenQA.Selenium;
    using OpenQA.Selenium.Chrome;
    using System;
    
    class Program
    {
        static void Main()
        {
            // 设置 Chrome 驱动器
            var options = new ChromeOptions();
            options.AddArgument("headless"); // 无头模式
    
            var driver = new ChromeDriver(options);
            driver.Navigate().GoToUrl("https://example.com");
    
            // 获取渲染后的网页内容
            var pageSource = driver.PageSource;
            Console.WriteLine(pageSource);
    
            driver.Quit();
        }
    }
    

    这个示例展示了如何通过 Selenium 在无头模式下启动 Chrome 浏览器,加载目标网页,并获取渲染后的 HTML 内容。

    3. PhantomJS 与 Selenium 的对比

    虽然 PhantomJS 和 Selenium 都可以用来模拟采集,但它们有一些显著的区别:

    特性PhantomJSSelenium
    浏览器类型无头浏览器 (Headless Browser)可以支持有头和无头浏览器
    性能高效,启动速度快,适合大规模抓取启动速度稍慢,但更强大、灵活
    JavaScript 支持完全支持 JavaScript完全支持 JavaScript
    支持的浏览器仅 PhantomJS支持所有主要浏览器(Chrome, Firefox, Safari)
    适用场景爬虫和抓取静态页面自动化测试和交互式爬虫

    从对比表格中可以看出,PhantomJS 更适合于快速的网页抓取,尤其是当你只需要获取页面内容而不需要模拟用户交互时。而 Selenium 更适合需要复杂交互的场景,如登录、填写表单、点击按钮等。

    4. 总结

    PhantomJS 和 Selenium 都是强大的工具,能够帮助开发者在 .NET 中模拟浏览器行为进行数据采集。选择哪个工具取决于你的需求:

    • 如果你需要快速抓取页面并且不需要复杂的用户交互,PhantomJS 是一个不错的选择。
    • 如果你需要模拟更复杂的用户行为(如填写表单、点击按钮等),Selenium 会更为适合。

    无论你选择哪个工具,正确使用它们将能帮助你高效地进行数据采集工作。

  • C#使用phantomjs采集页面HTML

    C#使用phantomjs采集页面HTML。

    code.js

    phantom.outputEncoding="gb2312"
    system = require('system')   
    address = system.args[1];//获得命令行第二个参数 接下来会用到   
    //console.log('Loading a web page');   
    var page = require('webpage').create();   
    var url = address;   
    //console.log(url);   
    page.open(url, function (status) {   
        //Page is loaded!   
        if (status !== 'success') {   
            console.log('Unable to post!');   
        } else {   
            //console.log(page.content);   
            //var title = page.evaluate(function() {   
            //  return document.title;//示范下如何使用页面的jsapi去操作页面的  
            //  });   
            //console.log(title);   
            //console.log(encodeURIComponent(page.content));   
            console.log(page.content);   
        }     
        phantom.exit();   
    });

    C#代码

    public string getAjaxCotnent(String url)
    {
        var b = System.Reflection.Assembly.GetEntryAssembly().Location;
        var path = Path.GetDirectoryName(b); // 末尾不带斜杠
    
        ProcessStartInfo start = new ProcessStartInfo(path + "//phantomjs.exe");
    
        start.Arguments = "code.js" + " " + url;//设置命令参数
        StringBuilder sb = new StringBuilder();
        start.CreateNoWindow = false;//不显示dos命令行窗口
        start.RedirectStandardOutput = true;//
        start.RedirectStandardInput = true;//
        start.UseShellExecute = false;//是否指定操作系统外壳进程启动程序
        Process p = Process.Start(start);
    
       // string encoding = p.StandardOutput.CurrentEncoding.ToString();
        StreamReader reader = p.StandardOutput;//截取输出流
        string line = reader.ReadLine();//每次读取一行
        sb.AppendLine(line);
        while (!reader.EndOfStream)
        {
            line = reader.ReadLine();
            sb.AppendLine(line);
        }
        p.WaitForExit();//等待程序执行完退出进程
        p.Close();//关闭进程
        reader.Close();//关闭流
        string strRet = System.Web.HttpUtility.UrlDecode(sb.ToString());
        return strRet;
    }

    调用

    var url = "http://po.baidu.com/feed/share?context=%7B%22nid%22%3A%22news_3493960622142783493%22%2C%22sourceFrom%22%3A%22bjh%22%7D";
    var detailHtml = getAjaxCotnent(url);
  • 模拟网页登录的一种全能思路

    原理就是从webbrowser拿到Cookie

    一般做网页登录,比如登录QQ空间,则要破解登录的相关的一系列加密算法。

    在给客户维护以前登录QQ空间采集好友访客的一个软件时,发现2015年的空间密码加密方式变了,由以前的MD5改为了RSA,找到了加密的JS文件,但懒的转为C#代码。本来想用Interop.MSScriptControl.dll在C#中调用JS,但是直接调用这个JS文件会报错。实在不想继续找下去,一直在想着有什么简单方便的方法。终于功夫不负有心人,还真让我找到了。

    原来的方式是,是从UI界面上获取用户要登录的用户名和密码,C#代码经过系统的加密,转换为TX需要的格式,并发包的服务器,服务器反回一些cookie和一些有用的值(比如g_tk),然后后续的一些操作要带上这些cookie和g_tk。现在既然这些过程解不了密,那么是否可以用Webbrowser控件来登录,登录完成后从webbrowser中拿到cookie,后续的操作带上这些cooke即可。

    经过动手尝试,证明是可行的。对于QQ空间的登录,过程是这样的:

    webbrowser中进行登录,登录完成后,在恰当的时机,拿到webbrowser的cookie,然后在cookieContainer中找到skey

    至此,所有cookie和skey都拿到了。然后还有一个重要的参数g_tk, 这个参数是根据skey计算出来的。算法如下:

    /// <summary>
    /// 获取空间登录的g_tk
    /// </summary>
    /// <param name="skey"></param>
    /// <returns></returns>
    public long g_tk(string skey)
    {
            long hash = 5381;
            for ( int i = 0; i < skey.Length; i++)
           {
                  hash += (hash << 5) + skey[i];
           }
            return hash & 0x7fffffff;
    }

    试了下,能用,还好,g_tk的算法没有改动。

    这样,项目维护完成,600块钱到手。

    下面附上webbrowser登录过程以及拿到cookie和相cookie中取skey的所有代码:

    public partial class QZoneWebLogin : Form
    {
        private bool flag = false;
        private string _skey = string.Empty;
        private CookieContainer _cookie = new CookieContainer();
    
        public string skey
        {
            get { return _skey; }
        }
    
        public CookieContainer cookie
        {
            get { return _cookie; }
        }
    
        public QZoneWebLogin()
        {
            InitializeComponent();
        }
    
        private void webBrowser1_DocumentCompleted( object sender, WebBrowserDocumentCompletedEventArgs e)
        {
            if (webBrowser1.Url.ToString().Contains( "http://user.qzone.qq.com/") && webBrowser1.Url.ToString().Contains("?ptsig=" ))
            {
                if (!flag)
                {
                    flag = true;
                    _skey = GetSkey();
                    this.DialogResult = System.Windows.Forms. DialogResult.OK;
                }
    
            }
    
        }
    
    
        private string GetSkey()
        {
            string skey = string.Empty;
    
            GetCookie();
            List< Cookie> listCookie = GetAllCookies(_cookie);
            foreach ( Cookie c in listCookie)
            {
                if (c.Name == "skey")
                {
                    skey = c.Value;
                    break;
                }
            }
            return skey;
        }
    
        private void GetCookie()
        {
            if (webBrowser1.Document.Cookie != null)
            {
                string cookieStr = webBrowser1.Document.Cookie;
                string[] cookstr = cookieStr.Split( ';');
                foreach ( string str in cookstr)
                {
                    string[] cookieNameValue = str.Split( '=');
                    Cookie ck = new Cookie(cookieNameValue[0].Trim().ToString(), cookieNameValue[1].Trim().ToString());
                    ck.Domain = ".qq.com";
                    _cookie.Add(ck);
                }
            }
        }
    
    
        /// <summary>
        /// 把CookieContainer所有的Cookie读出来
        /// </summary>
        /// <param name="cc"></param>
        /// <returns></returns>
        private List<Cookie> GetAllCookies( CookieContainer cc)
        {
            List< Cookie> lstCookies = new List< Cookie>();
            Hashtable table = ( Hashtable)cc.GetType().InvokeMember( "m_domainTable",
                System.Reflection. BindingFlags.NonPublic | System.Reflection.BindingFlags .GetField |
                System.Reflection. BindingFlags.Instance, null , cc, new object [] { });
    
            foreach ( object pathList in table.Values)
            {
                SortedList lstCookieCol = (SortedList)pathList.GetType().InvokeMember("m_list" ,
                    System.Reflection. BindingFlags.NonPublic | System.Reflection.BindingFlags .GetField
                    | System.Reflection. BindingFlags.Instance, null , pathList, new object[] { });
                foreach ( CookieCollection colCookies in lstCookieCol.Values)
                    foreach ( Cookie c in colCookies)
                        lstCookies.Add(c);
            }
    
            return lstCookies;
        }
    }
  • .NET采集用到的包

    ScrapySharp + HtmlAgilityPack

    这两个库基本可以完成98%的采集需求,剩下的2%可以再加一个库: phantomjs

    目前接到的需求中,还没有使用到 phantomjs这个库。

    补充(2015-3-9):

    今天又发现了几个和采集网页相关的库(以下4个都没用过,先记下来)

    1. CsQuery。CsQuery可以算是.net中实现的Jquery, 可以使用类似Jquery中的方法来处理html页面。CsQuery的项目地址是https://github.com/afeiship/CsQuery
    2. AngleSharp (据说有内存泄漏,不知道现在还有没有)
    3. fizzler   fizzler是HtmlAgilityPack的一个扩展,支持jQuery Selector
    4. NSoup   NSoup是JSoup的Net移植版本。

    ScrapySharp + HtmlAgilityPack 基本用法举例:

    string htmlstr = HttpHelper.HttpPost(“http://www.tzgsj.gov.cn/baweb/show/shiju/queryByName.jsp” , “spellcondition=%E9%BE%99” );

    HtmlDocument doc = new HtmlDocument ();

    doc.LoadHtml(htmlstr);

    HtmlNode docNode = doc.DocumentNode;

    var nodes = docNode.CssSelect(“#content” );

    这样就拿到了所有ID为content的DOM元素。

    这段HTML如下:

    为了进一步拿到里面的td的文字,可以接着这样写:

    foreach(var node in nodes)
    {
        var tdNodes = node.CssSelect( "td");
        foreach(var td in tdNodes)
        {
            string text = td.InnerText;
        }
    }

    经过这个双重循环后,所有的td里的内容就都被采集出来了。

    总结,ScrapySharp 主要是这个CssSelect好用。它是HtmlAgilityPack的一个扩展