Python 爬蟲(爬取 36Kr)

前一段接到任務(wù),要爬 36Kr 網(wǎng)站首頁的新聞,當(dāng)時(shí)想的時(shí)應(yīng)該很簡(jiǎn)單吧,跟之前爬 “不得姐” 和 “糗百” 應(yīng)該差不多,可是實(shí)際操作時(shí)還是遇到了幾個(gè)問題的。下面把自己的爬取方法分享出來,可能有比這更好的方法,歡迎交流。

一、分析網(wǎng)頁代碼

我們需要爬取的內(nèi)容在網(wǎng)頁中的位置如下,“最新文章” 下面的新聞。

要爬取的內(nèi)容.png

打開調(diào)試模式,看看代碼,發(fā)現(xiàn)它的外層是一個(gè) <ul class="feed_ul"> 標(biāo)簽,我們需要獲取的信息,在其內(nèi)部的 <li> 標(biāo)簽中。

feed_ul.png

打開一個(gè) <li> 標(biāo)簽,我們看到要獲取的新聞標(biāo)題、圖片、鏈接位置如下。

<li> 標(biāo)簽內(nèi)容.png

當(dāng)時(shí)想,還是一樣的套路,拿到這些標(biāo)簽對(duì)應(yīng)的元素不就行了,圖樣圖森破……

二、問題及解決方法

找到了要獲取的信息位置,開始寫代碼。

#!/usr/bin/env python
#coding:utf-8

from selenium import webdriver

class Kr:
    def __init__(self):
        self.dr = webdriver.Chrome()
        self.dr.get('http://36kr.com/')

    def loadData(self):
        feed_ul = self.dr.find_element_by_class_name('feed_ul')
        # 寫到這卡住了
        
        self.quit()

    def quit(self):
        self.dr.quit()

Kr().loadData()

獲取到 feed_ul 后不會(huì)寫了,因?yàn)槔锩娴?li 標(biāo)簽沒有 class 屬性,這怎么弄?

查了一下, Selenium 還有一個(gè)定位元素的方法 find_elements_by_tag_name,通過標(biāo)簽名來獲取元素,于是寫下了下面的代碼。

    def loadData(self):
        feed_ul = self.dr.find_element_by_class_name('feed_ul')
        li = feed_ul.find_elements_by_tag_name('li')

        for i in li:
            img_box = i.find_element_by_class_name('img_box')
            print img_box
            # load-img fade 獲取不到
            img = img_box.find_element_by_class_name('load-img fade')
            print img
            break
        
        self.quit()

因?yàn)檎{(diào)試階段我只讓它遍歷一次就 break 了,方便測(cè)試。img_box 可以成功獲取,而這個(gè) class 為 load-img fade 的 img 標(biāo)簽卻獲取不到,程序會(huì)報(bào)錯(cuò)。再看看網(wǎng)頁代碼,發(fā)現(xiàn) img 外層還有一個(gè)無 class 的 div,會(huì)不會(huì)是這個(gè)原因。

問題1.png

然后又查了一下,發(fā)現(xiàn)了上篇文章中提到的 find_element_by_xpath,通過標(biāo)簽的位置來定位元素,可以理解為標(biāo)簽在網(wǎng)頁中的路徑。于是又加上了下面的代碼。

img = img_box.find_element_by_xpath('//div/img')
print img.get_attribute('src')

'//div/img' 表示在 img_box 中第一個(gè) div 中的第一個(gè) img 標(biāo)簽。

雖然這樣獲取到了 img 標(biāo)簽,也打印出了它的 src,但是竟然是這樣一個(gè)圖片。

發(fā)現(xiàn)不對(duì)勁,但是還不知道哪里出錯(cuò)了?? ,既然這樣我干脆直接拿 xpath 來定位 img。

        for i in li:
            xpath = "http://div[@class='am-cf inner_li inner_li_abtest']/a/div[@class='img_box']/div/img"
            img   = i.find_element_by_xpath(xpath)
            print img.get_attribute('src')
            break

可能你會(huì)驚嘆,這么一長(zhǎng)串的 xpath 怎么寫的,一個(gè)一個(gè)找么?當(dāng)然不是,Chrome 有一個(gè)插件,可以獲取網(wǎng)頁元素的 xpath,這個(gè)我們最后再說,先來看問題。

這樣寫能夠獲取 img,但是打印出的 src 竟然是個(gè) None,當(dāng)時(shí)也是很迷糊。后來想可能是網(wǎng)頁通過 Ajax 加載的列表,因?yàn)榫W(wǎng)速較慢,所以當(dāng)時(shí) img 還沒有獲取到 src,所以是個(gè) None。為了確認(rèn)自己沒寫錯(cuò),我打印了一下 alt。

print img.get_attribute('alt')

可以獲取新聞的標(biāo)題,沒錯(cuò)???感覺自己寫的是假代碼。然后我把 break 去掉,又運(yùn)行了一次代碼。

一臉懵逼
  • 臥槽!什么情況?怎么都是一樣的?又哪里出問題了?

當(dāng)時(shí)我就這樣,一臉懵逼。經(jīng)歷了這個(gè)打擊后,換了個(gè)路子,我不去拿所有的 li 標(biāo)簽了,拿到 feed_ul 后直接通過 xpath 定位元素??梢钥吹?36kr 首頁一共有 28個(gè) li 標(biāo)簽,其中屬于新聞的只有 20 個(gè),其他的是話題或者是空的。改寫了如下代碼。

