美文网首页
Python 爬虫(爬取 36Kr)

Python 爬虫(爬取 36Kr)

作者: 哎呀我Qu | 来源:发表于2017-10-11 17:00 被阅读443次

    前一段接到任务,要爬 36Kr 网站首页的新闻,当时想的时应该很简单吧,跟之前爬 “不得姐” 和 “糗百” 应该差不多,可是实际操作时还是遇到了几个问题的。下面把自己的爬取方法分享出来,可能有比这更好的方法,欢迎交流。

    一、分析网页代码

    我们需要爬取的内容在网页中的位置如下,“最新文章” 下面的新闻。

    要爬取的内容.png

    打开调试模式,看看代码,发现它的外层是一个 <ul class="feed_ul"> 标签,我们需要获取的信息,在其内部的 <li> 标签中。

    feed_ul.png

    打开一个 <li> 标签,我们看到要获取的新闻标题、图片、链接位置如下。

    <li> 标签内容.png

    当时想,还是一样的套路,拿到这些标签对应的元素不就行了,图样图森破……

    二、问题及解决方法

    找到了要获取的信息位置,开始写代码。

    #!/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 后不会写了,因为里面的 li 标签没有 class 属性,这怎么弄?

    查了一下, Selenium 还有一个定位元素的方法 find_elements_by_tag_name,通过标签名来获取元素,于是写下了下面的代码。

        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()
    

    因为调试阶段我只让它遍历一次就 break 了,方便测试。img_box 可以成功获取,而这个 class 为 load-img fade 的 img 标签却获取不到,程序会报错。再看看网页代码,发现 img 外层还有一个无 class 的 div,会不会是这个原因。

    问题1.png

    然后又查了一下,发现了上篇文章中提到的 find_element_by_xpath,通过标签的位置来定位元素,可以理解为标签在网页中的路径。于是又加上了下面的代码。

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

    '//div/img' 表示在 img_box 中第一个 div 中的第一个 img 标签。

    虽然这样获取到了 img 标签,也打印出了它的 src,但是竟然是这样一个图片

    发现不对劲,但是还不知道哪里出错了😂 ,既然这样我干脆直接拿 xpath 来定位 img。

            for i in li:
                xpath = "//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
    

    可能你会惊叹,这么一长串的 xpath 怎么写的,一个一个找么?当然不是,Chrome 有一个插件,可以获取网页元素的 xpath,这个我们最后再说,先来看问题。

    这样写能够获取 img,但是打印出的 src 竟然是个 None,当时也是很迷糊。后来想可能是网页通过 Ajax 加载的列表,因为网速较慢,所以当时 img 还没有获取到 src,所以是个 None。为了确认自己没写错,我打印了一下 alt。

    print img.get_attribute('alt')
    

    可以获取新闻的标题,没错啊?感觉自己写的是假代码。然后我把 break 去掉,又运行了一次代码。

    一脸懵逼
    • 卧槽!什么情况?怎么都是一样的?又哪里出问题了?

    当时我就这样,一脸懵逼。经历了这个打击后,换了个路子,我不去拿所有的 li 标签了,拿到 feed_ul 后直接通过 xpath 定位元素。可以看到 36kr 首页一共有 28个 li 标签,其中属于新闻的只有 20 个,其他的是话题或者是空的。改写了如下代码。

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

    别说,还真好了,不再是同样的标题,只是除了第一个新闻能获取到 src,其他的都是 None。

    换了方法后的结果

    又有点小懵逼了,网速慢?不会啊,下电影还 500kb/s 多呢!然后又开始各种胡乱试,一度快要放弃,突然发现了下图的情况。

    36kr_01.gif

    只有处于当前窗口内的图片会显示,下面的图片还是灰的,滚动到窗口内才会被加载。

    • 我是个天才,这都被我发现了!

    当时就是这个想法,发现了问题还是很开心,不要喷我太水😂 ,毕竟第一次遇到这个问题。既然图片不在当前窗口内,那我就让浏览器滚一滚呗。

    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
    

    判断一下当前的 src 是否为 None,空的话就向下滚 200px(这个根据你爬取的网页自己设置),怕网速不给力,再睡 3 秒,重新获取一下。因为列表中存在专题栏目,避免向下滚动 200 正好处于专题位置还是拿不到 src 我又循环了 3 次。为什么是 3 ?事不过三😄

    • 关于 Selenium 对应浏览器的操作,以下几个也是可能会用到的
    dr.set_window_size(w, h)                    # 设置浏览器宽高
    dr.execute_script('window.scrollBy(x, y)')  # 控制浏览器滚动 x 向右 y 向下(相对于当前位置)
    dr.execute_script('window.scrollTo(x, y)')  # 控制浏览器滚动 x、y 为左上角坐标(相对于浏览器)
    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 = "//li[" + str(i) + "]/div[@class='am-cf inner_li inner_li_abtest']/a/div[@class='intro']/h3"
                    xpath_href = "//li[" + str(i) + "]/div[@class='am-cf inner_li inner_li_abtest']/a/div[@class='img_box']/div"
                    xpath_img  = "//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):
            # 这里拿到数据可以存入数据库或其他操作
            print title, '\n', url, '\n', src, '\n'
    
        def quit(self):
            self.dr.quit()
    
    while 1:
        Kr().loadData()
        time.sleep(600)
    
    演示

    欧了,因为是爬取新闻,也不涉及翻页的问题了,每隔一段时间调用一次 loadData() 即可。

    对了,关于那个获取 xpath 的插件,可以在 Chrome 的商店中搜索 XPath Helper 下载安装即可。使用也非常简单,shift + command + x 可以开启,若无反响刷新下页面或重启浏览器,按住 shift,移动鼠标到想获取 xpath 的元素上即可。

    XPath Helper

    相关文章

      网友评论

          本文标题:Python 爬虫(爬取 36Kr)

          本文链接:https://www.haomeiwen.com/subject/imkwyxtx.html