Python 爬虫(爬取 36Kr)

前一段接到任务,要爬 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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容