C#使用Selenium實現QQ空間數據抓取 說說抓取

上一篇講的是如何模擬真人操做登陸QQ空間,本篇主要講述一下如何抓取QQ說說數據css

繼續登陸空間後的操做git

登錄後咱們發現QQ空間的菜單實際上是固定的,只須要找到對應元素就能夠,繼續XPathgithub

能夠獲得地址數據庫

//*[@id="menuContainer"]/div/ul/li[5]/a瀏覽器

看來是第5個li元素,不過這個5看起來並非索引app

點擊說說菜單ide

 //*[@id="menuContainer"]/div/ul/li[5]/a
 var msgDom = driver.FindElementByXPath("//*[@id='menuContainer']/div/ul/li[5]/a");
 if (msgDom != null && msgDom.Displayed == true)
 {
     msgDom.Click();
 }

 

說說界面查看元素,瞧瞧,咱們又發現了什麼測試

沒錯,又是Iframe,機智的跳轉ui

//頁面跳轉,切換到說說頁面的說說列表Iframe
ITargetLocator t = driver.SwitchTo();
//這裏咱們換一種獲取元素方法,直接使用css獲取
//xpath //*[@id="app_container"]/iframe
//css #app_container > iframe
IWebElement frame = driver.FindElementByCssSelector("#app_container > iframe");
t.Frame(frame);

這就找到了列表頁,如今咱們看下一個說說都有哪些內容組成的阿里雲

頭像,這個屬於固定的,每一個人單獨一個,能夠提取出來

QQ號碼 說說內容 說說圖片 說說日期 贊數量 回覆 re回覆 可能還有位置 等其餘信息

咱們定義一個簡單的實體類,來存放這些數據,把這些數據先保存到一個集合中

 public class MessageInfo
    {
        /// <summary>
        /// 號碼
        /// </summary>
        public string QQ { get; set; }
        /// <summary>
        /// 發佈內容
        /// </summary>
        public string Content { get; set; }
        /// <summary>
        /// 發佈時間
        /// </summary>
        public DateTime? MessageTime { get; set; }
        /// <summary>
        /// 說說圖片列表
        /// </summary>
        public List<string> ImageList { get; set; }
       
    }

實體類定義了 那就開始找元素

這個說說列表是一個列表,並且還有分頁,那麼咱們須要考慮的問題就有兩個

列表讀取和翻頁操做了

先說列表,怎麼才能知道當前頁面有多少條說說呢?

有方法,咱們經過li元素的數量就能夠判斷當前頁面有多少個說說了,以下圖

固然不是一個一個找,而是經過方法

//注意這裏是複數
 var msgListDom = driver.FindElementsByXPath("//*[@id='msgList']/li");

調試能夠看到

 

QQ空間的說說一頁是20條

