Selenium+PhantomJS抓取数据

Selenium在前面的一篇文章中说过是一种浏览器自动化测试的工具,可以利用浏览器的驱动去控制浏览器访问网站,从

而模拟浏览行为抓取数据,这种方式可以抓到更多的数据,但是效率不是很高,而且浏览器的页面必须一直开着,比较

吃资源。最近看到了一个无页面的浏览器PhantomJS,访问网站效率高,速度快,无页面全后台抓取数据,而且可以和

Selenium结合使用个性化定制网站的数据抓取,下面会详细讲一下Selenium与PhantomJS在vs2013中是如何抓取数据

的,以携程网的酒店数据为例。


首先下载Selenium的dll文件和PhantomJS资源,在我的资源中都已经上传了地址在这里~

http://download.csdn.net/detail/u013407099/9687589

然后引用Selenium中的4个dll文件,将PhantomJS中bin目录下的exe文件放到工程目录下就好了

第一步我们先初始化PhantomJS类型的Selenium中的driver来控制浏览器

var driver = new OpenQA.Selenium.PhantomJS.PhantomJSDriver("../../Phantomjs");

第二步就让这个drivier去访问我们想要访问的地址

 driver.Navigate().GoToUrl("http://hotels.ctrip.com/citylist");

第三步先在浏览器中访问这个网址,观察网页的DOM结构的规律,去将所有的城市的酒店列表地址所在的元素获取到,也就是使用css选择器来筛选DOM结构

  //锁定留个城市名模块
            ReadOnlyCollection elements = driver.FindElementsByClassName("des_cont");
            foreach (var e in elements)
            {
                //每个字母对应的城市集合
                ReadOnlyCollection hreflist = e.FindElements(By.TagName("a"));
                foreach (var h in hreflist)
                {
                    string cityname = h.GetAttribute("innerHTML");
                    string hotellisthref = h.GetAttribute("href");
                    Console.WriteLine(cityname + hotellisthref);
                    City city = new City(cityname, hotellisthref);
                    if (!list.Contains(city))
                    {
                        list.Add(city);
                    }


                }

            }

因为携程网的城市按字母排序的,而且切换字母时的数据就是在一个页面中,所以可以一次性把所有的城市对应的酒店介绍地址获取到,下面就可以去分别访问每个城市的酒店列表,获取每个酒店更加详细的信息 ,这里因为单线程比较慢,所以开了多线程去跑,跑多线程的时候原来想把每个城市建一个文本文件记录的,但是多线程的执行方式会是的有很多重复数据写入(坑了自己好久),所以就将数据分组,然后一组一个文本文件就好了

分组代码:

  int p = 10;
            //商
            int value = list.Count / 10;
            //余数
            int remainder = list.Count % 10;
            List> citylist = new List>();
            for (int i = 0; i < p; i++)
            {
                List grouplist = new List();
                if (i < p - 1)
                {
                    for (int j = i * value; j < value * (i + 1); j++)
                    {
                        grouplist.Add(list[j]);
                    }
                }
                else
                {
                    for (int j = i * value; j < list.Count; j++)
                    {
                        grouplist.Add(list[j]);
                    }
                }
                string filename = "../../Data/File/Cash" + DateTime.Now.ToString("yyyyMMddHHmmss") + "_" + i + ".txt";
                File.Create(filename);
                cachelist.Add(filename);
                citylist.Add(grouplist);


            }

获取每个酒店的详细页面地址

  public async Task StartDetailTask(List list, string CacheFileName)
        {


            return await Task.Run(() =>
             {
                 var driver = new OpenQA.Selenium.PhantomJS.PhantomJSDriver("../../Phantomjs");
                 var result = string.Empty;
                 try
                 {
                     foreach (var city in list)
                     {
                         driver.Navigate().GoToUrl(city.Url.ToString());
                         ReadOnlyCollection elements = driver.FindElementsByClassName("searchresult_name");
                         foreach (var e in elements)
                         {
                             IWebElement w = e.FindElement(By.TagName("a"));
                             string hotelname = w.GetAttribute("title");
                             string allhref = w.GetAttribute("href");
                             //string hotelhref = allhref.Substring(0, allhref.IndexOf('?') - 1);
                             result += hotelname + "|" + allhref + "\r\n";
                             Console.WriteLine(hotelname + allhref);
                             StreamWriter sw = new StreamWriter(CacheFileName);
                             try
                             {
                                 sw.Write(result);
                             }
                             catch (Exception)
                             {


                                 throw;
                             }
                             finally
                             {
                                 sw.Flush();
                                 sw.Close();
                             }




                         }
                     }




                 }
                 catch (Exception e)
                 {


                     Console.WriteLine(e.StackTrace); ;
                 }
                 finally
                 {
                     driver.Close();
                     driver.Quit();
                 }


                 return result;
             });

在访问 的过程中可以设置PhantomJS的一些属性,比如HideCommandPromptWindow属性可以控制是否弹出PhantomJS的命令框,LoadImages可以控制是否加载页面图片等

最后一步就是获取每个酒店的详细评论了,在获取房间评论的过程中因为网站需要滑动才会动态加载完毕,从而选择切换到评论,所以需要人为的控制窗口滑动

  var driver = new PhantomJSDriver(driverService);
            //var driver = new ChromeDriver(@"C:\Program Files (x86)\Google\Chrome\Application");
            driver.Navigate().GoToUrl("http://hotels.ctrip.com/hotel/434938.html");
            //滚动到底部
            Actions action = new Actions(driver);
            for (int i = 0; i < 4; i++)
            {
                action.MoveToElement(driver.FindElementByClassName("gns")).Perform();
            }

其中“gns”是网站的底部一个元素的class,来定位网站的底部在哪里,然后控制div的店家来切换到评论窗口

   //切换到评论
            driver.FindElementById("commentTab").Click();

最后来抓取详细评论

  //评论集合
            ReadOnlyCollection commentlist = driver.FindElementsByCssSelector("div[class^='comment_block']");
            foreach (var comment in commentlist)
            {
                Console.WriteLine("用户账号:" + comment.FindElement(By.ClassName("name")).FindElement(By.TagName("span")).GetAttribute("innerHTML"));
                Console.WriteLine("用户评分:" + comment.FindElement(By.ClassName("score")).FindElement(By.ClassName("n")).GetAttribute("innerHTML"));
                Console.WriteLine("入住时间:" + comment.FindElement(By.ClassName("date")).GetAttribute("innerHTML"));
                Console.WriteLine("房间类型:" + comment.FindElement(By.CssSelector("a[class^='room J_baseroom_link']")).GetAttribute("innerHTML"));
                Console.WriteLine("详细评论" + comment.FindElement(By.ClassName("J_commentDetail")).GetAttribute("innerHTML"));
                Console.WriteLine();
            }


在这个过程中有一个问题没有解决,就是只能抓取5条评论,即使设置了等待时间或者等待条件也没有用,而等待条件的设置与chromedriver配合确可以完美解决,如果大家有什么好的解决方法可以提给我哦,等待条件的设置给大家看一下

  //等待加载完毕
            WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(5));
            wait.Until((d) =>
            {
                return d.FindElement(By.XPath("//*[@id='commentList']")).Displayed && d.FindElement(By.XPath("//*[@id='hotel_info_comment']/div[@id='commentList']")).Displayed
                    && !d.FindElement(By.XPath("//*[@id='hotel_info_comment']/div[@id='commentList']")).Text.Contains("点评载入中");
            });

你可能感兴趣的:(c#)