找回密码
 会员注册
查看: 30|回复: 0

python爬虫3——爬虫反反爬

[复制链接]

2万

主题

0

回帖

7万

积分

超级版主

积分
71775
发表于 2024-9-8 17:52:42 | 显示全部楼层 |阅读模式
一、常见的反爬手段和解决方法二、splash介绍与安装三、验证码识别图片验证码的处理方案手动输入(input)这种方法仅限于登录一次就可持续使用的情况图像识别引擎解析使用光学识别引擎处理图片中的数据,目前常用于图片数据提取,较少用于验证码处理打码平台爬虫常用的验证码解决方案打码平台:超级鹰斐斐打码以超级鹰举例:1、注册用户2、下载Python语言Demo3、打开项目4、修改验证码类型、用户名、登录密码等信息可以修改并封装代码方便以后调用#!/usr/bin/envpython#coding:utf-8importrequestsfromhashlibimportmd5classChaojiying_Client(object):def__init__(self,username,password,soft_id):self.username=usernamepassword=password.encode('utf8')self.password=md5(password).hexdigest()self.soft_id=soft_idself.base_params={'user':self.username,'pass2':self.password,'softid':self.soft_id,}self.headers={'Connection':'Keep-Alive','User-Agent':'Mozilla/4.0(compatible;MSIE8.0;WindowsNT5.1;Trident/4.0)',}defPostPic(self,im,codetype):"""im:图片字节codetype:题目类型参考http://www.chaojiying.com/price.html"""params={'codetype':codetype,}params.update(self.base_params)files={'userfile''ccc.jpg',im)}r=requests.post('http://upload.chaojiying.net/Upload/Processing.php',data=params,files=files,headers=self.headers)returnr.json()defPostPic_base64(self,base64_str,codetype):"""im:图片字节codetype:题目类型参考http://www.chaojiying.com/price.html"""params={'codetype':codetype,'file_base64':base64_str}params.update(self.base_params)r=requests.post('http://upload.chaojiying.net/Upload/Processing.php',data=params,headers=self.headers)returnr.json()defReportError(self,im_id):"""im_id:报错题目的图片ID"""params={'id':im_id,}params.update(self.base_params)r=requests.post('http://upload.chaojiying.net/Upload/ReportError.php',data=params,headers=self.headers)returnr.json()defget_code(img_src,cpt_type):chaojiying=Chaojiying_Client('超级鹰用户名','超级鹰用户名的密码','96001')#用户中心>>软件ID生成一个替换96001im=open(img_src,'rb').read()#本地图片文件路径来替换a.jpg有时WIN系统须要//returnchaojiying.Postpic(im.cap_type).get('pic_str')if__name__=='__main__': #本地图片文件路径来替换a.jpg有时WIN系统须要//print(get_code('chaojiying_Python/a.jpg',1004)) #1902验证码类型官方网站>>价格体系3.4+版print后要加()#printchaojiying.PostPic(base64_str,1902)#此处为传入base64代码123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869三、验证码登录1、可以先输入一个错误的登录账号找到登录接口2、点击验证码找到获取验证码图片的接口编写代码:importrequestsfromchaojiying.chaojiyingimportget_codedeflogin_input():headers={'User-Agent':'Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/124.0.0.0Safari/537.36'}session=requests.Session()login_url='https://www.chaojiying.com/user/login/'img_url='https://www.chaojiying.com/include/code/code.php?u=1&t=0.5925062343043659'#获取验证码img_resp=session.get(img_url,headers=headers)withopen('tmp.png','wb')asf:f.write(img_resp.content)code=input('请输入验证码:')#构造登录参数params={"user":"ls1233456","pass":"123456","imgtxt":code,"act":"1"}#登录resp=session.post(login_url,data=params,headers=headers)print(resp.text)deflogin_auto():headers={'User-Agent':'Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/124.0.0.0Safari/537.36'}session=requests.Session()login_url='https://www.chaojiying.com/user/login/'img_url='https://www.chaojiying.com/include/code/code.php?u=1&t=0.5925062343043659'img_resp=session.get(img_url,headers=headers)withopen('tmp.png','wb')asf:f.write(img_resp.content)#识别验证码code=get_code('tmp.png',1004)#构造登录参数params={"user":"ls1233456","pass":"123456","imgtxt":code,"act":"1"}#登录resp=session.post(login_url,data=params,headers=headers)print(resp.text)if__name__=="__main__":login_auto()12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758四、Chrome抓包分析JS数据源目标:获取所有英雄并下载每个英雄的壁纸找到每位英雄的壁纸接口找到外层每位英雄的列表数据编写代码逻辑importrequestsimportosfromtimeimportsleepheaders={'User-Agent':'Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/124.0.0.0Safari/537.36'}hero_js=requests.get('https://game.gtimg.cn/images/lol/act/img/js/heroList/hero_list.js',headers=headers)forheroinhero_js.json().get('hero'):id=hero.get('heroId')hero_name=hero.get('name')url=f'https://game.gtimg.cn/images/lol/act/img/js/hero/{id}.js'js_resp=requests.get(url,headers=headers)forskinjs_resp.json().get('skins'):file_name=sk.get('name').replace('','_')img_url=sk.get('mainImg')chromas=sk.get('chromas')ifchromas=='0':img_resp=requests.get(img_url,headers=headers)sleep(1)print(f'正在下载:{file_name}图片')file_dir=f'img/{hero_name}'ifnotos.path.exists(file_dir)s.mkdir(file_dir)withopen(f'img/{hero_name}/{file_name}.png','wb')asf:f.write(img_resp.content)12345678910111213141516171819202122232425262728五、JS逆向的操作思路六、python执行JS代码#pipinstallPyExecJSimportexecjs#pipinstalljs2pyimportjs2pyjs='''functionadd(num1,num2){returnnum1+num2;}functionshow(){return"hellopython2js";}'''deffunc1():ctx=execjs.compile(js)rs=ctx.call('add',1,2)print(rs)print(ctx.call('show'))deffunc2():context=js2py.EvalJs(js)context.execute(js)#result=context.add(1,2)result=context.show()print(result)if__name__=='__main__':func2()1234567891011121314151617181920212223242526272829七、JS逆向生成加密数据解密微信公众平台的登录密码实现登录下面是解密思路:假设这里:账号是123,密码是123456,可以发现密码已经被加密处理了,通过常规的密码来登录是不好使的找到这个对密码进行加密的js函数,直接拿出来放到我们的python中,这样我们可以通过它提供的加密规则,生成需要的密码来实现登录首先搜索一下谁调用了这bizlongin推测可能的js函数这里查找出9个相关的内容只有上图这里出现了passwd字样为了验证,可以直接打上断点进行调试这里试过之后好像不是,直接搜索pwd进行断点调试最终找到函数直接把这段js代码拷贝到我们的代码中js=‘’‘拷贝的js代码’‘’importexecjsctx=execjs.compile(js)rs=ctx.call('g.exports','123456')print(rs)123456789实例:某猫小说加密数据生成JS加密逆向分析探索八、常见的加密使用base64、md5deftest_base64():importbase64msg='hello'rs=base64.b32encode(msg.encode())print(rs)rs2=base64.b32encode(rs)print(rs2)deftest_md51():importhashlibm=hashlib.md5()m.update('hello'.encode())pwd=m.hexdigest()print(pwd)deftest_md52():importhashlibpwd=hashlib.new('md5',b'hello').hexdigest()print(pwd)if__name__=='__main__':test_base64()#test_md52()123456789101112131415161718192021222324DES/AESRSA九、python使用Node下载Node安装node创建js文件functionadd(a,b){returna+b}tmp_a=parseInt(process.argv[2])tmp_b=parseInt(process.argv[3])console.log(add(tmp_a,tmp_b))123456node执行jsdeftest_node1():importosa=1b=2cmd_line=(f'nodejs02.js{a}{b}')withos.popen(cmd_line)asnodejs:rs=nodejs.read().replace('\n','')print(rs)deftest_node2():importsubprocessa=1b=2cmd_line=(f'nodejs02.js{a}{b}')p=subprocess.Popen(cmd_line.split(),stdout=subprocess.PIPE)rs=p.stdout.read().decode().replace('\n','')print(rs)if__name__=='__main__':test_node1()test_node2()1234567891011121314151617181920十、IP代理池日志模块日志模块用于记录程序运行过程中的重要信息,这些信息可以帮助开发者调试程序,监控程序的运行状态,追踪错误和异常等。使用日志的好处包括:记录程序的运行情况:方便查看请求是否成功、代理是否有效等。错误追踪:当程序出错时,能够快速定位问题。性能监控:可以记录每次请求的响应时间,帮助优化爬虫性能。importloggingdefsetup_logging(log_file='./proxy_sys/my.log'):logging.basicConfig(filename=log_file,level=logging.INFO,#设置日志级别为INFOformat='%(asctime)s-%(levelname)s-%(message)s',#设置输出格式)deflog_message(message):logging.info(message)#使用示例if__name__=="__main__":setup_logging()log_message("程序启动成功")log_message("正在获取代理IP")1234567891011121314151617请求模块请求模块负责实际的网络请求,它会使用代理池中的代理IP进行请求,并处理响应结果。一个好的请求模块应该:自动选择代理IP:从代理池中随机选择一个可用的代理。处理异常:在请求失败时能够妥善处理,比如重试或切换代理。记录请求日志:在请求过程中记录相关信息,包括请求状态和代理的使用情况。以下是一个简单的请求模块示例,利用前面定义的日志模块进行记录:importrequestsimportrandomimportloggingfromsetup_loggingimportsetup_loggingclassProxyPool:def__init__(self):self.proxies=['http://username:password@proxy1.com:port','http://username:password@proxy2.com:port','http://username:password@proxy3.com:port',]defget_random_proxy(self):returnrandom.choice(self.proxies)classRequestModule:def__init__(self,proxy_pool):self.proxy_pool=proxy_pooldefmake_request(self,url):proxy=self.proxy_pool.get_random_proxy()logging.info(f"使用代理:{proxy}进行请求")try:response=requests.get(url,proxies={"http":proxy,"https":proxy},timeout=5)response.raise_for_status()#如果响应状态码不是200,抛出异常logging.info(f"成功请求:{url},状态码:{response.status_code}")returnresponse.textexceptrequests.exceptions.RequestExceptionase:logging.error(f"请求失败:{e}")returnNone#主程序if__name__=="__main__":setup_logging()proxy_pool=ProxyPool()request_module=RequestModule(proxy_pool)url="https://httpbin.org/ip"#测试获取IP的地址response=request_module.make_request(url)ifresponse:print(response)#打印响应内容1234567891011121314151617181920212223242526272829303132333435363738394041424344454647数据库模块importsqlite3classDatabase:def__init__(self,db_file='data.db'):"""初始化数据库连接和创建表"""self.connection=sqlite3.connect(db_file)self.cursor=self.connection.cursor()self.create_table()defcreate_table(self):"""创建存储数据的表"""self.cursor.execute('''CREATETABLEIFNOTEXISTSscraped_data(idINTEGERPRIMARYKEYAUTOINCREMENT,urlTEXTNOTNULL,contentTEXTNOTNULL)''')self.connection.commit()definsert_data(self,url,content):"""插入数据"""self.cursor.execute('INSERTINTOscraped_data(url,content)VALUES(?,?)',(url,content))self.connection.commit()deffetch_all_data(self):"""查询所有数据"""self.cursor.execute('SELECT*FROMscraped_data')returnself.cursor.fetchall()defclose(self):"""关闭数据库连接"""self.connection.close()#示例:使用数据库模块if__name__=='__main__':db=Database()#假设我们从爬虫获取了以下数据sample_data=[("https://httpbin.org/ip","178.128.123.45"),("https://httpbin.org/user-agent","Mozilla/5.0"),]#插入数据forurl,contentinsample_data:db.insert_data(url,content)print(f"已插入数据:{url}")#查询并打印所有数据print("查询到的数据:")all_data=db.fetch_all_data()forrowinall_data:print(row)db.close()1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556代理IP验证代理IP验证的目的确保有效性:过滤掉无法连接或响应时间过长的IP,以确保可用代理的质量。提高请求成功率:使用经过验证的代理IP进行请求,提高爬虫的效率和稳定性。避免封禁:使用有效的IP降低被目标网站封禁的风险。验证的方法1、简单GET请求:通过向一个稳定且不限制请求频率的URL(如http://httpbin.org/ip)发起请求来验证代理IP是否可用。2、响应时间检测:同时,可以记录请求的响应时间,以评估代理的性能。3、状态码检查:检查返回的HTTP状态码,以判断请求是否成功(状态码200表示成功)。4、特定内容验证:有些情况下,可以验证返回内容是否符合预期(如获取的IP是否与代理IP一致)。importrequestsimporttimeclassProxyPool:def__init__(self):#模拟代理IP列表self.proxies=['http://username:password@proxy1.com:port','http://username:password@proxy2.com:port','http://username:password@proxy3.com:port',#更多代理IP]defvalidate_proxy(self,proxy):"""验证代理IP的有效性"""url='http://httpbin.org/ip'#验证代理的URLtry:start_time=time.time()#记录请求开始时间response=requests.get(url,proxies={"http":proxy,"https":proxy},timeout=5)ifresponse.status_code==200:#检查返回的IPreturnresponse.json()['origin']#返回获得的IPelse:print(f"代理{proxy}返回状态码:{response.status_code}")returnNone#IP无效exceptrequests.exceptions.RequestExceptionase:print(f"代理{proxy}验证失败:{e}")returnNonefinally:elapsed_time=time.time()-start_timeprint(f"请求耗时:0.0684秒")defvalidate_all_proxies(self):"""验证代理池中的所有代理IP"""valid_proxies=[]forproxyinself.proxies:print(f"正在验证代理:{proxy}")ip=self.validate_proxy(proxy)ifip:print(f"代理{proxy}有效,获取的IP:{ip}")valid_proxies.append(proxy)else:print(f"代理{proxy}无效")returnvalid_proxies#使用实例if__name__=='__main__':proxy_pool=ProxyPool()valid_proxies=proxy_pool.validate_all_proxies()print("有效的代理IP列表:",valid_proxies)12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152下载代理IP下载代理IP的思路1、选择代理IP源:选择一些提供免费代理IP的网站,这些网站定期更新其代理IP列表。2、发送请求:使用爬虫发送HTTPGET请求,获取代理IP页面的HTML内容。3、解析HTML:提取所需的代理IP信息,包括IP地址、端口、匿名类型等。4、去重与有效性验证:将提取的IP地址进行去重和有效性验证,确保代理IP池中的IP是可用的。可以在下载时进行简单的有效性检查。5、存储:将可用的代理IP存储到数据库或内存中,以供后续使用。pipinstallrequestsbeautifulsoup41importrequestsfrombs4importBeautifulSoupclassProxyDownloader:def__init__(self,url):self.url=urldefdownload_proxies(self):"""从指定URL下载代理IP"""try:response=requests.get(self.url,timeout=5)response.raise_for_status()#检查响应状态returnself.parse_proxies(response.text)exceptrequests.exceptions.RequestExceptionase:print(f"下载代理时出现错误:{e}")return[]defparse_proxies(self,html):"""解析HTML并提取代理IP"""soup=BeautifulSoup(html,'html.parser')proxies=set()#使用集合去重#根据网站的HTML结构提取IP和端口forrowinsoup.find_all('tr')[1:]:#跳过表头columns=row.find_all('td')iflen(columns)>=2:#确保有足够的列ip=columns[0].text.strip()#第一列是IP地址port=columns[1].text.strip()#第二列是端口proxies.add(f"{ip}:{port}")#添加到集合returnlist(proxies)#使用示例if__name__=='__main__':url='http://www.ip3366.net/free/?stype=3'#指定代理源网站downloader=ProxyDownloader(url)proxies=downloader.download_proxies()print("下载到的代理IP:")forproxyinproxies:print(proxy)123456789101112131415161718192021222324252627282930313233343536373839404142IP代理池的调度器调度器的主要功能获取代理IP:从代理池中获取当前可用的代理IP。负载均衡:合理分配请求到不同的代理IP,以避免某个代理过载。监控代理状态:监测代理的使用情况(成功率、响应时间等),根据反馈动态调整代理的有效性。代理更新:在需要时更新代理池,移除失效的代理IP,并添加新的可用IP。调度器的基本结构一个简单的IP代理池调度器通常包括以下几个部分:代理池:存储和管理代理IP的集合。请求管理:处理请求并分发给合适的代理IP。状态监控:记录和分析每个代理的使用情况。importrandomimportrequestsimporttimeclassProxyPool:def__init__(self):#初始化代理IP列表self.proxies=[]defadd_proxy(self,proxy):"""添加代理IP到池中"""self.proxies.append(proxy)defremove_proxy(self,proxy):"""从池中移除代理IP"""self.proxies.remove(proxy)defget_random_proxy(self):"""获取随机代理IP"""ifself.proxies:returnrandom.choice(self.proxies)else:returnNoneclassScheduler:def__init__(self,proxy_pool):self.proxy_pool=proxy_poolself.success_count={}#用于记录每个代理的成功请求数self.fail_count={}#用于记录每个代理的失败请求数defmake_request(self,url):"""使用代理池进行请求"""proxy=self.proxy_pool.get_random_proxy()ifnotproxy:print("没有可用的代理IP!")returnNoneprint(f"使用代理:{proxy}")try:response=requests.get(url,proxies={"http":proxy,"https":proxy},timeout=5)response.raise_for_status()#检查响应状态self.success_count[proxy]=self.success_count.get(proxy,0)+1returnresponse.textexceptrequests.exceptions.RequestExceptionase:print(f"请求失败:{e}")self.fail_count[proxy]=self.fail_count.get(proxy,0)+1#如果失败次数达到阈值,可以将该代理从代理池移除ifself.fail_count[proxy]>=3:print(f"代理{proxy}失效,移除。")self.proxy_pool.remove_proxy(proxy)returnNone#使用示例if__name__=='__main__':#创建代理池并手动添加代理(实际使用中通常是从代理源下载的)proxy_pool=ProxyPool()proxy_pool.add_proxy('http://username:password@proxy1.com:port')proxy_pool.add_proxy('http://username:password@proxy2.com:port')proxy_pool.add_proxy('http://username:password@proxy3.com:port')#创建调度器scheduler=Scheduler(proxy_pool)#循环发送请求(可根据需求调整循环次数和请求间隔)for_inrange(5):content=scheduler.make_request('http://httpbin.org/ip')ifcontent:print("请求成功,响应内容:",content)#短暂休息以避免过快请求time.sleep(2)12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970API接口API接口的主要功能1、获取可用代理IP:提供一种方式让用户获取当前可用的代理IP。2、添加代理IP:允许外部程序将新代理IP添加到代理池中。3、删除代理IP:提供接口以便外部程序删除无效或不需要的代理IP。4、查询代理IP状态:查询特定代理IP的使用情况,如是否有效、请求成功率等。常见的RESTfulAPI设计(使用HTTP动词)如下:GET/proxies:获取可用代理IP列表POST/proxies:添加新代理IPDELETE/proxies/{ip}:删除特定的代理IPGET/proxies/{ip}:查询代理IP的状态pipinstallFlask1fromflaskimportFlask,jsonify,requestimportrandomapp=Flask(__name__)classProxyPool:def__init__(self):self.proxies={}#用字典存储代理和状态defadd_proxy(self,proxy):self.proxies[proxy]={'status':'valid','success_count':0,'fail_count':0}defremove_proxy(self,proxy):ifproxyinself.proxies:delself.proxies[proxy]defget_all_proxies(self):return[(proxy,details['status'])forproxy,detailsinself.proxies.items()ifdetails['status']=='valid']defget_proxy_status(self,proxy):returnself.proxies.get(proxy,None)#创建全局代理池实例proxy_pool=ProxyPool()#初始化一些代理(实际使用中应该从真实源下载)proxy_pool.add_proxy('http://username:password@proxy1.com:port')proxy_pool.add_proxy('http://username:password@proxy2.com:port')@app.route('/proxies',methods=['GET'])defget_proxies():"""获取可用代理IP列表"""proxies=proxy_pool.get_all_proxies()returnjsonify(proxies)@app.route('/proxies',methods=['POST'])defadd_proxy():"""添加新代理IP"""data=request.jsonproxy=data.get('proxy')ifproxy:proxy_pool.add_proxy(proxy)returnjsonify({"message":"Proxyaddedsuccessfully."}),201returnjsonify({"error":"Proxynotprovided."}),400@app.route('/proxies/',methods=['DELETE'])defdelete_proxy(proxy):"""删除特定的代理IP"""proxy_pool.remove_proxy(proxy)returnjsonify({"message":f"Proxy{proxy}removedsuccessfully."}),200@app.route('/proxies/',methods=['GET'])defproxy_status(proxy):"""查询代理IP的状态"""status=proxy_pool.get_proxy_status(proxy)ifstatus:returnjsonify({"proxy":proxy,"status":status['status'],"success_count":status['success_count'],"fail_count":status['fail_count']})returnjsonify({"error":"Proxynotfound."}),404if__name__=='__main__':app.run(debug=True)12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061将上述代码保存为proxy_pool_api.py,并在终端中运行。pythonproxy_pool_api.py1使用API测试工具,如Postman或curl,测试API端点。获取可用的代理IP列表:curl-XGEThttp://127.0.0.1:5000/proxies1添加一个新的代理IP:curl-XPOSThttp://127.0.0.1:5000/proxies-H"Content-Type:application/json"-d'{"proxy":"http://username:password@proxy3.com:port"}'1删除一个代理IP:curl-XDELETEhttp://127.0.0.1:5000/proxies/http://username:password@proxy1.com:port1查询特定代理的状态:curl-XGEThttp://127.0.0.1:5000/proxies/http://username:password@proxy2.com:port1
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2025-1-10 14:08 , Processed in 0.475592 second(s), 26 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表