咱們獲取到這些元素,而且循環查找單個元素,而且保存到定義好的集合之中

 var msgListDom = driver.FindElementsByXPath("//*[@id='msgList']/li");
                //接下來就是循環取數了
                //定義說說集合
                var dataAll = new List<MessageInfo>();
                //由於查找元素須要用到索引值,因此這裏就用for循環方式
                for (var i = 1; i < msgListDom.Count + 1; i++)
                {
                    var data = new MessageInfo();
                    //接下來是查找元素
                    //第一個
                    //*[@id="msgList"]/li[1]/div[3]/div[2]/a 因此獲取元素能夠這樣寫
                    var qqDom = driver.FindElementByXPath("//*[@id='msgList']/li[" + i + "]/div[3]/div[2]/a"); //*[@id="msgList"]/li[1]/div[3]/div[2]/a.content 暱稱 data-uin QQ號碼 
                    if (qqDom != null)
                    {
                        //獲取元素屬性  data-uin對應QQ號碼
                        data.QQ = qqDom.GetAttribute("data-uin");
                    }
                    var preDom = driver.FindElementByXPath("//*[@id='msgList']/li[" + i + "]/div[3]/div[2]/pre"); //*[@id="msgList"]/li[1]/div[3]/div[2]/a.content 暱稱 data-uin QQ號碼 
                    if (preDom != null)
                    {
                        //獲取元素Text文本值
                        data.Content = preDom.Text;
                    }
                    //獲取時間值,咱們使用css方式獲取,固然也可使用xpath或者其餘方法,查找元素方法第一篇講過了
                    //這是獲取的是和時間型號同一級的元素,有的包含型號,有的不包含
                    var times = driver.FindElementsByCssSelector("#msgList > li:nth-child(" + i + ") > div.box.bgr3 > div.ft > div.info > span");
                    //一個是時間,一個是機型
                    //2015年2月10日 來自iPhone
                    if (times.Count > 1)
                    {
                        var time = driver.FindElementByCssSelector("#msgList > li:nth-child(" + i + ") > div.box.bgr3 > div.ft > div.info > span:nth-child(1) > a");
                        data.MessageTime = Convert.ToDateTime(time.GetAttribute("title"));
                        //獲取手機型號
                        var mobileDom = driver.FindElementsByCssSelector("#msgList > li:nth-child(" + i + ") > div.box.bgr3 > div.ft > div.info > span:nth-child(2) > span");
                        if (mobileDom.Count > 0)
                        {
                            var mobile = driver.FindElementByCssSelector("#msgList > li:nth-child(" + i + ") > div.box.bgr3 > div.ft > div.info > span:nth-child(2) > span");
                            data.MobileType = mobile.Text;
                        }
                    }
                    else
                    {
                        var time = driver.FindElementByCssSelector("#msgList > li:nth-child(" + i + ") > div.box.bgr3 > div.ft > div.info > span > a");
                        data.MessageTime = Convert.ToDateTime(time.GetAttribute("title"));
                    }
                    #region 圖片視頻區域
                    //若是有視頻則獲取視頻,不然獲取圖片
                    var videos = driver.FindElementsByCssSelector("#msgList > li:nth-child(" + i + ") > div.box.bgr3 > div.md > div.video");
                    if (videos.Count > 0) { }
                    else
                    {
                        var imageList = new List<string>();
                        var images = driver.FindElementsByXPath("//*[@id='msgList']/li[" + i + "]/div[3]/div[3]/div[1]/div/a");
                        if (images != null && images.Count > 0)
                        {
                            //*[@id="msgList"]/li[1]/div[3]/div[3]/div[1]/div/a[2]
                            for (int m = 0; m < images.Count; m++)
                            {
                                var imageDom = driver.FindElementByXPath("//*[@id='msgList']/li[" + i + "]/div[3]/div[3]/div[1]/div/a[" + (m + 1) + "]");
                                if (imageDom != null)
                                {
                                    imageList.Add(imageDom.GetAttribute("href"));
                                }
                            }
                        }
                        data.ImageList = imageList;
                    }
                    #endregion
                    dataAll.Add(data);
                }
View Code

裏面須要注意一些細節,元素查找的地方,有不同的地方,不是全部的說說都是規整的格式,有轉發的,轉發帶視頻的,

還有一些自定義的圖標之類的,這些均可能會影響咱們的數據抓取,不過這個能夠慢慢調整,直到程序慢慢完善

舉個例子 好比這條說說,在原來顯示時間的地方多一個span標籤,一個是時間 一個是手機型號,如圖

 上面代碼中,針對這個作了下特殊處理

調試測試如圖,咱們正確獲取到了想要的數據,固然這些只是簡單的數據,如需評論 回覆的 你們能夠自行研究一下,也是找元素

這樣咱們能獲取到當前頁的數據,接下來咱們介紹下如何翻頁

既然是模擬真人操做,那麼咱們就找到翻頁,點擊下一頁進行數據獲取

咱們找到下一頁這個元素,當頁面20條數據循環完畢以後點擊它進行翻頁操做,每一次都是頁面數據讀取完成以後點擊下一頁

咱們把讀取數據和翻頁的方法提取出來,作一個遞歸,直到下一頁沒法點擊(最後一頁的時候,點擊下一頁是無效的,就終止數據抓取了)

