趁着日常闲余时间,想着搞一搞某条的反爬,练练手,想到自己很久没开发过前端界面了,有点生疏,也趁此机会用flask开发一个简单的搜索界面(真的很简单…)
首页会展示实时热点信息
搜索界面中会根据输入的内容进行搜索,展示搜索结果第一页
热点接口链接
aHR0cHM6Ly93d3cudG91dGlhby5jb20vaG90LWV2ZW50L2hvdC1ib2FyZC8=
搜索接口链接
aHR0cHM6Ly9zby50b3V0aWFvLmNvbS9zZWFyY2g=
通过研究确定反爬点(研究不深…):热点接口有一个_signature
,但是其实不传过去也能拿到数据;搜索接口有s_v_web_id
和__ac_signature
,这两个参数都是通过js生成的,需要逆向破解,然后研究发现__ac_signature
可以随便定义,但是一定要传过去。(最好还是破解…)
首先处理_signature
,Ctrl + Shift + F
全局搜索_signature
打断点后,刷新会定位断点位置,然后进入I
函数
其中O
是必要的参数,然后执行到return
下面会跳转进去生成_signature
的代码地方
然后下面用补环境
的方式进行处理
把代码都拿到本地运行,会发现运行失败,提示缺一些环境
我们只要补全这些环境就可以了,最后只需要补这些参数即可
但是我们调用方法生成参数时发现提示报错了,根据报错可以确定代码没有按正常的流程执行,说明还是有环境问题
仔细查看代码发现是这里有问题,exports
在浏览器环境下是undefined
,但是在node环境下是Object
,所以这里被检测到环境差异了
改掉后就可以运行得到结果了,但是结果对应浏览器的结果发现长度有问题,还是有环境问题。程序已经能够正常跑起来了,但是结果不对,这个时候要看一下是不是有些环境没补或者补不对,大家可以看看
最后确定是cookie问题,在我们调用加密方法时,需要传入cookie值才能拿到结果,注意tt_scid
是服务器返回的cookie
ac_signature
其实可以用_signature
的方式生成,大家可以看一下,其实ac_signature
就是不需要最后传入cookie生成的结果。
s_v_web_id
是动态生成的,如果浏览器检测没有这个参数会出现滑块验证,这里我们使用hook的方式捕捉cookie生成,使用油猴插件实现代码注入
刷新界面后就能在s_v_web_id
生成时断住,注意s_v_web_id
会生成两次,第二次才有值
跳出hook函数后,可以确定就是在这个位置进行cookie设置
研究发现,其实s_v_web_id
不是在这里生成的,这里只是对生成的s_v_web_id
进行二次校验,实际生成的地方需要跳出函数,跳出函数后就能知道s_v_web_id
实际生成代码了,其实就是wt
函数
这里我们稍微了解代码后,对代码进行整理
然后需要把生成的结果进行二次校验,把整个n
函数都拿下来
本地执行缺啥补啥
这样就能拿到结果了,然后拿加密结果去抓取接口就能拿到数据。
static目录放渲染文件,templates放模板文件
创建flask实例化对象时,需要指定文件路径
app = Flask(__name__, static_folder='static', template_folder='templates')
flask引用templates模板文件,并传入数据
@app.route("/toutiao/search", methods=['GET'])
def tt_spider():
keyword = request.args.get('keyword')
tt_spider = TTSpider()
search_result = tt_spider.download_search_news(keyword)
return render_template("search.html", search_result=search_result)
@app.route("/homepage", methods=['GET'])
def home_page():
hot_news_spider = TTSpider()
return render_template("index.html", hot_news=hot_news_spider.download_hot_news())
html文件需要使用指定命令格式引入渲染文件
{{url_for('static', filename='css/style.css')}}
{{url_for('static', filename='images/holmes_logo-hover.svg')}}
{% for hot_new in hot_news.msg %} // 循环字典开头
...
{{ item.title }} // 调用元素
...
{% endfor %} // 循环字典结束
整个流程搞下来还是花了点时间的,当然这个有点简单,不具备实际使用场景,有些隐藏的风控和难点会在使用量一上去就初心,所以练练手就行…