爬取facebook站点的正确姿势

背景

最近的爬虫工作做得有些无趣,还好遇上几个比较难缠的站点,积累点偏门的经验。平常我们爬普通站点,跟着页码一页一页慢慢翻就可以,当然如果你觉得太慢,大可直接构造分页,然后并发爬取,很快很暴力,但是被封的风险也很高,偶尔遇到些像facebook这样的站点,还是比较有趣的。

GraphQL

GraphQL是最近的一个技术热点,这个技术就是Facebook放出来的,他家的产品自然也是用的这个技术做查询。对于这个技术,我之前也就只是听说而已,没有太多了解,今天爬取这个站点的时候,算是打了个照面,知道了它的查询方法。

跟普通站点的分页不同,没有死板的页码,它通过前端构造一段查询的文本,通过post方法提交后,由后端解析,后端知道了要查询哪些数据,并如何整合数据,处理完之后,以特定的格式返回数据。GraphQL的查询方式有点像关系数据库的join,而且要什么数据,怎么整合数据都是前端在字符串中定义好的,后端只负责解析字符串并查询、整合和返回数据。以往我们都是在后端定义好数据的查询,前端顶多就是一个url和一堆参数,根本看不出数据存储格式以及她们之间的关联。但GraphQL改变了这一切,看一段GraphQL的查询语句:

1
{"q7":{"priority":0,"q":"Query PagePhotosTabAllPhotosView_react_PageRelayQL {node(1515755625400923) {@F3}} QueryFragment F0 : Feedback {does_viewer_like,id} QueryFragment F1 : Photo {id,album {id,name},feedback {id,can_viewer_comment,can_viewer_like,likers {count},comments {count},@F0}} QueryFragment F2 : Photo {image.sizing(cover-fill-cropped).height(201).width(201) as _image1LP0rd {uri},url,id,@F1} QueryFragment F3 : Page {id,posted_photos.after(1543357872640698).first(28) as _posted_photos1oJl4V {edges {node {id,@F2},cursor},page_info {has_next_page,has_previous_page}}}","query_params":{}}}
1
Query PagePhotosTabAllPhotosView_react_PageRelayQL {node(1515755625400923) {@F3}}

定义一个查询,返回一个对象,将node后面的ID作为F3的查询ID查询病返回结果,查询表达式为{@F3}

1
QueryFragment F3 : Page {id,posted_photos.after(1543357872640698).first(28) as _posted_photos1oJl4V {edges {node {id,@F2},cursor},page_info {has_next_page,has_previous_page}}}

定义查询碎片,从Page集合中查询ID为1515755625400923的相关字段,返回内容包括id和_posted_photos1oJl4V

1
posted_photos.after(1543357872640698).first(28) as _posted_photos1oJl4V {edges {node {id,@F2},cursor},page_info {has_next_page,has_previous_page}}

从posted_photos这个字段中查询出ID为1543357872640698之后的28条数据,并以_posted_photos1oJl4V为名返回,对象中包括edages和page_info两个属性,edges包括node和curser两个属性,page_info包括has_next_page和has_previous_page两个属性。node包含id,以及该id对应下F2的返回数据。

1
QueryFragment F2 : Photo {image.sizing(cover-fill-cropped).height(201).width(201) as _image1LP0rd {uri},url,id,@F1}

从Photo集合中查出与当前id相对应的图片,并将图片缩放,以_image1LPord.uri返回,同时返回url和id以及F1返回值。

另外两段我就不解释了,大致明白了这段代码的含义。我做的爬虫其实只对它的图片感兴趣,因此我是想要他的原图的,怎么搞更方便呢??我尝试着去删掉.height(201).width(201)这段代码,虽然它返回了一张更大的图,但并非原图,很可惜,只能通过其他途径来获取了。例如url字段。

我们爬取的要求很经常是首次爬取全部,之后每天爬取前几页。怎么爬取全部呢?F3里面的posted_photos.after(1543357872640698).first(28)你知道怎么改了嘛,把.after(1543357872640698).first(28)也去掉就可以了,这个测试是通过的。通过调整得到了下面的查询字符串:

1
{"q7":{"priority":0,"q":"Query PagePhotosTabAllPhotosView {node(1515755625400923) {@F3}} QueryFragment F2 : Photo {url} QueryFragment F3 : Page {posted_photos as data {edges {node {id,@F2}}}}","query_params":{}}}

通过encodeURIComponent转码并套上curl的代码

1
curl -X POST --data "__user=0&__a=1&__dyn=7xeUmwkHgfpEbEK4osrWo5O12wAxu13wqovzEcUO2qaxe4E4u14wXx6exm3q260EEqx24o&__req=5&__be=-1&__pc=PHASED:DEFAULT&lsd=AVopxP6J&__rev=2408878&batch_name=PagePhotosTabAllPhotosView_react_PageRelayQL&method=GET&queries=%7B%22q7%22%3A%7B%22priority%22%3A0%2C%22q%22%3A%22Query%20PagePhotosTabAllPhotosView%20%7Bnode(1515755625400923)%20%7B%40F3%7D%7D%20QueryFragment%20F2%20%3A%20Photo%20%7Burl%7D%20QueryFragment%20F3%20%3A%20Page%20%7Bposted_photos%20as%20data%20%7Bedges%20%7Bnode%20%7Bid%2C%40F2%7D%7D%7D%7D%22%2C%22query_params%22%3A%7B%7D%7D%7D&response_format=json&scheduler=phased" https://www.facebook.com/api/graphqlbatch/

请求后就可以活动该用户所有图片页面了。

文章目录
  1. 1. 背景
  2. 2. GraphQL
,