今日头条Web端爬虫as,cp值破解
请各位转载的朋友请注明出处.
作者:小胖
实验网址:https://www.toutiao.com/ch/news_tech/
实验环境:Windows10
实验工具:Chrome,Fiddler(该抓包观察比较方便,当时抓取HTTPS的网页需要设置证书,具体请百度品尝,很多教程)
0x1:分析
A:打开头条官网抓包.发现其是一个ajax的页面.然后抓包分析.然后发现了一个对爬虫不友好的三个值.as,cp,_signature
B:我抓了2个获取文章标题的包,发现我们获取的值和封包中一个名为max_behot_time的值有关。当我修改下图中选中的值进行测试的时候,发现会给我们一个错误的返回!至此,我们就需要进行JS对抗了。
0x2: JS对抗 - 前端无秘密
A:找到关键包,从Initiator中进去是最快的。(此方法对于此类大站或是含关键JS混淆的站点百试不爽),具体操作看图!
B:我们运行,通过堆栈信息倒找关键位置。我们在init后,发现this.o.data中包含了我们需要的as,cp,_signature(太难描述具体操作).此刻我们就应该要去寻找,它从哪来!
C:我们经过一系列的操作,发现getHoney是一个生成as和cp的方法.所以我们进去瞧瞧。 这里我把该方法拿了出来。我们的as和cp其实就搞定了。(自行把md5方法扣出来,pc端比m端的简单一点)
e.getHoney = function() {
var t = Math.floor((new Date).getTime() / 1e3)
, e = t.toString(16).toUpperCase()
, i = md5(t).toString().toUpperCase();
if (8 != e.length)
return {
as: "479BB4B7254C150",
cp: "7E0AC8874BB0985"
};
for (var n = i.slice(0, 5), a = i.slice(-5), s = "", o = 0; 5 > o; o++)
s += n[o] + e[o];
for (var r = "", c = 0; 5 > c; c++)
r += e[c + 3] + a[c];
return {
as: "A1" + s + e.slice(-3),
cp: e.slice(0, 3) + r + "E1"
}
}
D:经过测试,_signature值和cp值其实是可有可无的。所以我们只需要as值即可。我这边上一段我修改后的获取as值方法。
function getHoney() {
var t = Math.floor((new Date).getTime() / 1e3)
, e = t.toString(16).toUpperCase()
, i = b(t).toString().toUpperCase();
if (8 != e.length)
return "479BB4B7254C150";
for (var n = i.slice(0, 5), a = i.slice(-5), s = "", o = 0; 5 > o; o++)
s += n[o] + e[o];
for (var r = "", c = 0; 5 > c; c++)
r += e[c + 3] + a[c];
return "A1" + s + e.slice(-3)
}
E:max_behot_time值分析
每一个max_behot_time值其实都是由上一个同类型的包获取得到的。首个包max_behot_time直接写0即可。
F:JS代码调试。JS关键文件我写到了toutiao.js中。
import execjs
with open("toutiao.js","r") as f:
JsCode = f.read()
js=execjs.compile(JsCode)
as_ = js.call("getHoney")
print(as_)
PS:在编写代码后才发现,as.cp并不是必须的。但是了解下js如何破解还是很有必要的
0x3:编写Python代码
A:构造crawl类,写个init方法,初始化一些必要的参数.这边特别说明下cookie的问题. 我们必须要拿每次访问得到的cookie,否则无法获得到新的内容.这点需要注意
至于我如何知道的,当然是自己踩出来的.
class crawl():
def __init__(self,page):
self.page = page
self.time = 0
self.headers ={
"Host":"www.toutiao.com",
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36"
}
self.cookie = {}
# 初始化JS
with open("toutiao.js", "r") as f:
JsCode = f.read()
self.js = execjs.compile(JsCode)
B:这时候我们就可以来写一个request方法.来访问它的页面.这里需要带上headers.我在**init方法中初始化了.否则会被检测.
其次它的返回值是JSON格式**的,所以我们直接市容resp.json()
是非常的方便的.
def request(self,page):
if page == 1:
self.time = 0
as_ = self.js.call("getHoney") # r返回结果 as
url = "https://www.toutiao.com/api/pc/feed/?category=news_tech&utm_source=toutiao&widen=1&max_behot_time={0}&max_behot_time_tmp={0}&tadrequire=true&as={1}".format(
self.time, as_)
resp = requests.get(url,headers = self.headers,cookies = self.cookie)
self.cookie = resp.cookies
result = resp.json()
self.time = result['next']['max_behot_time']
return result
C:拿到了数据,我们来写一个parse_html方法,处理下我们的数据.
def parse_html(self,data):
items = data['data']
for item in items:
yield{
'title':item['title'],
'source':item['source'],
'abstract':item['abstract'] ,
'url':"https://www.toutiao.com/a{0}/".format(item['item_id'])
}
D: 数据整理完毕,就要写文件了.不然爬取出来貌似没有啥太大的意义!因此就有了write_to_file方法,代码中注释了一点坑.
def write_to_file(self,context):
#这边不建议转换成我们看的懂的中文,否则后期我们在读入数据的时候,
#因为title或者abstract中会有很多奇怪的字符.会导致很多问题.
with open('toutiao.txt', 'a') as f:
print(context)
f.write(json.dumps(context) + '\n')
E:这时候我们就可以在测试下,写个main方法,把这几个方法配合的调用起来。
def main(self):
for page in range(1,self.page+1):
htmlData = self.request(page)
for item in self.parse_html(htmlData):
self.write_to_file(item)
至此,我们的代码就全部编写完毕了。
0x4:代码运行
crawl(3).main()
效果图如下:
0x5:总结
事实上,这个头条并不需要去JS破解的,我这里是因为没有充分的测试,所以导致了浪费了些许时间去破解JS.也导致本文的篇幅多了许多.
正是因为如此,我们可以知道一点:有的时候我们并不需要去破解JS,它有可能在后台根本没有检测.
至于AJAX其实就是发现网页规律,然后去访问而已.我也不知道网上的教程为何要总去说ajax,明明很普通,基础的东西
今日头条的signature
值的破解比as,cp
有意思一点.有兴趣的小伙伴可以去玩玩