一、常见的反爬手段和解决方法
二、splash 介绍与安装
三、验证码识别
图片验证码的处理方案
- 手动输入(input) 这种方法仅限于登录一次就可持续使用的情况
- 图像识别引擎解析 使用光学识别引擎处理图片中的数据,目前常用于图片数据提取,较少用于验证码处理
- 打码平台 爬虫常用的验证码解决方案
- 打码平台: - 超级鹰- 斐斐打码
以超级鹰举例:
1、注册用户
2、下载 Python语言Demo
3、打开 项目
4、修改验证码类型、用户名、登录密码等信息
可以修改并封装代码方便以后调用
#!/usr/bin/env python# coding:utf-8import requests
from hashlib import md5
classChaojiying_Client(object):def__init__(self, username, password, soft_id):
self.username = username
password = password.encode('utf8')
self.password = md5(password).hexdigest()
self.soft_id = soft_id
self.base_params ={'user': self.username,'pass2': self.password,'softid': self.soft_id,}
self.headers ={'Connection':'Keep-Alive','User-Agent':'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.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)return r.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)return r.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)return r.json()defget_code(img_src,cpt_type):
chaojiying = Chaojiying_Client('超级鹰用户名','超级鹰用户名的密码','96001')# 用户中心>>软件ID 生成一个替换 96001
im =open(img_src,'rb').read()# 本地图片文件路径 来替换 a.jpg 有时WIN系统须要//return chaojiying.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 后要加()#print chaojiying.PostPic(base64_str, 1902) #此处为传入 base64代码
三、验证码登录
1、可以先输入一个错误的登录账号 找到登录接口
2、点击验证码 找到获取验证码图片的接口
编写代码:
import requests
from chaojiying.chaojiying import get_code
deflogin_input():
headers ={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/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')as f:
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 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/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')as f:
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()
四、Chrome抓包分析JS数据源
目标:获取所有英雄并下载每个英雄的壁纸
找到每位英雄的壁纸接口
找到外层每位英雄的列表数据
编写代码逻辑
import requests
import os
from time import sleep
headers ={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36'}
hero_js = requests.get('https://game.gtimg.cn/images/lol/act/img/js/heroList/hero_list.js', headers=headers)for hero in hero_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)for sk in js_resp.json().get('skins'):
file_name = sk.get('name').replace(' ','_')
img_url = sk.get('mainImg')
chromas = sk.get('chromas')if chromas =='0':
img_resp = requests.get(img_url, headers=headers)
sleep(1)print(f'正在下载:{file_name} 图片')
file_dir =f'img/{hero_name}'ifnot os.path.exists(file_dir):
os.mkdir(file_dir)withopen(f'img/{hero_name}/{file_name}.png','wb')as f:
f.write(img_resp.content)
五、JS逆向的操作思路
六、python执行JS代码
# pip install PyExecJSimport execjs
# pip install js2pyimport js2py
js ='''
function add(num1, num2){
return num1+num2;
}
function show(){
return "hello python2js";
}
'''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()
七、JS逆向生成加密数据
解密 微信公众平台 的登录密码 实现登录
下面是解密思路:
假设这里:账号是123,密码是123456,可以发现 密码已经被加密处理了,通过常规的 密码来登录是不好使的
找到这个对密码进行加密的 js 函数,直接拿出来 放到我们的 python 中,这样我们可以通过它提供的加密规则 ,生成需要的密码来实现登录
首先搜索一下 谁调用了这bizlongin
推测可能的 js 函数
这里查找出9个相关的内容 只有上图这里出现了passwd字样 为了验证 ,可以直接打上断点进行调试 这里试过之后好像不是,直接搜索 pwd 进行断点调试 最终找到函数
直接把这段js代码拷贝到我们的代码中
js = ‘’‘
拷贝的js代码
’‘’
import execjs
ctx = execjs.compile(js)
rs = ctx.call('g.exports','123456')print(rs)
实例:某猫小说加密数据生成JS加密逆向分析探索
八、常见的加密使用 base64、md5
deftest_base64():import base64
msg ='hello'
rs = base64.b32encode(msg.encode())print(rs)
rs2 = base64.b32encode(rs)print(rs2)deftest_md51():import hashlib
m = hashlib.md5()
m.update('hello'.encode())
pwd = m.hexdigest()print(pwd)deftest_md52():import hashlib
pwd = hashlib.new('md5',b'hello').hexdigest()print(pwd)if __name__ =='__main__':
test_base64()# test_md52()
DES/AES
RSA
九、python使用Node
下载Node
安装node
创建 js 文件
function add(a,b){return a+b
}
tmp_a = parseInt(process.argv[2])
tmp_b = parseInt(process.argv[3])
console.log(add(tmp_a,tmp_b))
node 执行 js
deftest_node1():import os
a =1
b =2
cmd_line =(f'node js02.js {a}{b}')with os.popen(cmd_line)as nodejs:
rs = nodejs.read().replace('\n','')print(rs)deftest_node2():import subprocess
a =1
b =2
cmd_line =(f'node js02.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()
十、IP代理池
日志模块
日志模块用于记录程序运行过程中的重要信息,这些信息可以帮助开发者调试程序,监控程序的运行状态,追踪错误和异常等。使用日志的好处包括:
- 记录程序的运行情况:方便查看请求是否成功、代理是否有效等。
- 错误追踪:当程序出错时,能够快速定位问题。
- 性能监控:可以记录每次请求的响应时间,帮助优化爬虫性能。
import logging
defsetup_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")
请求模块
请求模块负责实际的网络请求,它会使用代理池中的代理IP进行请求,并处理响应结果。一个好的请求模块应该:
- 自动选择代理IP:从代理池中随机选择一个可用的代理。
- 处理异常:在请求失败时能够妥善处理,比如重试或切换代理。
- 记录请求日志:在请求过程中记录相关信息,包括请求状态和代理的使用情况。
以下是一个简单的请求模块示例,利用前面定义的日志模块进行记录:
import requests
import random
import logging
from setup_logging import setup_logging
classProxyPool:def__init__(self):
self.proxies =['http://username:[email protected]:port','http://username:[email protected]:port','http://username:[email protected]:port',]defget_random_proxy(self):return random.choice(self.proxies)classRequestModule:def__init__(self, proxy_pool):
self.proxy_pool = proxy_pool
defmake_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}")return response.text
except requests.exceptions.RequestException as e:
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)if response:print(response)# 打印响应内容
数据库模块
import sqlite3
classDatabase: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('''
CREATE TABLE IF NOT EXISTS scraped_data (
id INTEGER PRIMARY KEY AUTOINCREMENT,
url TEXT NOT NULL,
content TEXT NOT NULL
)
''')
self.connection.commit()definsert_data(self, url, content):"""插入数据"""
self.cursor.execute('INSERT INTO scraped_data (url, content) VALUES (?, ?)',(url, content))
self.connection.commit()deffetch_all_data(self):"""查询所有数据"""
self.cursor.execute('SELECT * FROM scraped_data')return self.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"),]# 插入数据for url, content in sample_data:
db.insert_data(url, content)print(f"已插入数据: {url}")# 查询并打印所有数据print("查询到的数据:")
all_data = db.fetch_all_data()for row in all_data:print(row)
db.close()
代理IP验证
代理IP验证的目的
- 确保有效性:过滤掉无法连接或响应时间过长的IP,以确保可用代理的质量。
- 提高请求成功率:使用经过验证的代理IP进行请求,提高爬虫的效率和稳定性。
- 避免封禁:使用有效的IP降低被目标网站封禁的风险。
验证的方法
1、简单GET请求:通过向一个稳定且不限制请求频率的URL(如 http://httpbin.org/ip)发起请求来验证代理IP是否可用。
2、响应时间检测:同时,可以记录请求的响应时间,以评估代理的性能。
3、状态码检查:检查返回的HTTP状态码,以判断请求是否成功(状态码200表示成功)。
4、特定内容验证:有些情况下,可以验证返回内容是否符合预期(如获取的IP是否与代理IP一致)。
import requests
import time
classProxyPool:def__init__(self):# 模拟代理IP列表
self.proxies =['http://username:[email protected]:port','http://username:[email protected]:port','http://username:[email protected]: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)if response.status_code ==200:# 检查返回的IPreturn response.json()['origin']# 返回获得的IPelse:print(f"代理{proxy}返回状态码: {response.status_code}")returnNone# IP无效except requests.exceptions.RequestException as e:print(f"代理{proxy}验证失败: {e}")returnNonefinally:
elapsed_time = time.time()- start_time
print(f"请求耗时: 0.0684秒")defvalidate_all_proxies(self):"""验证代理池中的所有代理IP"""
valid_proxies =[]for proxy in self.proxies:print(f"正在验证代理: {proxy}")
ip = self.validate_proxy(proxy)if ip:print(f"代理{proxy}有效, 获取的IP: {ip}")
valid_proxies.append(proxy)else:print(f"代理{proxy}无效")return valid_proxies
# 使用实例if __name__ =='__main__':
proxy_pool = ProxyPool()
valid_proxies = proxy_pool.validate_all_proxies()print("有效的代理IP列表:", valid_proxies)
下载代理IP
下载代理IP的思路
1、选择代理IP源:选择一些提供免费代理IP的网站,这些网站定期更新其代理IP列表。
2、发送请求:使用爬虫发送HTTP GET请求,获取代理IP页面的HTML内容。
3、解析HTML:提取所需的代理IP信息,包括IP地址、端口、匿名类型等。
4、去重与有效性验证:将提取的IP地址进行去重和有效性验证,确保代理IP池中的IP是可用的。可以在下载时进行简单的有效性检查。
5、存储:将可用的代理IP存储到数据库或内存中,以供后续使用。
pip install requests beautifulsoup4
import requests
from bs4 import BeautifulSoup
classProxyDownloader:def__init__(self, url):
self.url = url
defdownload_proxies(self):"""从指定URL下载代理IP"""try:
response = requests.get(self.url, timeout=5)
response.raise_for_status()# 检查响应状态return self.parse_proxies(response.text)except requests.exceptions.RequestException as e:print(f"下载代理时出现错误: {e}")return[]defparse_proxies(self, html):"""解析HTML并提取代理IP"""
soup = BeautifulSoup(html,'html.parser')
proxies =set()# 使用集合去重# 根据网站的HTML结构提取IP和端口for row in soup.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:")for proxy in proxies:print(proxy)
IP代理池的调度器
调度器的主要功能
- 获取代理IP:从代理池中获取当前可用的代理IP。
- 负载均衡:合理分配请求到不同的代理IP,以避免某个代理过载。
- 监控代理状态:监测代理的使用情况(成功率、响应时间等),根据反馈动态调整代理的有效性。
- 代理更新:在需要时更新代理池,移除失效的代理IP,并添加新的可用IP。
调度器的基本结构
一个简单的IP代理池调度器通常包括以下几个部分:
- 代理池:存储和管理代理IP的集合。
- 请求管理:处理请求并分发给合适的代理IP。
- 状态监控:记录和分析每个代理的使用情况。
import random
import requests
import time
classProxyPool: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"""if self.proxies:return random.choice(self.proxies)else:returnNoneclassScheduler:def__init__(self, proxy_pool):
self.proxy_pool = proxy_pool
self.success_count ={}# 用于记录每个代理的成功请求数
self.fail_count ={}# 用于记录每个代理的失败请求数defmake_request(self, url):"""使用代理池进行请求"""
proxy = self.proxy_pool.get_random_proxy()ifnot proxy: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)+1return response.text
except requests.exceptions.RequestException as e:print(f"请求失败: {e}")
self.fail_count[proxy]= self.fail_count.get(proxy,0)+1# 如果失败次数达到阈值,可以将该代理从代理池移除if self.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:[email protected]:port')
proxy_pool.add_proxy('http://username:[email protected]:port')
proxy_pool.add_proxy('http://username:[email protected]:port')# 创建调度器
scheduler = Scheduler(proxy_pool)# 循环发送请求(可根据需求调整循环次数和请求间隔)for _ inrange(5):
content = scheduler.make_request('http://httpbin.org/ip')if content:print("请求成功,响应内容:", content)# 短暂休息以避免过快请求
time.sleep(2)
API接口
API 接口的主要功能
1、获取可用代理IP:提供一种方式让用户获取当前可用的代理IP。
2、添加代理IP:允许外部程序将新代理IP添加到代理池中。
3、删除代理IP:提供接口以便外部程序删除无效或不需要的代理IP。
4、查询代理IP状态:查询特定代理IP的使用情况,如是否有效、请求成功率等。
常见的RESTful API设计(使用HTTP动词)如下:
- GET /proxies:获取可用代理IP列表
- POST /proxies:添加新代理IP
- DELETE /proxies/{ip}:删除特定的代理IP
- GET /proxies/{ip}:查询代理IP的状态
pip install Flask
from flask import Flask, jsonify, request
import random
app = 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):if proxy in self.proxies:del self.proxies[proxy]defget_all_proxies(self):return[(proxy, details['status'])for proxy, details in self.proxies.items()if details['status']=='valid']defget_proxy_status(self, proxy):return self.proxies.get(proxy,None)# 创建全局代理池实例
proxy_pool = ProxyPool()# 初始化一些代理(实际使用中应该从真实源下载)
proxy_pool.add_proxy('http://username:[email protected]:port')
proxy_pool.add_proxy('http://username:[email protected]:port')@app.route('/proxies', methods=['GET'])defget_proxies():"""获取可用代理IP列表"""
proxies = proxy_pool.get_all_proxies()return jsonify(proxies)@app.route('/proxies', methods=['POST'])defadd_proxy():"""添加新代理IP"""
data = request.json
proxy = data.get('proxy')if proxy:
proxy_pool.add_proxy(proxy)return jsonify({"message":"Proxy added successfully."}),201return jsonify({"error":"Proxy not provided."}),[email protected]('/proxies/<string:proxy>', methods=['DELETE'])defdelete_proxy(proxy):"""删除特定的代理IP"""
proxy_pool.remove_proxy(proxy)return jsonify({"message":f"Proxy {proxy} removed successfully."}),[email protected]('/proxies/<string:proxy>', methods=['GET'])defproxy_status(proxy):"""查询代理IP的状态"""
status = proxy_pool.get_proxy_status(proxy)if status:return jsonify({"proxy": proxy,"status": status['status'],"success_count": status['success_count'],"fail_count": status['fail_count']})return jsonify({"error":"Proxy not found."}),404if __name__ =='__main__':
app.run(debug=True)
将上述代码保存为 proxy_pool_api.py,并在终端中运行。
python proxy_pool_api.py
使用API测试工具,如Postman或curl,测试API端点。
获取可用的代理IP列表:
curl -X GET http://127.0.0.1:5000/proxies
添加一个新的代理IP:
curl -X POST http://127.0.0.1:5000/proxies -H "Content-Type: application/json"-d '{"proxy": "http://username:[email protected]:port"}'
删除一个代理IP:
curl -X DELETE http://127.0.0.1:5000/proxies/http://username:[email protected]:port
查询特定代理的状态:
curl -X GET http://127.0.0.1:5000/proxies/http://username:[email protected]:port
版权归原作者 脑袋不灵光的小白羊 所有, 如有侵权,请联系我们删除。