def loadData(self):
    feed_ul = self.dr.find_element_by_class_name('feed_ul')
    
    i = 1
    while  i <= 28:
        try:
            xpath_head = "http://li[" + str(i) + "]/div[@class='am-cf inner_li inner_li_abtest']/a/div[@class='intro']/h3"
            xpath_href = "http://li[" + str(i) + "]/div[@class='am-cf inner_li inner_li_abtest']/a/div[@class='img_box']/div"
            xpath_img  = "http://li[" + str(i) + "]/div[@class='am-cf inner_li inner_li_abtest']/a/div[@class='img_box']/div/img"

            head  = feed_ul.find_element_by_xpath(xpath_head)
            title = head.text

            href  = feed_ul.find_element_by_xpath(xpath_href)
            url   = 'http://36kr.com' + href.get_attribute('href')

            img   = feed_ul.find_element_by_xpath(xpath_img)
            src   = img.get_attribute('src')

        except Exception as e:
            print 'error'

        else:
            print title
            print url
            print src

        i += 1

    self.quit()

別說,還真好了,不再是同樣的標(biāo)題,只是除了第一個(gè)新聞能獲取到 src,其他的都是 None。

換了方法后的結(jié)果

又有點(diǎn)小懵逼了,網(wǎng)速慢?不會(huì)啊,下電影還 500kb/s 多呢!然后又開始各種胡亂試,一度快要放棄,突然發(fā)現(xiàn)了下圖的情況。

36kr_01.gif

只有處于當(dāng)前窗口內(nèi)的圖片會(huì)顯示,下面的圖片還是灰的,滾動(dòng)到窗口內(nèi)才會(huì)被加載。

  • 我是個(gè)天才,這都被我發(fā)現(xiàn)了!

當(dāng)時(shí)就是這個(gè)想法,發(fā)現(xiàn)了問題還是很開心,不要噴我太水?? ,畢竟第一次遇到這個(gè)問題。既然圖片不在當(dāng)前窗口內(nèi),那我就讓瀏覽器滾一滾唄。

img   = feed_ul.find_element_by_xpath(xpath_img)
src   = img.get_attribute('src')

t = 1
while t <= 3:
    if src == None:
        self.dr.execute_script('window.scrollBy(0,200)')
        time.sleep(3)
        src = img.get_attribute('src')
        t += 1
    else:
        break

判斷一下當(dāng)前的 src 是否為 None,空的話就向下滾 200px(這個(gè)根據(jù)你爬取的網(wǎng)頁自己設(shè)置),怕網(wǎng)速不給力,再睡 3 秒,重新獲取一下。因?yàn)榱斜碇写嬖趯n}欄目,避免向下滾動(dòng) 200 正好處于專題位置還是拿不到 src 我又循環(huán)了 3 次。為什么是 3 ?事不過三??

  • 關(guān)于 Selenium 對(duì)應(yīng)瀏覽器的操作,以下幾個(gè)也是可能會(huì)用到的
dr.set_window_size(w, h)                    # 設(shè)置瀏覽器寬高
dr.execute_script('window.scrollBy(x, y)')  # 控制瀏覽器滾動(dòng) x 向右 y 向下(相對(duì)于當(dāng)前位置)
dr.execute_script('window.scrollTo(x, y)')  # 控制瀏覽器滾動(dòng) x、y 為左上角坐標(biāo)(相對(duì)于瀏覽器)
dr.refresh()                                # 刷新頁面

三、完整代碼與演示

#!/usr/bin/env python
#coding:utf-8

from selenium import webdriver
import time

class Kr:
    def __init__(self):
        self.dr = webdriver.Chrome()
        self.dr.get('http://36kr.com/')

    def loadData(self):
        feed_ul = self.dr.find_element_by_class_name('feed_ul')
        
        i = 1
        while  i <= 28:
            try:
                xpath_head = "http://li[" + str(i) + "]/div[@class='am-cf inner_li inner_li_abtest']/a/div[@class='intro']/h3"
                xpath_href = "http://li[" + str(i) + "]/div[@class='am-cf inner_li inner_li_abtest']/a/div[@class='img_box']/div"
                xpath_img  = "http://li[" + str(i) + "]/div[@class='am-cf inner_li inner_li_abtest']/a/div[@class='img_box']/div/img"

                head  = feed_ul.find_element_by_xpath(xpath_head)
                title = head.text

                href  = feed_ul.find_element_by_xpath(xpath_href)
                url   = 'http://36kr.com' + href.get_attribute('href')

                img   = feed_ul.find_element_by_xpath(xpath_img)
                src   = img.get_attribute('src')

                t = 1
                while t <= 3:
                    if src == None:
                        self.dr.execute_script('window.scrollBy(0,200)')
                        time.sleep(3)
                        src = img.get_attribute('src')
                        t += 1
                    else:
                        break

            except Exception as e:
                print 'error\n'

            else:
                self.saveData(title, url, src)

            i += 1

        self.quit()

    def saveData(self, title, url, src):
        # 這里拿到數(shù)據(jù)可以存入數(shù)據(jù)庫或其他操作
        print title, '\n', url, '\n', src, '\n'

    def quit(self):
        self.dr.quit()

while 1:
    Kr().loadData()
    time.sleep(600)
演示

歐了,因?yàn)槭桥廊⌒侣劊膊簧婕胺摰膯栴}了,每隔一段時(shí)間調(diào)用一次 loadData() 即可。

對(duì)了,關(guān)于那個(gè)獲取 xpath 的插件,可以在 Chrome 的商店中搜索 XPath Helper 下載安裝即可。使用也非常簡(jiǎn)單,shift + command + x 可以開啟,若無反響刷新下頁面或重啟瀏覽器,按住 shift,移動(dòng)鼠標(biāo)到想獲取 xpath 的元素上即可。

XPath Helper
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容