花瓣一日游

作者: 慕长风啊 | 来源:发表于2019-02-02 14:25 被阅读8次

    朋友工作上有需求,要在花瓣上采集1w+的图片,于是我就帮忙写了个爬虫。


    初拿到账号,登上去看了眼网站布局。是这个样子:

    花瓣用户主页

    布局简洁明了,爬取思路也很清晰:

    先拿到一个board(画板)链接

    board

    进入一个board后拿到所有pin(图片)的链接就ok了

    pins

    按照以前写爬虫的套路,抓包,分析网络请求,模拟网络请求,数据提取等一套操作下来,这种小爬虫,十来分钟,保准交差!

    然而...flag立得有些早了(/捂脸)

    刚开始提取数据的时候就遇到了点小阻碍

    F12下看到的页面中想提取的数据长这个样子:

    html

    但是响应的数据包里的数据格式却是这个样子:

    response

    看来页面是经过JavaScript动态渲染过了,这下惯用的bs4库是派不上用场了。
    看到网上有人用Selenium库来模拟浏览器操作,我觉得没有必要,太影响性能了。

    不如首先确定下要爬取的board链接在响应数据中的位置吧,Ctrl+F在页面源代码里搜一下。
    有点小坑,直接搜索“/boards/45651489/”还搜不到,还得截取board_id才能搜到:

    board_link

    不过既然在源码里找到了要爬取的数据就好说了,直接上正则提取吧:

    r = requests.get("http://login.meiwu.co/{}/".format(USER_NAME),
                     cookies=self.cookies, headers=self.headers)
    result = re.search(
        r'app.page\[\"user\"\] = (.*?)};', r.text).group(1) + "}"
    boards = json.loads(result)['boards']
    for board in boards:
        self.boards.put((board['board_id'], board['title'])) # 用队列存储board_id和board_title
    
    

    本以为数据提取这部分就算大功告成了,后面就可以松一口气了,没想到...

    loading

    花瓣还用上了Ajax异步加载...

    简单科普一下,有时候我们用requests库模拟请求的时候,返回数据和浏览器中看到的不一样,一种情况就是上面那样经过JS渲染了,还有一种情况是返回的数据好像不完整,比如这里页面下拉能看到更多的board,但是我们一次request是看不到后来的这些数据的,这种数据加载是一种异步加载方式,原始页面不包含后续的数据,当你下拉的时候,页面会向后台请求某个接口获取数据,然后才会被处理呈现到浏览器上,这其实就是发送了一个Ajax请求。

    幸好不是第一次遇到了,以前爬小密圈的时候还觉得蛮棘手,现在也算有些经验了,就是又要费把子力气。

    先打开F12,筛选XHR类型的请求,然后加载下一页board,提取XHR请求的特征

    XHR

    分析后发现,主要参数为max,其他几个参数可以不用管
    其值是上一页中最后一个board_id
    类比我之前爬过的小密圈,其中一个关键值是上一页最后一条帖子的发言时间
    找到这个规律后,接下来的工作就容易很多了:
    提取每一页board的最后一个board_id,然后构造下一页的request

    r = requests.get("http://login.meiwu.co/{}/?jrkb9krs&limit=10&wfl=1&max={}".format(
        USER_NAME, board['board_id']), cookies=self.cookies, headers=self.headers)
    

    经过以上的工作,用户页面的所有画板链接就都提取到手了,接着就是对board页面里的所有图片进行链接提取了,套路和上面是一模一样的,这里就不赘述了。

    总的说来,如果有前端基础,再加上些Python功底,写爬虫是件很easy的事情。
    我自己其实不懂前端,全凭着些Python编程的经验,所以上面写的可能有些错漏之处,有大牛看到的话还望多指点。

    最后补充两点

    1. 数据存储和查重
      为了方便后面对图片进行多线程断点下载,保存数据的时候需要注意下,其实每个pin(图片)都有个key,这个key可以定位到图片的下载地址,同样也可以用于图片查重操作。
      这里我将代码分成了两部分去写:
      第一部分遍历每个board,并获取其中每张图片的key, raw_text(图片描述)和对应的board_title(用于图片分类),然后将所有图片信息保存成json文件。
      第二部分就是读取json文件,获取每张图片的下载链接,完成一个简单的多线程图片下载任务就行了,单张图片下载前会借助os模块读取文件目录,依据key或者图片名判断图片是否已经存在。

    2. 反反爬虫
      其实最令我头疼的是这个问题,写代码前的准备工程中我就发现,花瓣的访问很不稳定,尤其用爬虫模拟访问时,经常出现连接错误,而且摸不清规律。
      为此代码中对大部分request都用上了while语句加上随机数休眠,由于不清楚其反爬虫机制,暂时只是加上了headers和休眠措施,目前测试情况来看已经满足需求了,后面有空尝试搭建代理池再试试效果。

    附完整代码:
    https://github.com/Moofeng/Spider/tree/master/huaban

    相关文章

      网友评论

        本文标题:花瓣一日游

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