美文网首页数据科学家
No.2 从0写一个爬虫,爬取500w好友关系数据

No.2 从0写一个爬虫,爬取500w好友关系数据

作者: 2453cf172ab4 | 来源:发表于2017-09-05 10:40 被阅读196次

    0x00 前言

    上一篇文章已经写了一部分数据获取和爬虫的内容,这篇文章我们一起来实现一个网络爬虫,用这个小爬虫来爬取500w的简书的粉丝关系对。

    1. 两个小问题

    为什么要爬关系对数据呢,爬些文字数据岂不更好?

    为什么要爬关系对数据?因为居士最近正在搞和社交关系相关的项目,需要对大量的关系数据做处理,而且要用到 LPA 、 PageRank 这些算法,写博客本来就需要为自己学习和工作来服务,为了加深自己的理解,因此前几篇博客都和关系数据相关。后续当需要文本数据的时候,会有专门的文章。

    为什么要爬简书数据呢?

    为什么爬简书的数据?这就和我们爬虫的实现有关了,比较成熟的网站都有很强的防爬虫措施,比如xx百科,很容易被封IP。但是像简书和CxxN这些网站的防爬虫相对做的比较弱,比较容易下手。

    2. 通过本文能收获什么?

    1. 你将能了解到如何针对特定网站也设计一个爬虫,设计思路很重要。我们会主要来讲怎么设计,实现的代码很简单,大致过一下,不一行行讲了,都有详细注释。
    2. 你会了解到爬虫的几个工程要点和一种简单的实现方法:图的BFS,页面的解析和已爬取URL列表的维护。
    3. 完整能运行的代码,有需要的话爬好的数据也可以拿走。

    0x01 设计一个小爬虫

    1. 明确目标

    先明确一下目标:我们要爬取简书中的的关注数据,比如A关注B,那我们就存下来B A这样一条数据,即人+粉丝,以Tab分割。

    2. 总结规则

    下图,是我们想要爬取数据的主要页面,我们希望从这个页面得到这样一份数据:

    dantezhao   小飞_b7ef
    dantezhao   Daisy向阳花儿
    dantezhao   Maxine24w
    dantezhao   Jialin28
    

    接着,我们需要看一下它返回给浏览器的 HTML 的source code什么样子,然后从中来解析出来这个关系对。在Chrome中直接在当前网页下按 ctrl+u 即可。

    从下图中,我们可以看到,只要拿到红框圈住的部分就可以了。仔细看一下,红框圈住的部分包含两块内容:<a class="name" href="/u/9e2b3667983d">用户名。 这两块内容我们都需要,特别是前面的那串奇怪的字符,属于上一篇文章《No.1 聊一聊数据获取和爬虫》中提到的URL解析。能把这个解析出来,我们就能不停地获取新的网页链接,爬取内容。

    3. 设计遍历算法

    这里我们会用到图的广度有限遍历,我们会有一个十分简单的实现版本,简单,但是够用。

    这个算法是这样的:

    1. 初始化一个 Queue ,并放入一个作为入口的 URL,(用来存放将要被爬取的 URL )和一个 Set(存放已经爬取过的 URL , Set 是去重过的,因此我们就不用再去重了);
    2. 从Queue的队首访问一个 URL ,解析出该网页中需要的正文(前面提到的用户名)的将要被爬取链接(前面提到的<a class="name" href="/u/9e2b3667983d">,这串字符再拼上www.jianshu.com/即代表一个新的URL);
    3. 将该 URL 从 Queue 的队首移除并放入 Set 中,并将在该 URL 中解析出来的新的 URL 放入 Queue 的队尾。
    4. 跳转到第二步继续执行。

    好了,遍历大致就是这样实现了,会额外用到一个队列和一个集合。

    4. 程序整体框架

    然后我们看一下程序的整体框架(在医院等人的时候画的,本居士也是够拼了)。

    这里不再细致讲框架的内容了,仔细看一下图中的元素即可。

    5. 其它

    我们的爬虫的算法和程序大致框架都已经有了,整体骨骼已有,再需要补充几点就可以来实现了。

    关系对的表示

    我们在存放关系对的时候使用简书生成的一个唯一的userid,而不是用户名。

    因为通过这个字符串能唯一标识一个用户,而且这个字符串还能拼接成新的URL,也就是说解析出来了这个9e2b3667983d,既能当作关系,也可以当作新的URL一部分。

    URL解析

    通过HTML的标签来解析出下面图中的1,再拼上http://www.jianshu.com/users/,最后得到一个新的新的URLhttp://www.jianshu.com/users/2453cf172ab4

    粉丝和关注

    粉丝和关注分别在两个页面,他们的网页的前半部分是相同的:http://www.jianshu.com/users/2453cf172ab4,不同的地方在于一个是followers一个是following

    另外这里还设计到简书的分页问题,根据观察,我们可以发现是每页有9个用户,每一页需要加一个?page=N。也就是说一个完整的网页应该是这个样子:http://www.jianshu.com/users/2453cf172ab4/following?page=2

    数据持久化

    最终的数据都会存在本地磁盘,这里不再用数据库,比较重。

    0x02 实现

    实现这一块本来是想详细的写的,不过大部分的精力都在设计上了,实现这一块就少写一点,不大段贴代码了。

    整个程序的框架在前面已有提到,可以往上翻看一下图,下面主要讲一下各个模块的实现。

    1. 用到的 Python 包

    • Python版本:3.5
    • BeautifulSoup:解析HTML标签
    • urllib:获取URL的内容

    2. BFS的实现

    看代码,大致的思路其实很简单。

    3. 页面的获取

    最简单的页面获取程序非常短,三行就可以搞定。下面这三行代码,拿去运行一下,就可以把一个网页的正文全部抓下来。

    import urllib.request
    data = urllib.request.urlopen("http://blog.csdn.net/zhaodedong").read().decode('UTF-8')
    print (data)
    

    我们在此基础上还要再做页面的解析,不然存下来也没用,这里面就用到了BeautifulSoup,专门来解析HTML,这里不再详细说明,感兴趣的同学,搜一下官网,很容易。

    # 根据url获取一页的所有用户
    def get_user_set(url):
        data = urllib.request.urlopen(url).read()
        data = data.decode('UTF-8')
        soup = BeautifulSoup(data, 'html.parser')
        links = soup.find_all("a", class_="name")
        user_set=set()
        for link in links:
            user = link.get("href").split("/")[2].strip()
            if (len(user) > 1):
                user_set.add(user)
        return user_set
    

    4. 文件持久化

    为了避免频繁在磁盘读写文件,该程序是每抓取五个用户的所有关系对后写一次文件,一个用户平均有500个关系对,也就是说,平均2500个关系对写一次文件。写文件的逻辑很简单,可以看一下代码。

    另外我们的已访问URL列表也是存在本地文件中,方便多次运行程序重复爬网页。

    5. 防爬虫

    我们写的爬虫相对来讲算是很暴力的,一般都会被各个网站屏蔽,比如:ip限制、Http请求的Header限制。

    这里我们为了防止简书干掉我们的小爬虫,需要加两个操作:

    1. 每次请求页面,随机获取一个Header。
    2. 每次请求页面,随机休息1~3秒。

    经过这两步优化,基本上可以运行很长时间了,如果还是被封掉,就可以考虑使用代理和分布式爬虫了。因为爬简书封IP,而且自己不是特别继续这个数据,就用单进程来写了,跑几天就能爬下500w的数据。

    0x03 零基础学爬虫的建议

    目前这个小爬虫已经运行了三四天了,总共爬取了500w左右的关系对。最后的关系对数据长下面这个样子。

    看文章其实只能帮助理解,爬虫这东西,还是要跑一下代码,运行两次就能搞定了。

    这里推荐一下零基础的同学学习Python爬虫的顺序。

    1. 先尝试获取一个网页

    先用下面代码就可以获取一个网页,打印出来看一下

    import urllib.request
    data = urllib.request.urlopen("http://blog.csdn.net/zhaodedong").read().decode('UTF-8')
    print (data)
    

    2. 获取网页中自己关心的内容

    获取到了HMTL的全部内容后,就要来对它进行解析,建议尝试一下BeautifulSoup,运行一下官方的demo,上手绝对快。

    3. 接着让爬虫不停歇地跑下去

    然后就是爬虫的广度优先遍历了,本文和上一篇文章都有提到,而且也有实现,可以参照一下。 或者是直接运行现有的例子就可以。

    4. 持续改进

    只要爬虫能跑起来之后就好办了,遇到问题了就解决。比如说遇到防爬虫了,我们就想办法来骗它们;爬得慢了我们就改成多线程的,还不行就改成分布式的。遇到问题了解决就行。

    0xFF 总结

    文章是在医院等人的时候写的,思路被打断好多次,因此有一点不是特别流畅的地方,不过我想大致的内容还是表达出来了。特别是思路方面,先了解了思路,实现起来还是挺简单的,一两个小时就可以写出来。

    现在已经爬了500w左右的关系对数据,也在此基础上了玩了玩PageRank和社区划分的算法,也实现了相应MapReduce版本的算法,后面会一点点地写出来。


    作者:dantezhao |简书 | CSDN | GITHUB

    个人主页:http://dantezhao.com
    文章可以转载, 但必须以超链接形式标明文章原始出处和作者信息

    相关文章

      网友评论

        本文标题:No.2 从0写一个爬虫,爬取500w好友关系数据

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