調試圖片

源碼,你們能夠根據我的狀況進行調試修改

 public class QzoneTest
    {
        [Fact]
        public void QQLogin()
        {
            dynamic type = (new PictureTest()).GetType();
            string currentDirectory = Path.GetDirectoryName(type.Assembly.Location);
            var driver = new ChromeDriver(currentDirectory);
            driver.Url = "https://qzone.qq.com/";
            try
            {
                //切換語法有兩種,一種是根據索引切換,另一種根據iframe名稱切換
                //這裏咱們使用name切換
                ITargetLocator tagetLocator = driver.SwitchTo();
                //tagetLocator.Frame(1);  //frame index.
                tagetLocator.Frame("login_frame");  //frame frame name.
                var switchLogin = driver.FindElementByCssSelector("#switcher_plogin");
                switchLogin.Click();
                var userName = driver.FindElementByXPath("//*[@id='u']");
                //這裏的userName就是用戶名的文本框
                //設置用戶名的值
                userName.SendKeys("123456");
                var pwd = driver.FindElementByXPath("//*[@id='p']");
                pwd.SendKeys("********");
                var btnLogin = driver.FindElementByXPath("//*[@id='login_button']");
                //這裏是判斷登陸按鈕是否可見,能夠不寫,直接調用click方法
                if (btnLogin != null && btnLogin.Displayed == true)
                {
                    btnLogin.Click();
                }
                System.Threading.Thread.Sleep(3000);
                //*[@id="menuContainer"]/div/ul/li[5]/a
                var msgDom = driver.FindElementByXPath("//*[@id='menuContainer']/div/ul/li[5]/a");
                if (msgDom != null && msgDom.Displayed == true)
                {
                    msgDom.Click();
                }
                //頁面跳轉,切換到說說頁面的說說列表Iframe
                ITargetLocator t = driver.SwitchTo();
                //這裏咱們換一種獲取元素方法,直接使用css獲取
                //xpath //*[@id="app_container"]/iframe
                //css #app_container > iframe
                IWebElement frame = driver.FindElementByCssSelector("#app_container > iframe");
                t.Frame(frame);
                //定義說說集合
                var dataAll = new List<MessageInfo>();
                //第一頁開始,pageIndex 默認0
                //定義漢字方法爲了直觀描述功能,不要在乎這些細節
                說說內容獲取(driver, dataAll, 0);
            }
            finally
            {
                driver.Quit();
            }

        }

        private static void 說說內容獲取(ChromeDriver driver, List<MessageInfo> dataAll, int pageIndex)
        {
            //翻頁以後休眠3s防止數據沒有加載完成出現找不到元素異常
            System.Threading.Thread.Sleep(3000);
            var msgListDom = driver.FindElementsByXPath("//*[@id='msgList']/li");
            //接下來就是循環取數了

            //由於查找元素須要用到索引值,因此這裏就用for循環方式
            for (var i = 1; i < msgListDom.Count + 1; i++)
            {
                var data = new MessageInfo();
                //接下來是查找元素
                //第一個
                //*[@id="msgList"]/li[1]/div[3]/div[2]/a 因此獲取元素能夠這樣寫
                var qqDom = driver.FindElementByXPath("//*[@id='msgList']/li[" + i + "]/div[3]/div[2]/a"); //*[@id="msgList"]/li[1]/div[3]/div[2]/a.content 暱稱 data-uin QQ號碼 
                if (qqDom != null)
                {
                    //獲取元素屬性  data-uin對應QQ號碼
                    data.QQ = qqDom.GetAttribute("data-uin");
                }
                var preDom = driver.FindElementByXPath("//*[@id='msgList']/li[" + i + "]/div[3]/div[2]/pre"); //*[@id="msgList"]/li[1]/div[3]/div[2]/a.content 暱稱 data-uin QQ號碼 
                if (preDom != null)
                {
                    //獲取元素Text文本值
                    data.Content = preDom.Text;
                }
                //獲取時間值,咱們使用css方式獲取,固然也可使用xpath或者其餘方法,查找元素方法第一篇講過了
                //這是獲取的是和時間型號同一級的元素,有的包含型號,有的不包含
                var times = driver.FindElementsByCssSelector("#msgList > li:nth-child(" + i + ") > div.box.bgr3 > div.ft > div.info > span");
                //一個是時間,一個是機型
                //2015年2月10日 來自iPhone
                if (times.Count > 1)
                {
                    var time = driver.FindElementByCssSelector("#msgList > li:nth-child(" + i + ") > div.box.bgr3 > div.ft > div.info > span:nth-child(1) > a");
                    data.MessageTime = Convert.ToDateTime(time.GetAttribute("title"));
                    //獲取手機型號
                    var mobileDom = driver.FindElementsByCssSelector("#msgList > li:nth-child(" + i + ") > div.box.bgr3 > div.ft > div.info > span:nth-child(2) > span");
                    if (mobileDom.Count > 0)
                    {
                        var mobile = driver.FindElementByCssSelector("#msgList > li:nth-child(" + i + ") > div.box.bgr3 > div.ft > div.info > span:nth-child(2) > span");
                        data.MobileType = mobile.Text;
                    }
                }
                else
                {
                    var time = driver.FindElementByCssSelector("#msgList > li:nth-child(" + i + ") > div.box.bgr3 > div.ft > div.info > span > a");
                    data.MessageTime = Convert.ToDateTime(time.GetAttribute("title"));
                }
                #region 圖片視頻區域
                //若是有視頻則獲取視頻,不然獲取圖片
                var videos = driver.FindElementsByCssSelector("#msgList > li:nth-child(" + i + ") > div.box.bgr3 > div.md > div.video");
                if (videos.Count > 0) { }
                else
                {
                    var imageList = new List<string>();
                    var images = driver.FindElementsByXPath("//*[@id='msgList']/li[" + i + "]/div[3]/div[3]/div[1]/div/a");
                    if (images != null && images.Count > 0)
                    {
                        //*[@id="msgList"]/li[1]/div[3]/div[3]/div[1]/div/a[2]
                        for (int m = 0; m < images.Count; m++)
                        {
                            var imageDom = driver.FindElementByXPath("//*[@id='msgList']/li[" + i + "]/div[3]/div[3]/div[1]/div/a[" + (m + 1) + "]");
                            if (imageDom != null)
                            {
                                imageList.Add(imageDom.GetAttribute("href"));
                            }
                        }
                    }
                    data.ImageList = imageList;
                }
                #endregion
                dataAll.Add(data);
            }
            //下一頁
            //數據抓取完成以後點擊下一頁
            driver.FindElementByXPath("//*[@id='pager_next_" + pageIndex + "']").Click();
            pageIndex++;
            說說內容獲取(driver, dataAll, pageIndex);
            //dataAll 數據存放於內存之中,若是有須要的能夠保存到數據庫,這裏就不詳細介紹了
        }
    }

另外元素集合獲取到了應該是能夠解析出全部li元素內容的,我這裏偷懶沒有仔細檢查元素,你們能夠看一下經過

FindElementsByXPath找到的元素集合來收集數據,這樣就不用再次經過瀏覽器驅動找了,偷了個懶

總結

 1 這種數據獲取的方法模擬真人,和諧概率較小

 2 學會了獲取說說,固然留言板,相冊什麼的 都是查找元素,相冊再多一個圖片下載保存,還能夠直接使用阿里雲oss存儲,把爬到的數據直接上傳到雲

 3 思惟發散一下 , 能夠登陸本身的QQ去獲取QQ好友空間的留言說說及相冊,作個數據分析神馬的,畢竟Qzone鏈接是固定格式,好比https://user.qzone.qq.com/123456

  把收集到的QQ號碼做爲種子保存到一個集合,看成瀏覽器驅動下一個訪問的目標,固然還有一點,模擬真人,速度確定跟爬蟲是不能比的.

都不知道本身想說什麼,溜了溜了.

 git 源碼地址:https://github.com/ermpark/CrawlingQzone

相關文章
相關標籤/搜索