1、原理讲解
--代理以及proxy代理的原理
--proxy代理参数通过指定代理ip,让代理ip对应的正向代理服务器转发我们发送的请求
--代理ip本质上就是一个代理服务器,用来向目标地址转发请求,接受返回数据并转发给我们
--正向代理 和 反向代理的区别
--正向代理:浏览器或者客户端请求的时候知道地址在哪儿,直接发过去,例如 VPN
--反向代理:不知道最终目的地址在哪儿,而是由代理服务器代为转发,例如 nginx
注:正向代理是知道目的地址的容易被攻击,反向代理是看不出来的,如果你用了nginx还是能够在浏览器上直接看到服务器ip,说明nginx配置有问题
比如说,之前一段时间我们有个项目就是vue的axios中直接写的ip:port/url,导致泄露其实nginx也配置了,但是开发图省事没有进行调整导致ip暴露,非常不安全除了ddos攻击,还有各种数据库泄露风险
2、代理ip的分类
--匿名度:
--透明代理:就是目标服务器可以通过代理服务器直接看到是你发的请求,相当于你是个裸露的状态
--匿名代理:就是目标服务器尽管看不穿它,不能获取最终的来源,但目标服务器知道它是代理,这样任然可以处理爬虫
--高匿名代理:目标服务器不能够分辨其是不是代理
--协议分类:
--http:超文本传输协议
--https: http + ssl 传输协议加上套接字加密
--socks隧道代理:
--socks 代理只是简单地传递数据包,不关心是何种应用协议(FTP、HTTP和HTTPS等)。
--socks 代理比http、https代理耗时少。
--socks 代理可以转发http和https的请求
--有的高质量代理会自动帮助我们添加一些请求头使得请求更加像一个客户端
--代理使用:
# 代理服务器字典
proxies = {
'http': 'http://112.64.233.130:9991',
'http': 'http://116.231.96.146:8118'
}
# 通过代理发出请求,用法如下:
response = requests.get(url, proxies=proxies)
--https://www.kuaidaili.com/free/inha/1/ # 代理网站
3、verify 参数 和 ca 证书
--很多网站地址访问时会出现一个问题,认证证书不合法或者过期
--ca证书L:可以自行了解
https://blog.csdn.net/yangyuge1987/article/details/79209473/
--代码示例:解决认证问题的参数设置如下 verify = True [默认验证]
import requests
url = "https://sam.huat.edu.cn:8443/selfservice/"
response = requests.get(url,verify=False) # False表示不验证证书
4、使用get 所有参数来进行一次爬取
--requests.get 可以使用的参数如下:
--url: 地址
--headers: 包含了所有headers参数,其中cookies参数可以单独设立,如下示例是headers中包含cookie的示例
--headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36'
'Cookie': 'BIDUPSID=2CF813A2AEB265C63047C561C2F527A5; PSTM=1598334227; BAIDUID=2CF813A2AEB265C69E357779E7CB081F:FG=1; BD_UPN=12314753; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; delPer=0; BD_CK_SAM=1; PSINO=5; H_PS_645EC=6e6aIC2q4odoi7mcnGGdS65lfidJ7TlEwI1A%2FEKA%2BoJKieFj9J72m6qELsU; COOKIE_SESSION=1773_0_1_3_0_3_0_1_0_1_2_2_0_0_0_0_0_0_1598454900%7C3%230_0_1598454900%7C1; BD_HOME=1; sug=3; sugstore=1; ORIGIN=0; bdime=0; H_PS_PSSID=32645_1435_32619_32537_32327_32351_32046_32681_32116_31709_32618_32583; BDUSS=ZVU0tLSnZoTU02dUttTDVBc2NyTGEzZno1bmFHaXpmSnJELVpPSzVDTnlFRzVmSVFBQUFBJCQAAAAAAAAAAAEAAAA7QlE-1MLA1tTE1MNMTExMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHKDRl9yg0Zfa0; BDUSS_BFESS=ZVU0tLSnZoTU02dUttTDVBc2NyTGEzZno1bmFHaXpmSnJELVpPSzVDTnlFRzVmSVFBQUFBJCQAAAAAAAAAAAEAAAA7QlE-1MLA1tTE1MNMTExMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHKDRl9yg0Zfa0'
}
--cookies: 上面示例可以看出 cookies 可以写成字典形式,示例如下:
cookies = {
'dfbvhe' : 'hdjbuvbe',
'dfjvbleui' : 'lala'
}
url_request = requests.get(url_bd, headers=headers, cookies=Cookies_dict)
--timeout: timeout=3 # 限制超时时间
--proxies: 是否使用代理ip
proxies = {
'http': 'http://112.64.233.130:9991',
'http': 'http://116.231.96.146:8118'
}
response = requests.get(url, proxies=proxies)
--verufy: 是否使用 ca验证证书
--代码示例如下:
import requests
reponse = requests.get(url, headers=headers, timeout=3, cookies=cookies, proxies=proxies, verify=False)
print(reponse.content.decode()) # decode() 不设置默认解码方式为utf8
reponse.encoding = 'utf8' # text 和 encoding配套使用
print(reponse.text)
5、post请求发送数据
--使用post的场合,post具有加密的功能
登录注册( 在web工程师看来POST 比 GET 更安全,url地址中不会暴露用户的账号密码等信息)
需要传输大文本内容的时候( POST 请求对数据长度没有要求)
--requests发送post请求的方法
response = requests.post(url, data) # data参数接收一个字典
requests模块发送post请求函数的其它参数和发送get请求的参数完全一致
--以调用翻译网站为例使用post提交数据,代码示例如下:
--示例1: 可用
import requests
import json
class Translate(object):
"""
1、浏览器的翻译网址为:http://fy.iciba.com/ajax.php?a=fy
2、但是浏览器是 GET 方式,且不是真正发送翻译数据的链接,所以需要使用 f12 打开检查按钮进行查看,逐个比对找到真实的返回数据地址
"""
def __init__(self, data):
self.url = 'http://fy.iciba.com/ajax.php?a=fy'
self.headers = {
'user-agent': 'ozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36'
}
self.data = {
'f': 'auto',
't': 'auto',
'w': data
}
def __get_data(self):
response = requests.post(self.url, data=self.data, headers=self.headers)
return response.content.decode()
def parse_data(self, data):
json_data = json.loads(data)
return json_data
def trans_cn_to_en(self):
# 获取post 翻译内容
content = self.__get_data()
json_data = self.parse_data(content)
print(json_data['content']['out'])
if __name__ == '__main__':
trans = Translate('洗衣机')
trans.trans_cn_to_en()
--百度翻译案例使用,自己做的:
--使用无痕窗口的目的:因为浏览器会自动帮你保持对话,使用无痕窗口则无法保持
--对于百度的翻译功能使用需要如下操作
--找到接口:
https://fanyi.baidu.com/v2transapi?from=zh&to=en
--两次调用是发送的数据:
from: zh
to: en
query: 老鼠
transtype: translang
simple_means_flag: 3
sign: 659542.979815
token: 0272e107d8f0f1f7152d8f59d8181b73
domain: common
from: zh
to: en
query: 老鼠
transtype: realtime
simple_means_flag: 3
sign: 659542.979815
token: 0272e107d8f0f1f7152d8f59d8181b73
domain: common
--对比数据: 打开无痕窗口,再次抓包发现token发生变化,但是sign没有变化
--这里token是生成的值,所以需要查清楚生成来源
--这里来源是第一种:加载页面时就生成完毕并传送到前端保存起来
--复制 token:0272e107d8f0f1f7152d8f59d8181b73,在所有包中进行搜索,会发现就在fanyibaidu.com的地址中
--如下为爬取到的值:
<script>
window['common'] = {
token: '0272e107d8f0f1f7152d8f59d8181b73',
systime: '1601224615692',
logid: '',
langList: {
'zh': '中文','jp': '日语','jpka': '日语假名','th': '泰语','fra': '法语','en': '英语','spa': '西班牙语','kor': '韩语','tr': '土耳其语','vie': '越南语','ms': '马来语','de': '德语','ru': '俄语','ir': '伊朗语','ara': '阿拉伯语','est': '爱沙尼亚语','be': '白俄罗斯语','bul': '保加利亚语','hi': '印地语','is': '冰岛语','pl': '波兰语','fa': '波斯语','dan': '丹麦语','tl': '菲律宾语','fin': '芬兰语','nl': '荷兰语','ca': '加泰罗尼亚语','cs': '捷克语','hr': '克罗地亚语','lv': '拉脱维亚语','lt': '立陶宛语','rom': '罗马尼亚语','af': '南非语','no': '挪威语','pt_BR': '巴西语','pt': '葡萄牙语','swe': '瑞典语','sr': '塞尔维亚语','eo': '世界语','sk': '斯洛伐克语','slo': '斯洛文尼亚语','sw': '斯瓦希里语','uk': '乌克兰语','iw': '希伯来语','el': '希腊语','hu': '匈牙利语','hy': '亚美尼亚语','it': '意大利语','id': '印尼语','sq': '阿尔巴尼亚语','am': '阿姆哈拉语','as': '阿萨姆语','az': '阿塞拜疆语','eu': '巴斯克语','bn': '孟加拉语','bs': '波斯尼亚语','gl': '加利西亚语','ka': '格鲁吉亚语','gu': '古吉拉特语','ha': '豪萨语','ig': '伊博语','iu': '因纽特语','ga': '爱尔兰语','zu': '祖鲁语','kn': '卡纳达语','kk': '哈萨克语','ky': '吉尔吉斯语','lb': '卢森堡语','mk': '马其顿语','mt': '马耳他语','mi': '毛利语','mr': '马拉提语','ne': '尼泊尔语','or': '奥利亚语','pa': '旁遮普语','qu': '凯楚亚语','tn': '塞茨瓦纳语','si': '僧加罗语','ta': '泰米尔语','tt': '塔塔尔语','te': '泰卢固语','ur': '乌尔都语','uz': '乌兹别克语','cy': '威尔士语','yo': '约鲁巴语','yue': '粤语','wyw': '文言文','cht': '中文繁体' },
account: {
is_login: '1',
user_name: '月乐阅悦LLLL',
add_name:true },
sid: '',
locale: 'zh',
remote: {
query: '',
lang: '',
expand: ''
},
rtSwitch: 'on',
rtl: [
'ara' ],
langMap: {
'zh': ['en','ara','est','bul','pl','dan','de','ru','fra','fin','kor','nl','cs','rom','pt','jp','swe','slo','th','wyw','spa','el','hu','it','yue','cht','zh','vie'],'en': ['zh','ara','est','bul','pl','dan','de','ru','fra','fin','kor','nl','cs','rom','pt','jp','swe','slo','th','wyw','spa','el','hu','it','yue','cht','vie'],'ara': ['zh','en','est','bul','pl','dan','de','ru','fra','fin','kor','nl','cs','rom','pt','jp','swe','slo','th','wyw','spa','el','hu','it','yue','cht','vie'],'est': ['zh','en','ara','bul','pl','dan','de','ru','fra','fin','kor','nl','cs','rom','pt','jp','swe','slo','th','wyw','spa','el','hu','it','yue','cht','vie'],'bul': ['zh','en','ara','est','pl','dan','de','ru','fra','fin','kor','nl','cs','rom','pt','jp','swe','slo','th','wyw','spa','el','hu','it','yue','cht','vie'],'pl': ['zh','en','ara','est','bul','dan','de','ru','fra','fin','kor','nl','cs','rom','pt','jp','swe','slo','th','wyw','spa','el','hu','it','yue','cht','vie'],'dan': ['zh','en','ara','est','bul','pl','de','ru','fra','fin','kor','nl','cs','rom','pt','jp','swe','slo','th','wyw','spa','el','hu','it','yue','cht','vie'],'de': ['zh','en','ara','est','bul','pl','dan','ru','fra','fin','kor','nl','cs','rom','pt','jp','swe','slo','th','wyw','spa','el','hu','it','yue','cht','vie'],'ru': ['zh','en','ara','est','bul','pl','dan','de','fra','fin','kor','nl','cs','rom','pt','jp','swe','slo','th','wyw','spa','el','hu','it','yue','cht','vie'],'fra': ['zh','en','ara','est','bul','pl','dan','de','ru','fin','kor','nl','cs','rom','pt','jp','swe','slo','th','wyw','spa','el','hu','it','yue','cht','vie'],'fin': ['zh','en','ara','est','bul','pl','dan','de','ru','fra','kor','nl','cs','rom','pt','jp','swe','slo','th','wyw','spa','el','hu','it','yue','cht','vie'],'kor': ['zh','en','ara','est','bul','pl','dan','de','ru','fra','fin','nl','cs','rom','pt','jp','swe','slo','th','wyw','spa','el','hu','it','yue','cht','vie'],'nl': ['zh','en','ara','est','bul','pl','dan','de','ru','fra','fin','kor','cs','rom','pt','jp','swe','slo','th','wyw','spa','el','hu','it','yue','cht','vie'],'cs': ['zh','en','ara','est','bul','pl','dan','de','ru','fra','fin','kor','nl','rom','pt','jp','swe','slo','th','wyw','spa','el','hu','it','yue','cht','vie'],'rom': ['zh','en','ara','est','bul','pl','dan','de','ru','fra','fin','kor','nl','cs','pt','jp','swe','slo','th','wyw','spa','el','hu','it','yue','cht','vie'],'pt': ['zh','en','ara','est','bul','pl','dan','de','ru','fra','fin','kor','nl','cs','rom','jp','swe','slo','th','wyw','spa','el','hu','it','yue','cht','vie'],'jp': ['zh','en','ara','est','bul','pl','dan','de','ru','fra','fin','kor','nl','cs','rom','pt','swe','slo','th','wyw','spa','el','hu','it','yue','cht','jpka','vie'],'swe': ['zh','en','ara','est','bul','pl','dan','de','ru','fra','fin','kor','nl','cs','rom','pt','jp','slo','th','wyw','spa','el','hu','it','yue','cht','vie'],'slo': ['zh','en','ara','est','bul','pl','dan','de','ru','fra','fin','kor','nl','cs','rom','pt','jp','swe','th','wyw','spa','el','hu','it','yue','cht','vie'],'th': ['zh','en','ara','est','bul','pl','dan','de','ru','fra','fin','kor','nl','cs','rom','pt','jp','swe','slo','wyw','spa','el','hu','it','yue','cht','vie'],'wyw': ['zh','en','ara','est','bul','pl','dan','de','ru','fra','fin','kor','nl','cs','rom','pt','jp','swe','slo','th','spa','el','hu','it','yue','cht','vie'],'spa': ['zh','en','ara','est','bul','pl','dan','de','ru','fra','fin','kor','nl','cs','rom','pt','jp','swe','slo','th','wyw','el','hu','it','yue','cht','vie'],'el': ['zh','en','ara','est','bul','pl','dan','de','ru','fra','fin','kor','nl','cs','rom','pt','jp','swe','slo','th','wyw','spa','hu','it','yue','cht','vie'],'hu': ['zh','en','ara','est','bul','pl','dan','de','ru','fra','fin','kor','nl','cs','rom','pt','jp','swe','slo','th','wyw','spa','el','it','yue','cht','vie'],'it': ['zh','en','ara','est','bul','pl','dan','de','ru','fra','fin','kor','nl','cs','rom','pt','jp','swe','slo','th','wyw','spa','el','hu','yue','cht','vie'],'yue': ['zh','en','ara','est','bul','pl','dan','de','ru','fra','fin','kor','nl','cs','rom','pt','jp','swe','slo','th','wyw','spa','el','hu','it','cht','vie'],'cht': ['zh','en','ara','est','bul','pl','dan','de','ru','fra','fin','kor','nl','cs','rom','pt','jp','swe','slo','th','wyw','spa','el','hu','it','yue','vie'],'vie': ['zh','en','ara','est','bul','pl','dan','de','ru','fra','fin','kor','nl','cs','rom','pt','jp','swe','slo','th','wyw','spa','el','hu','it','yue','cht'] },
// 图片翻译小流量
ocrHit: '1',
// 文档 翻译小流量
docHit: '1' === '1' ? true : false,
docLangHit: '0' === '1' ? true : false,
domainHit: '0' === '1' ? true : false,
docSid: '',
docTransWithoutLogin: '0' === '1' ? true : false,
pdfHit: (('1' === '1') || ('0' === '1')) ? true : false,
defaultNavList: '[2,1,0,3,4,6,5,7,10,8,13,14,11,12,15,9]',
ownerNavList: '[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]',
hasSideAd: true,
hasBottomBannerAd: true,
transPageUrl: encodeURI('')
}
</script>
--制定爬取计划:
--1、获取fanyibaidu.com的响应html文件并提取 token
--2、包括token一起提交数据进行爬取
--post数据来源5类:
--1、固定值 抓包比较不变值
--2、输入值 抓包比较改变的值是哪些
--通常这种url有很多需要仔细分析
--3、
6、使用post 提交数据--百度翻译为例,第一种生成值,静态文件中就存在,直接正则提取即可
--针对百度翻译为例,对于爬虫时 输入值 生成值 固定值怎么看,有一定的规律
--固定值 输入值:通过比对两个相同请求即可
--生成值:最好打开无痕窗口 [先固定在状态栏 --> 再在chrome图标上右击 --> 打开新的无痕窗口 --> 输入要分析的url]
--分析思路:
--找到真正的post和后端交互的 url
Request URL: https://fanyi.baidu.com/v2transapi?from=zh&to=en
--Form Data数据分析:
from: zh
to: en
query: 字典
simple_means_flag: 3
sign: 932920.712457
token: 462b27db323b9bea619b6a975281077a
domain: common
注:这里 query 是存储输入值,from to simple_means_flag sign domain这些通过浏览器无痕和有痕打开比对之后发现是固定的,token是变化的但是不是输入带来的变化,即token为生成值
--token 值的来源分析:
--右上角 三个竖点哪里点击,打开search,输入token值,会发现no match找不到
--到最初https://fanyi.baidu.com/?aldtype=16047#en/zh/d这个链接下的response内容中搜索,发现找到了,也就是说这个是初始url中带有的,你需要从其中取出token,将最新的token拿来用于第二级url的查询
--思路总结:
--这种方式就是,既有固定值,也有输入值,还有变动值,变动值来自于初级url的response.content中可以取出来,用于次级的url查询中。这种也是最常见的变动值的爬虫处理方式
--第二种生成值思路:
--即在初级url访问后,需要发一个请求到后端得到响应的生成值
--第三种生成值思路:
--在客户端自己生成的生成值,非常难,需要js中生成,示例为有道翻译
7、使用post 提交数据 -- 有道翻译 第三种生成值,需要使用js自己生成,非常复杂需要一定的js功底
--http://json.cn/ 在线的一个json解析
--思路分析
--进入有道,f12 输入一个汉字,等待自动检测进行翻译
--获取 url:
http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule
方式:POST
--提交的数据如下:
i: 太阳 --- 输入值 自己输入的数据
from: AUTO --- 固定值
to: AUTO --- 固定值
smartresult: dict --- 固定值
client: fanyideskweb --- 固定值
salt: 15987075684903 --- 变动值
sign: ce896b52828f28cb671ea840b31c5c86 --- 变动值
lts: 1598707568490 --- 变动值
bv: aa510f0fd141e8aee98da89f3b8bad73 --- 固定值
doctype: json --- 固定值 数据格式
version: 2.1 --- 固定值 版本
keyfrom: fanyi.web --- 固定值
action: FY_BY_CLICKBUTTION --- 可变的固定值 点击翻译就是 BY_CLICKBUTTON,实时监测翻译就是 BY_REALTIME
--对上面数据,按照 不同浏览器 多个url 有痕无痕 分别打开,取出数据进行比对
salt sign lts bv这四个值是变动的但是不确定来源
--对四个值进行分析:
--salt 14位时间戳
--lts 13位的时间戳
--sign js生成 有点难度