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<IWebElement> elements = driver.FindElementsByClassName("des_cont");             foreach (var e in elements)             {                 //每个字母对应的城市集合                 ReadOnlyCollection<IWebElement> 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<List<City>> citylist = new List<List<City>>();             for (int i = 0; i < p; i++)             {                 List<City> grouplist = new List<City>();                 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<string> StartDetailTask(List<City> 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<IWebElement> 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<IWebElement> 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<bool>((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("点评载入中");             });