花瓣一日游
朋友工作上有需求,要在花瓣上采集1w+的图片,于是我就帮忙写了个爬虫。
初拿到账号,登上去看了眼网站布局。是这个样子:
花瓣用户主页布局简洁明了,爬取思路也很清晰:
先拿到一个board(画板)链接
board进入一个board后拿到所有pin(图片)的链接就ok了
pins按照以前写爬虫的套路,抓包,分析网络请求,模拟网络请求,数据提取等一套操作下来,这种小爬虫,十来分钟,保准交差!
然而...flag立得有些早了(/捂脸)
刚开始提取数据的时候就遇到了点小阻碍
F12下看到的页面中想提取的数据长这个样子:
html但是响应的数据包里的数据格式却是这个样子:
response看来页面是经过JavaScript动态渲染过了,这下惯用的bs4库是派不上用场了。
看到网上有人用Selenium库来模拟浏览器操作,我觉得没有必要,太影响性能了。
不如首先确定下要爬取的board链接在响应数据中的位置吧,Ctrl+F在页面源代码里搜一下。
有点小坑,直接搜索“/boards/45651489/”还搜不到,还得截取board_id才能搜到:
不过既然在源码里找到了要爬取的数据就好说了,直接上正则提取吧:
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编程的经验,所以上面写的可能有些错漏之处,有大牛看到的话还望多指点。
最后补充两点
-
数据存储和查重
为了方便后面对图片进行多线程断点下载,保存数据的时候需要注意下,其实每个pin(图片)都有个key,这个key可以定位到图片的下载地址,同样也可以用于图片查重操作。
这里我将代码分成了两部分去写:
第一部分遍历每个board,并获取其中每张图片的key, raw_text(图片描述)和对应的board_title(用于图片分类),然后将所有图片信息保存成json文件。
第二部分就是读取json文件,获取每张图片的下载链接,完成一个简单的多线程图片下载任务就行了,单张图片下载前会借助os模块读取文件目录,依据key或者图片名判断图片是否已经存在。 -
反反爬虫
其实最令我头疼的是这个问题,写代码前的准备工程中我就发现,花瓣的访问很不稳定,尤其用爬虫模拟访问时,经常出现连接错误,而且摸不清规律。
为此代码中对大部分request都用上了while语句加上随机数休眠,由于不清楚其反爬虫机制,暂时只是加上了headers和休眠措施,目前测试情况来看已经满足需求了,后面有空尝试搭建代理池再试试效果。
附完整代码:
https://github.com/Moofeng/Spider/tree/master/huaban
- 本文链接: https://blog.moofeng.cn/2019/02/02/花瓣一日游/
- 版权声明: 转载请注明出处!