起因是看到github上一段代码,准备直接复制粘贴跑起来,结果却遇到莫名其妙的问题,
#化妆品信息许可 import json import requests if __name__ == '__main__': id_list = [] all_data_list = [] url='http://scxk.nmpa.gov.cn:81/xk/itownet/portalaction.do' headers={ 'user-agent':'mozilla/5.0 (x11; linux x86_64) applewebkit/537.36 (khtml, like gecko) chrome/90.0.4430.72 safari/537.36' } params={ 'method': 'getxkzslist' } for page in range(1,6): page=str(page) data={'on': 'true', 'page': page, 'pagesize': '15', 'productname': '', 'conditiontype': '1', 'applyname': '', 'applysn': ''} dict_data=requests.post(url=url,data=data,params=params,headers=headers).json() for dic in dict_data['list']: id_list.append(dic['id']) post_url='http://scxk.nmpa.gov.cn:81/xk/itownet/portalaction.do' params = { 'method': 'getxkzsbyid' } for id in id_list: data={ 'id':id } detail_json=requests.post(url=url,params=params,data=data,headers=headers).json() all_data_list.append(detail_json) fp=open('./hz.json','w') json.dump(all_data_list,fp=fp,ensure_ascii=false) print('over!!!')
爬取后全是加密的js,没找到有用的信息,分析一下原来是某数的最新(2021-12月后)的反爬方案。
常规的做法是调试一下找出来每个ajax访问链接后添加的hkhnqflv和8x7yi61c两个参数怎么计算的,同时还有cookie。
另外,熟悉某数的都知道这两个参数是每次动态计算的,使用一次就失效了。
打开firefox developer edition,f12,然后就暂停了,挺正常的反爬手段,属于常用招数,通常几个解决方法:永不在此处暂停,清除所有计时器,使用代理软件直接拿、修改源码删除debugger等等。
不过firefox developer edition有个问题,在循环中的条件断点似乎不起作用,那就换成chrome继续。同时记录一下翻页按钮事件绑定的代码位置。
换上chrome,再重复一下屏蔽debugger,查看按钮绑定的地方似乎没有生成链接后的两个参数,看起来是挺正常的。
那就是重写了send,open之类的,断一下xhr,在最终的网络请求处肯定能找到,再逆推回去。一般而言,如同游戏内存挂一样,从发包处逆推会更容易,避免在各种虚拟机函数里迷失方向。
ok,现在找到生成的地方了,找的过程中多次下断点,遇到函数先别跳进去,先直接打印一下函数结果,看是不是有关联的信息。
生成两个参数的地方依次找到,接下来通常的做法是把生成参数的代码扒出来,做个本地服务或是想办法在python里运行js,扒代码的过程中注意只找纯逻辑的地方,本地运行的和动态调试的多做比较,这样能排除掉一些坑。
但这样听起来挺麻烦的,为什么是扒代码而不是去分析算法,直接重写算法就行了?因为这不是一般人静下心来搞的事,项目开发就是各类风险的控制过程,写算法这样的严重成本超支行为风险太大了。
ok,到此就差不多了。流程上行得通,但是。。。有必要花这么大精力吗?
目标只是去爬点数据,结果搞成了和反爬进行斗争,还是得时刻牢记不忘初心。
上面这个方法是最常用的,也算是最正规的攻防套路吧,那还有其它方案吗?
还真有,也不少人用,比如使用selenium,直接使用浏览器,不管怎么反爬,数据总得显示出来吧?
当然selenium也有一些问题,比如已经被防守了,需要修改很多东西,去掉一些特征,再加上一些手段,也是可以达到目的的。
等等,怎么感觉刚出一个坑,又跳进一个坑?
综上,想来最简单的方案,就是使用类似selenium采集的方式,写一个简单的浏览器,再克服一下selenium本来的问题,让整个流程简化再简化,那不就挺完美吗?
ok,结下来想想需要什么:
首先是能访问网页的浏览器,简单,用mfc嵌入个iwebbrowser2
有人会说弄个浏览器,这样比纯代码的访问差多了,因为会去请求图片,css,这些本来不需要的东西,耽误时间浏览,也对,那就再嵌入个代理,让iwebbrowser2连接到代理,然后过滤掉所有的图片,css请求。同时,观察发现和上面的原来一样,只需要网页加载一次获取到cookie和那些加密反爬js的运行环境就行,后续都不需要再加载打开网页了,只需在当前网页上不断地注入js发ajax请求后续的页码就行,详情页同理也是一样,这样就和纯代码的方案差不多了,也不会有浪费时间资源的困扰了,甚至还要主动让程序sleep,避免给网站造成太大负担,毕竟我们只是想爬取点数据,其它任何附带的最好都不要有,同时采集过的内容自己缓存一下,别同样的内容跑去反复采。
总不能爬取一点东西就去改一遍c 程序的逻辑吧?编译调试都挺费事的,ok,那嵌入个python脚本吧,让python控制浏览器的操作,比如打开网页,开启屏蔽图片css等,能够将js代码注入到浏览器中做网络请求,能够点击链接跳转页面,能够获取到网络请求的响应(类似chrome的网络面板),这样相当于是和selenium一样的方法,控制是在python做,具体的业务逻辑还是在“原版”的网页上。
想想大概就这三点吧,大概思路就这些了,这样能够在不做任何侵入的情况下,完成所需的功能,直接注入js发网络请求,你反爬重写了send, 我注入的js发请求你总得也一视同仁一起接管吧,该加密什么的你也得主动加吧?
最后得到的就是纯json的返回值了,在python控制脚本里,根本就不用管反爬的事。
什么,听起来这样实现还是挺难的?如果你之前已经写过类似的东西呢,或者是能获取到已经这样封装好的东西呢?以后都可以无视此类反爬了。
最开始只是一个想法思路,后来搞一搞还真行得通,贴点代码随便看看吧:
cmd.blockimage() cmd.blockcss() iloaded = 0 with usingmysql(log_time=true) as um: cmd.nagivate('http://scxk.nmpa.gov.cn:81/xk/itownet/allqyxx/allqyxx.jsp') cmd.waittillloaded(10000) page = 1 if page == 1: res = cmd.getresponse('itownet/portalaction.do', 30000) iloaded = iloaded processlist(res, log, um, cmd) log.logger.info("load mda_nmpa_scxk_list " str(iloaded) " records") cmd.print("load mda_nmpa_scxk_list " str(iloaded) " records") with open("data\\" runround "p1.json",'w ') as fo: fo.writelines(res) while page <= 419: if os.path.exists("data\\" runround "p" str(page) ".json"): page = page 1 continue cmd.clearresponse() time.sleep(random.randint(1,5)) ...
其实写程序最快乐的事,就是随便搞搞,但最后完美满足需求。
大概就这些了,攻防是会永远存在的,有时候避其锋芒,剑走偏锋也能够事半功倍。
麻将胡了pg电子网站的版权属于:月萌api www.moonapi.com,转载请注明出处